IDA, hotpatched functions and signatures that don’t work…

In my recent post, I described issues related to signatures of functions prefixed with 0xCC (int 3).

It turns out that there is one more issue that causes sigs to fail, including both the built-in ones and also these I build myself. I was recently seeing more and more files where sigs failed and eventually decided to investigate the reason.

The problem is caused by the hotpatch prefix for the functions. For 32-bit it is the instruction mov edi,edi (8B FF). If your signatures were built from standard libraries that were compiled with a hotpatch prefix, the sigs will include the prefix as well.

What happens now when you see a piece of software that is using the very same version of the static library, but that uses the functions without the hotpatch? You end up with signature mismatch, and functions will not be recognized!

Let’s look at the example:

8BFF558BEC837D0800742D8B511483FA0872048B01EB028BC1394508721A83FA
1E 2DB8 003E :0000 ?_Inside@?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@QAE_NPB_W@Z

This is a pattern generated from the libcpmt.lib included in SDK 10.0.

Now, consider the code that looks like this:

fun1:
                push    ebp
                mov     ebp, esp
                cmp     DWORD PTR [ebp+8], 0
                jz      short loc_10001297
                mov     edx, [ecx+14h]
                cmp     edx, 8
                jb      short loc_10001276
                mov     eax, [ecx]
                jmp     short loc_10001278
loc_10001276:
                mov     eax, ecx
loc_10001278:
                cmp     [ebp+8], eax
                jb      short loc_10001297
                cmp     edx, 8
                jb      short loc_10001286
                mov     eax, [ecx]
                jmp     short loc_10001288
loc_10001286:
                mov     eax, ecx
loc_10001288:
                mov     ecx, [ecx+10h]
                lea     eax, [eax+ecx*2]
                cmp     eax, [ebp+8]
                jbe     short loc_10001297
                mov     al, 1
                jmp     short loc_10001299
loc_10001297:
                xor     al, al
loc_10001299:
                pop     ebp
                retn    4

fun2:
                mov     edi,edi
                push    ebp
                mov     ebp, esp
                cmp     DWORD PTR [ebp+8], 0
                jz      short loc_2_10001297
                mov     edx, [ecx+14h]
                cmp     edx, 8
                jb      short loc_2_10001276
                mov     eax, [ecx]
                jmp     short loc_2_10001278
loc_2_10001276:
                mov     eax, ecx
loc_2_10001278:
                cmp     [ebp+8], eax
                jb      short loc_2_10001297
                cmp     edx, 8
                jb      short loc_2_10001286
                mov     eax, [ecx]
                jmp     short loc_2_10001288
loc_2_10001286:
                mov     eax, ecx
loc_2_10001288:
                mov     ecx, [ecx+10h]
                lea     eax, [eax+ecx*2]
                cmp     eax, [ebp+8]
                jbe     short loc_2_10001297
                mov     al, 1
                jmp     short loc_2_10001299
loc_2_10001297:
                xor     al, al
loc_2_10001299:
                pop     ebp
                retn    4

Start:
  call fun1
  call fun2
  invoke ExitProcess,0

If you now apply the signature built using the pattern above, you will get the following result:

The function with the hotpatch prefix (fun2) is recognized, and the one without (fun1) – is not!

After discovering this bit I contacted the Hexrays guys and they fixed it with a simple, yet clever patch (Thx Igor&Ramiro). I can’t talk about the details, but I hope this will find its way into the new release of IDA.

Beyond good ol’ Run key, Part 61

3.5 years ago in the part 4 of the series we talked about various Registry keys related to debuggers.

Today we will implement one.

One of the important system processes present in Windows since Vista is the Local Session Manager that runs from the file called lsm.exe. In Windows 10 the service was moved from a separate service process to lsm.dll that is loaded under svchost.exe. I don’t discuss Vista and Win8 details below, because who is using them anyway, but… it should work there too.

The lsm process has a little secret.

When it starts it checks for a presence of the following entries:

  • HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server
    • DebugTS=1 or 2 or 3
    • LSMBreakOnStart=1 or 2 or 3 (Windows 10 only)
    • Debugger = any string

The value of DebugTS (or LSMBreakOnStart) determines how LSM process will behave:

  • 1 – it will check if the user or kernel mode debugger is running, and if any of them is present it will just call DebugBreak to break into debugger session.
  • 2 – if the user mode debugger is not present, the LSM will actually launch it (guess what this post is all about…).
  • 3 – similar to 1.

For the option 2, the LSM will launch the NTSD debugger process using the following command line syntax:

  • ntsd -d -G -x -p <LSM pid>

Yup. The \windows\system32 path is not even specified, so the ntsd just needs to be placed in any location covered by the PATH environment variable.

And in case you are wondering, the ntsd will be launched with the full SYSTEM privileges.

Windows 7:

Window 10:

In order for things to work you need to implement ntsd.exe as a debugger to mimic the expected behavior. It has to take the PID from the command line, or retrieve the parent’s PID via NtQueryInformationProcess (or other means) and attach itself to LSM process. It then needs to create a debugging loop that will ensure the lsm.exe continues to work… and a system starts with the ntsd.exe running in a background. Since the ntsd is launched in an insecure way, it is most likely a subject to a path companion attack (both via PATH and the ntsd.com). I have not tested the latter, cuz it’s 2:00am.