2

I found a function that I would like to hook. The function looks that way:

undefined __register setVisible(int param_1, undefined param_2)
             undefined         AL:1           <RETURN>
             int               EAX:4          param_1
             undefined         DL:1           param_2
                             setVisible                                  

    XREF[3705]:  [...]
0047c09c 88 50 18        MOV        byte ptr [param_1 + 0x18],param_2
0047c09f c3              RET

My goal is to jump to my own function when this one is called. A jump to my own function will take 5 bytes while the function I want to replace is 4 bytes large, meaning that I would be overriding the function below (FUN_0047c0a0). I can obviously find a way to avoid having to hook this function by hooking the code that calls this function, but this one is being called from a lot of different functions and that does not seem to be the proper way to do.

Do you have any idea how I could manage to handle such a case, where a function is shorter than an hook?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Nox
  • 713
  • 7
  • 16
  • 3
    You can use 2-byte `jmp` instruction, to a location where 5-byte `call`/`jmp` hooking code can be placed. – Iwa Jan 03 '23 at 11:07
  • Thank you. In my case, I am surrouded by many 4-bytes functions, I could probably find a way with this solution but that would be really cumbersome, do you know if this is the only way? – Nox Jan 03 '23 at 11:15
  • Another option I could think of is that using <=4 byte instructions (i.e. `INT3`, 1-byte) to generate exceptions and catch them at your own exception handler. But I am not familiar enough with os-specific APIs to guide you on that, sorry. – Iwa Jan 03 '23 at 11:30
  • 2
    So the following function starts right after the `ret`, not aligned by 16 like is normal for compilers these days? If you can map the page at virtual address `0`, you could `xor ecx, ecx` (2B) / `jmp ecx` (2B) to jump there. Or as @Iwa says, `int 3` or `int 123` or whatever if you can set up the right handler. 2-byte `div edx` or `div ah` are guaranteed to `#DE` fault. – Peter Cordes Jan 03 '23 at 11:36
  • Can you carve out 5 bytes in a larger function nearby, like maybe "hook" part of it to jump away somewhere, to make room for a `jmp rel32` that this function could jump to? You don't change the functionality of the other function, just put some of its code somewhere else. If there aren't any >=5-byte functions within -128..+127 bytes of this function, that's not an option. – Peter Cordes Jan 03 '23 at 11:39
  • I am not sure I will be able to use INT3 as I would like to use it to detect breakpoints on the software. Also, the software I am reversing already have some exception handling, I am afraid it would interefere with it – Nox Jan 03 '23 at 11:54
  • Regarding the other functions, yes, I indeed don't have any space to write whatever I would like, the program has been written in Delphi 7 and the only requirement is that function should be on an even number (0x....0, 0x...2, 0x...4, ...) – Nox Jan 03 '23 at 11:57
  • 2
    Regarding mapping to 0, I guess it might work, the issue being that there's actually multiple function with 4 or less bytes that I would like to hook, so consistency matters a lot in my case. I can think of some dirty ways too: as I have a big function some bytes below, I could kill it after the first 5 bytes and hook it to to add its behaviour again – Nox Jan 03 '23 at 12:03
  • 2
    If you can map at zero you can use `xor ecx, ecx` then `call ecx`. This allows to pop the fake return address of the stack and dispatch based on it. – ecm Jan 03 '23 at 12:51
  • Do not post pictures of code please. Instead, post the code and dump as text! I have downvoted your question and will remove the downvote once you replace the pictures with text. – fuz Jan 03 '23 at 14:28
  • 1
    @Nox: Yes, borrowing space in a nearby function is one idea I suggested. It doesn't have to be the *first* 5 bytes, though; you could pick some seldom-used block (like error handling) and replace the start of it with a `jmp rel32`. That could reduce the performance cost, as long as it's still in range for the hook that has to reach it. – Peter Cordes Jan 03 '23 at 14:56
  • @ecm: Great idea. Having code at address zero check a return address also lets you verify that you're coming from the / an expected location, not an accidental null-deref function pointer. Unfortunately you can't have exec without data-read permission, so null-deref loads would no longer fault, but at least stores would still fault. – Peter Cordes Jan 03 '23 at 14:59
  • Can you manually inline some of these stupidly tiny functions into their callers, making room to hook others? Like replace the `call` instruction with a store instruction padded out to 5 bytes? (e.g. [with two DS prefixes](https://stackoverflow.com/questions/48046814/what-methods-can-be-used-to-efficiently-extend-instruction-length-on-modern-x86)). Or do all of these functions have their address taken somewhere, and you can't be sure you've found all places to replace it? – Peter Cordes Jan 03 '23 at 15:04
  • 1
    4 bytes is also room for `push imm8` / `ret` so send execution to different places, but that's horrible for performance, guaranteed mispredict and unbalances the return-address-predictor stack so it'll mispredict future returns farther up the call stack. `xor`-zero / `call` is probably better, although that also involves a `call` not balanced by a `ret`, unless you intentionally use a `ret` to jump somewhere, suffering one guaranteed mispredict now to hopefully avoid 2 or more in the future. – Peter Cordes Jan 03 '23 at 15:14
  • Depending on what you already have available or can analyze your way to in terms of cross referencing information, the cleanest approach might indeed be to replace all the calls to this with ones that instead go to your replacement function. – 500 - Internal Server Error Jan 03 '23 at 19:27
  • 1
    As for the "map in low memory" idea, I think you can get a bit more flexibility with a `jmp rel16` (opcode E9 with a 66 operand size override). This takes a 16-bit displacement but truncates the resulting destination address to 16 bits. But at least that lets you jump anywhere in the low 64K, and you can leave page 0 unmapped to preserve your null-pointer checks. – Nate Eldredge Jan 03 '23 at 19:38
  • I was also thinking about segment overrides. If you have control over the descriptor table, and if you can be sure that fs or gs would be left alone, then `jmp [fs:reg]` is 3 bytes. So if you have any register containing a known value, you can set the fs base and limit appropriately, and place your real destination address there. So I was trying to think if there is any 1-byte instruction that results in any GP register (other than eax and edx, which we need to keep) having a known value. – Nate Eldredge Jan 03 '23 at 19:59
  • Peter Cordes: The function is called by more than 3700 different locations and I am not counting those who calls it that are not found by Ghidra. – Nox Jan 04 '23 at 00:47
  • Regarding the idea of ecm & Nate Eldredge, I think it could really be suitable for my case, I will do some researchs about it as I am not familiar with it. Thank you everyone! – Nox Jan 04 '23 at 00:49
  • Just thought about it, eax in my case are objectd that inherit from a common object, I could maybe add something in their virtual table at an offset let's say 0x100 (where no objects has any function) and use it with something like ` 006e43aa ff 52 14 CALL dword ptr [EDX + 0x100]` not sure if that makes sense neither if it works though. – Nox Jan 04 '23 at 01:02
  • As another thought, if you have control over memory mapping, you could use a 5-byte `call` whose last byte equals the value that comes next in memory. For instance, suppose the function at `0047c0a0` has `0x90` as its first byte. At `0047c09c`, you patch in the bytes `e8 xx yy zz`, where `xx yy zz` are whatever you want, and this decodes to a call to address `0x0047c0a1+0x90zzyyxx`. You compute this address and map your replacement function in there, choosing the lower bytes `zzyyxx` however it is convenient. – Nate Eldredge Jan 06 '23 at 17:47

0 Answers0