steven-michaud / HookCase

Tool for reverse engineering macOS/OS X

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is it possible to PATCH_HOOK a function inside an executable instead of a shared lib?

zhaoyeming opened this issue · comments

I don't understand the internals, but from my tests it seems it does not work for functions inside executable. It complains that the module is not loaded in process.
Thanks!

Sorry, I meant PATCH_FUNCTION.

Sigh, you're right :-(

I'll be working on this. With luck it's something stupid, and I'll be able to fix it within the next day or two.

I tested by creating a simple command-line executable using the puts() command:

    #include <stdio.h>

    int main(int argc, char *argv[])
    {
      puts("Hello there.");
      return 0;
    }

Then I added the following to my standard hook library template:

    int (*puts_caller)(const char *arg) = NULL;

    static int Hooked_puts(const char *s)
    {
      int retval = puts_caller(s);
      LogWithFormat(true, "Hook.mm: puts(): arg \"%s\", returning \'%i\'", s, retval);
      PrintStackTrace();
      // Not always required, but using it when not required does no harm.
      reset_hook(reinterpret_cast<void*>(Hooked_puts));
      return retval;
    }

    ...

    PATCH_FUNCTION(puts, /Users/smichaud/Documents/ReverseEngineering/puts/puts)

Thanks for your report!

Interestingly INTERPOSE_FUNCTION works just fine (with my example). This leads me to believe that it is something stupid, and won't be too hard to fix.

Arghh. I've managed to misunderstand how my own kext is supposed to work :-( And I'm not able to see the bug you reported, after all. Sorry for the confusion. But in my defense, I haven't used HookCase for several months.

The example I used to "prove" the bug actually does no such thing. There is a "_puts" symbol in my example's command-line executable's symbol table. But it's in the indirect symbol table, which "points" (some simplification here) to the actual "puts" code in a different module -- /usr/lib/system/libsystem_c.dylib. So the correct way to use the PATCH_FUNCTION macro in my example would have been:

    PATCH_FUNCTION(puts, /usr/lib/system/libsystem_c.dylib)

If instead I patch the main() function, whose code is actually located in the "puts" executable, I don't have any trouble. I'll write up the details in my next comment.

Here's the testcase I should have been using, which works fine (which doesn't show the bug you reported):

    #include <stdio.h>

    int main(int argc, char *argv[])
    {
      return 0;
    }

And here's what I should have added to my interpose library template:

    int (*main_caller)(int argc, char *argv[]) = NULL;

    int Hooked_main(int argc, char *argv[])
    {
      int retval = main_caller(argc, argv);
      LogWithFormat(true, "Hook.mm: main(): argc \"%i\", returning \'%i\'", argc, retval);
      for (int i = 1; i <= argc; ++i) {
        LogWithFormat(false, "  argv[%i]: %s", i - 1, argv[i - 1]);
      }
      PrintStackTrace();
      return retval;
    }

    ...

    PATCH_FUNCTION(main, /Users/smichaud/Documents/ReverseEngineering/puts/blah)

Note that you do need to call the new executable ("blah") with its full path - /Users/smichaud/Documents/ReverseEngineering/puts/blah in this example. That could itself be considered as a bug, of sorts. I'll dig further to see if there's anything I can do about it.

So ... were you making the same mistake I did? Were you trying to patch a function whose code isn't actually in the main executable?

Thanks for looking into this. The mistake I made is actually not running the executable with its full path. I was only using full path in PATCH_FUNCTION.

It now works and I'll close this issue but describe another one I met (possibly another one about usage) in the comment below.

Still in the executable, I was playing around hooking using function address (the sub_123abc form) because eventually I will be dealing with some stripped binaries.

I got the address of foo (0x1f00) from nm

$ nm a.out
00001000 T __mh_execute_header
00001f00 T _foo
00001f20 T _main
         U _printf
         U _sleep
         U dyld_stub_binder

and created Hooked_sub_1f00. When I fired a.out (32-bit) with its full path, nothing happened. Tried 0x1f20 for main and it was the same. Guess maybe I was wrong about the addresses.

I'm glad to hear that the issue you originally reported has been resolved.

As for your problem using the sub_123abc format for a patch hook, I suggest removing the first digit in the address that nm reports. So you'd use "sub_f00" for "foo" and "sub_f20" for "main". This works for me in both 32-bit mode and 64-bit mode. Let me know your results.

This is still a bug, and I'll open a new "issue" on it once I've had a chance to investigate it a bit further. HookCase seems to have trouble figuring out the correct "slide" for symbols in the main executable. I may end up fixing it by just adding the (unslid) address of "_mh_execute_header" to the "slide" reported for the main executable. If I did that, you'd no longer need to remove the first digit.

Before now, I never tested the sub_123abc format with functions in the main executable -- only with functions in dylibs. Thanks again for your report!

Subtracting 0x1000 does the trick in a main executable. Thanks.

And I learned one thing or two about Mach-O along the way. Cheers!

I just fixed the issue you reported about using the sub_123abc form for patch hooks in main executables. So you shouldn't any longer have to subtract _mh_execute_header's address from the addresses you use.

Do open "issues" if you have any more problems. Good luck!