steven-michaud / HookCase

Tool for reverse engineering macOS/OS X

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Some interpose hooks are skipped on Big Sur

qiuwenqiao opened this issue · comments

Last commit: https://github.com/steven-michaud/HookCase/issues/21

I tested new version (5.0.1), and the problem still exists. I try to write a simple case.

INTERPOSE_FUNCTION(open)

static int Hooked_open(const char * filename, int flags, mode_t mode)
{
const char * lpDot = strrchr(filename, '.');
if (lpDot && (strcmp(lpDot, ".pdf")==0))
{
printf("open %s\n", filename);
}
return open(filename, flags, mode);
}

Then, Run Preview and open a pdf file. In macos 10.15.7, It will show 4 records. But In macos 11.1 It only show 1 record. So, I think maybe some hooks didn't attach.

The problem I see is worse that what you report. Using an interpose hook (as you do), I don't see any calls to open() on macOS 11.2.1. They do appear if you use a patch hook. But I need to figure out why the interpose hooks aren't working.

This is a bug in HookCase. There's a structure called a lazy pointer table that HookCase uses to support interpose hooks. In the past it was always located in the __DATA segment. But now, in Big Sur, it's sometimes located in the __DATA_CONST segment. I need to change HookCase code so that it will look in both places (if need be).

I've written a patch, and will be testing it over the next few days. Once I'm reasonably sure it works properly, I'll land it here and bump the version number. Then I'll ask you to test it.

This bug should now be fixed. Please comment here if you still see the exact same problem you originally reported. Please open a new bug if you see some other problem.

One more thing I should mention. HookCase can have trouble with variable argument functions like open(). This function only has one possible "extra" argument -- mode_t mode. So your hook needs to always specify the mode argument, even for cases in which it isn't present. This you've done correctly.

But there's another problem: The AMD64 processor ABI specifies that, on calling a function with variable arguments, the RAX register should specify the number of arguments stored in "vector registers" (XMM0 to XMM7). Since mode is an integer, RAX should always be 0 on calls to open(). The only way to guarantee this in a HookCase hook is to put the call to the original open() function at the very beginning of the hook. For example:

    static int Hooked_open(const char *path, int oflag, mode_t mode)
    {
      int retval = open(path, oflag, mode);
    
      const char *lpDot = NULL;
      if (path) {
        lpDot = strrchr(path, '.');
      }
    
      if (lpDot && (strcmp(lpDot, ".pdf")==0)) {
        LogWithFormat(true, "Hook.mm: open(): path %s, oflag 0x%x, mode 0x%x, returning %i",
                      path, oflag, mode, retval);
        PrintStackTrace();
      }
    
      return retval;
    }

Problems with variable argument functions are probably present in all "function hooking" software. printf() is even worse, because it can take almost any number of arguments. My advice is to avoid hooking variable argument functions. Instead hook their va_list variants, like vprintf(). A variable argument function will almost always call some kind of va_list variant. But to hook it you'll need to use a patch hook (since the high-level code may not call it directly).

I have tested in 10.15.7/11.1/11.2.2, unfortunately, The problem still exists, and have the same logs. by the way, should I modify the library template?

I'll talk only about your results on macOS 11, since (according to your original report) that's where the problem exists.

Are you sure you're using HookCase version 5.0.2? What version number do you see when you do kextstat -b org.smichaud.HookCase?

Please try testing with a hook library that contains only the hook from my previous comment.

Also make sure you're recompiling HookCase.kext and your hook libraries on every different version of macOS that you test on.

I assume you're still testing with Preview, by opening a file in it.

I retested and found some interesting things. Here are test code.
static int Hooked_open(const char *path, int oflag, mode_t mode)
{
int retval = open(path, oflag, mode);

  const char *lpDot = NULL;
  if (path) {
    lpDot = strrchr(path, '.');
  }

  if (lpDot && ((strcmp(lpDot, ".pdf")==0) || (strcmp(lpDot, ".jpg")==0)) ) {
    LogWithFormat(true, "Hook.mm: open(): path %s, oflag 0x%x, mode 0x%x, returning %i",
                  path, oflag, mode, retval);
   // PrintStackTrace();
  }

  return retval;
}

First of all. I comfired the hookcase version.
kextstat -b org.smichaud.HookCase
No variant specified, falling back to release
Index Refs Address Size Wired Name (Version) UUID
160 0 0xffffff7fa2414000 0x1a000 0x1a000 org.smichaud.HookCase (5.0.2) 4B2D06F9-8DEC-3846-80F9-E773408914A7 <8 6 5 3 2 1>

Then, I Run
HC_INSERT_LIBRARY=/Users/mac/Downloads/hook.dylib /System/Applications/Preview.app/Contents/MacOS/Preview

When I opened a jpg file, The result is correct.

_(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x700006e7f000] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x8000, mode 0x4c1a, returning 4
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x8100, mode 0xe21a, returning 4
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0x0, returning 4
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0xffe0, returning 4
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0x0, returning 4
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x8004, mode 0xa, returning 4
2021-03-01 10:47:34.889 Preview[817:20886] WARNING: The SplitView is not layer-backed, but trying to use overlay sidebars.. implicitly layer-backing for now. Please file a radar against this app if you see this.
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0x0, returning 4
(Mon Mar 1 10:47:34 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0x0, returning 4
(Mon Mar 1 10:47:35 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0x0, returning 4
(Mon Mar 1 10:47:35 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x0, mode 0xffe0, returning 6
(Mon Mar 1 10:47:36 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x70000710e000] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x2, mode 0xcf1a, returning 6
2021-03-01 10:47:36.589 Preview[817:20886] +[NSSavePanel warmUp] attempted warmup
(Mon Mar 1 10:47:40 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x700007191000] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x2, mode 0xb408, returning 6
(Mon Mar 1 10:47:40 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x70000708b000] Hook.mm: open(): path /Users/mac/Downloads/5.jpg, oflag 0x2, mode 0x9f1a, returning 11

But When I opened a pdf file. I found that the path is null


(Mon Mar 1 10:48:12 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x700006e7f000] Hook.mm: open(): path , oflag 0x8000, mode 0xe026, returning 13
(Mon Mar 1 10:48:12 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path , oflag 0x8100, mode 0x4826, returning 13
(Mon Mar 1 10:48:12 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path , oflag 0x0, mode 0xffe0, returning 13
(Mon Mar 1 10:48:12 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path , oflag 0x0, mode 0x0, returning 13
(Mon Mar 1 10:48:12 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path , oflag 0x8004, mode 0xb690, returning 13
2021-03-01 10:48:12.457 Preview[817:20886] WARNING: The SplitView is not layer-backed, but trying to use overlay sidebars.. implicitly layer-backing for now. Please file a radar against this app if you see this.
(Mon Mar 1 10:48:13 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x117b69e00] Hook.mm: open(): path , oflag 0x0, mode 0xffe0, returning 16
(Mon Mar 1 10:48:13 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x700006d79000] Hook.mm: open(): path , oflag 0x2, mode 0x7f26, returning 16
(Mon Mar 1 10:48:17 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x700006cf6000] Hook.mm: open(): path , oflag 0x2, mode 0xefe0, returning 16
(Mon Mar 1 10:48:17 2021) /System/Applications/Preview.app/Contents/MacOS/Preview[817] [0x700006d79000] Hook.mm: open(): path , oflag 0x2, mode 0x7f26, returning 16

Interesting indeed.

It looks like the calls from Preview to open .PDF files succeeded. I assume the file you tried to open did load successfully.

path wasn't NULL. In that case the hook would have crashed. Instead I suspect it started with an improperly formatted multibyte character -- one that wasn't labelled as such in the character stream. The first byte of some multibyte characters is 0. So if the logger was only expecting a single byte character, it might have thought that path pointed to an empty array of characters.

Exactly what path were you trying to load? I assume it contained some Chinese characters, or at least some Roman characters from the 简体中文 or 繁體中文 character sets.

Yes, I do speak a little Chinese :-)

Another possibility is that path contained a correctly formatted multibyte (non-UTF8) character stream whose very first byte was 0. Both printf() and my interpose library template's LogWithFormatV() function would assume the character stream is UTF8, and therefore interpret the first character as a terminal NULL.

If this is the case, I'm not sure what I can do about it, if anything.

OK, I've now figured it out:

If your preferred language (偏好语言) is Chinese (simplified or traditional) and the path you're trying to open contains at least one Chinese character (simplified or traditional), path will be empty in the open() hook.

This is actually a completely different bug from what was originally reported (as I understood it), and what I fixed with 0f843c2. It's also completely unrelated. In the next few days I'll open a new issue on this bug. But like I said, I'm not sure there's anything I can do about it.

I think probably the terminal does not support Chinese characters. Because

for (int i=0; i<strlen(path);i++)
{
printf("%x", path[i]);
}
The result is correct.

I tested the code with english pathname again. seems the interpose hooks bug is fixed.

As you say, printf() works just fine whether your preferred language is English or Chinese, and whether or not path contains Chinese characters. And path doesn't contain any embedded NULLs even when your preferred language is Chinese. So that's not the reason my code in LogWithFormatV() can't read it.

I need to figure out why printf() works but my code doesn't. Once I've done that, I'll open a new issue and fix the bug.

It may be a while, though. I'm having to dig very deep into undocumented CoreFoundation code.

I just opened Issue #25 and landed a fix for it.