|
-
Knowing hook length
Hi all.
This is more an asm general question but it applies to gothic too.
How can I know the hook length I should pass to HookFunction? I know there is a file with the length for every function, but I would need to hook part of code inside some function but I don't know how "long" they are.
Thank you
-
If you don't mind using Google translate, I'll just reference a post from Lehona. It basically comes down to counting the bytes of the instruction at that address in IDA and making sure it is at least 5 bytes long and does not contain relative addresses (any kind of jump instruction). If an instruction is shorter then 5 bytes you may include the next instruction so long you always take the full length of the instructions and never "cut" them off.
-
Ok thank you. One question if you don't mind.
Code:
7629a8: 8b 0d d4 27 ab 00 mov ecx,DWORD PTR ds:0xab27d4
7629ae: 8b f0 mov esi,eax
7629b0: e8 9b 3d fd ff call 0x736750
7629b5: 3b f3 cmp esi,ebx
7629b7: 75 08 jne 0x7629c1
If I wanted to hook 7629b5 I should include the successive lines which includes a jump and it's not ok, as far as you said. So is it fine to start hooking from the previous line with length 7? If ebx was set by the call, but I wanted to know its value, hooking from the previous line would store ebx in the daedalus variable BEFORE the call, though, right?
-
You can't hook call or jmp instructions (it would mess up the relative addresses). So you'd have to hook 7629a8h unless you need the result of that call...
Edit: Yes, if you could hook there, EBX would contain the value before the function call.
-
unless you need the result of that call
And how would you do that if you did need it?
Geändert von Frank-95 (18.12.2017 um 15:02 Uhr)
-
Zitat von Frank-95
And how would you do that you did need it?
If you absolutely cannot hook at a later point, what you could do is overwrite the call with nop, hook at that address and manually call the overwritten function at the beginning of your hooking function (including pushing the arguments properly and updating the registers afterwards). This is prone to errors and it's very easy to mess up.
Some problems that come to mind: using a recyclable call would require you to read and and re-push all arguments as well as overwriting any prior push instructions with nop (otherwise the addresses of the arguments will become invalid in game saves); you'll need a safeguard that prevents the hook from being overwritten at initialization of loading or at new game.
I wouldn't recommend it and you should avoid doing that at all costs. Usually there is a way around it. If you share what you want to do with/around the call, there might be an alternative.
-
Okay, so...
My aim would be to implement a steal system like G3 (or similar at least). I actually don't want to draw a custom inventory with LeGo function, so I tried to look for some built-in functions that might be used for it.
The most similar I've found is oCNpc:OpenDeadInventory, which obviously checks whether the focused npc is dead or unconscious.
Code:
762970: 64 a1 00 00 00 00 mov eax,fs:0x0
762976: 6a ff push 0xffffffff
762978: 68 4e 6f 82 00 push 0x826f4e
76297d: 50 push eax
76297e: 64 89 25 00 00 00 00 mov DWORD PTR fs:0x0,esp
762985: 83 ec 18 sub esp,0x18
762988: 53 push ebx
762989: 56 push esi
76298a: 57 push edi
76298b: 8b f9 mov edi,ecx
76298d: e8 5e 02 fd ff call 0x732bf0
762992: 33 db xor ebx,ebx
762994: 3b c3 cmp eax,ebx
762996: a3 d4 27 ab 00 mov ds:0xab27d4,eax
76299b: 0f 84 2b 01 00 00 je 0x762acc
7629a1: 8b c8 mov ecx,eax
7629a3: e8 98 3d fd ff call 0x736740
7629a8: 8b 0d d4 27 ab 00 mov ecx,DWORD PTR ds:0xab27d4
7629ae: 8b f0 mov esi,eax
7629b0: e8 9b 3d fd ff call 0x736750
7629b5: 3b f3 cmp esi,ebx
7629b7: 75 08 jne 0x7629c1
7629b9: 3b c3 cmp eax,ebx
7629bb: 0f 84 0b 01 00 00 je 0x762acc
(if you're wondering why I don't have an IDA interface is because I decompiled gothic.exe under linux with objdump)
Red functions are oCNpc::IsDead and oCNpc::IsUnconscious. At first I thought about inverting the result of one of those to "simulate" a dead npc. IsUnconscious was difficult, IsDead was easy but as soon as I wrote the bytes (that would have been restored to original after few milliseconds) every npc died because IsDead returned True for everyone; I acqually never dreamt that that function was continuously called.
Green addresses are adresses which I think make the function returns (but I may be wrong). My second idea would be to hook blue istructions to read what there is in those registers, because I suppose there might be return values of oCNpc::IsDead and oCNpc::IsUnconscious. Hence if I'm right I could change those instead of the functions above.
The problem is that they are between call and jump istruction and they cannot be hooked as far as you told me.
What do you think about it?
Thanks
-
How are you calling it? Let's assume your Daedalus-Code looks like this:
Code:
func void openStealInventory(var c_npc slf) {
/* Complex Call stuff */
};
You can probably do it like this:
Code:
func void openStealInventory(var c_npc slf) {
DisableOpenDeadInventoryChecks();
/* Complex Call stuff */
EnableOpenDeadInventoryChecks();
};
In the Disable-Function you simply override the relevant jumps with a nop-instruction (decimal value is 144, i.e. a single byte, but there's a constant for that somewhere in LeGo...). In the Enable-Function you just restore the original bytes.
I might be able to say more when I get home (couple o' days), because I don't have access to a windows machine, either
-
Code:
76299b: 0f 84 2b 01 00 00 je 0x762acc
7629a1: 8b c8 mov ecx,eax
7629a3: e8 98 3d fd ff call 0x736740
7629a8: 8b 0d d4 27 ab 00 mov ecx,DWORD PTR ds:0xab27d4
7629ae: 8b f0 mov esi,eax
7629b0: e8 9b 3d fd ff call 0x736750
7629b5: 3b f3 cmp esi,ebx
7629b7: 75 08 jne 0x7629c1
7629b9: 3b c3 cmp eax,ebx
7629bb: 0f 84 0b 01 00 00 je 0x762acc
I "noped" the last jump istruction but the hero says "nothing to plunder, even though I'm sure there is.
Nervetheless I'm sure that is the return address because I "decompiled" the code by clicking F5 in IDA, and there is a goto LABEL twice in the first part of the code: after oCNpc:etFocusVob, that jumps if it is not valid, and after oCNpc:IsDead and oCNpc:IsUnconscious, inside an if statement that jumps if both function return FALSE, so when the inventory cannot be opened.
So it's pretty annoying.
I have an item in the inventory which a click, and via FF calls another function after few second, so that player inventory is closed at that moment. The last called function is made like you said:
Code:
NopJumps;
CALL__thiscall(heroaddress, 0x762970 /*oCNpc:OpenDeadInventory*/);
RestoreJumps;
Berechtigungen
- Neue Themen erstellen: Nein
- Themen beantworten: Nein
- Anhänge hochladen: Nein
- Beiträge bearbeiten: Nein
|
|