Home Upgrade Search Memberlist Extras Hacker Tools Award Goals Help Wiki Contact

C 

[SRC] Intercepting system service calls on WoW64 Processes

Hello, so I decide to share this project I wrote some time ago, basically what this code does is set a hook at wow64cpu!X86SwitchTo64BitMode in order to intercept all sistem services functions, the advantage of this method is that you can intercept all functions with a single hook. implement the code as a DLL project, so to use it you should inject the DLL in the target process, injector is included.

monitoring calls in firefox:
[img]http://s4.postimg.org/4f0ijfbml/firefox_hook.jpg[/img]

Note that this method can be avoided using native calls (64-bit version of ntdll) or direct syscall in any case normal applications do not.

MSVC project can be downloaded from my github:
https://github.com/Excr4sh/Wow64SsvcIntrcp

Quick look:
Code
#include "Wow64Hook.h"
#include "LinkedList.h"

HANDLE CurrentProcessHandle;
HANDLE ProcessHeapHandle;
LIST_ENTRY WoW64ListHead;
PVOID JmpSwitchBaseAddress;

PWOW64_SYSCALL_INFO AllocWow64Entry(
    _In_ PSTR FunctionName,
    _In_ ULONG Index
    )
{
    PWOW64_SYSCALL_INFO Entry;
    SIZE_T FunctionNameLen;
    PSTR FunctionNameAlloc;

    FunctionNameLen = strlen(FunctionName);
    if (!(FunctionNameLen > 0))
    {
        return NULL;
    }

    Entry = (PWOW64_SYSCALL_INFO)HeapAlloc(ProcessHeapHandle, HEAP_ZERO_MEMORY, sizeof(WOW64_SYSCALL_INFO));
    FunctionNameAlloc = (PSTR)HeapAlloc(ProcessHeapHandle, HEAP_ZERO_MEMORY, FunctionNameLen + sizeof(CHAR));
    if (!(Entry && FunctionNameAlloc))
    {
        if (Entry)
        {
            HeapFree(ProcessHeapHandle, 0, Entry);
        }
        if (FunctionNameAlloc)
        {
            HeapFree(ProcessHeapHandle, 0, FunctionNameAlloc);
        }

        return NULL;
    }
        
    RtlCopyMemory(FunctionNameAlloc, FunctionName, FunctionNameLen);
    Entry->FunctionName = FunctionNameAlloc;
    Entry->Index = Index;

    return Entry;
}

BOOL AddWoW64FunctionEntry(
    _In_ PSTR FunctionName,
    _In_ PVOID FunctionAddress
    )
{
    PBYTE BytesAddress;
    ULONG Index;
    PWOW64_SYSCALL_INFO NewWow64Entry;

    /*
    
    ZwCreateFile    B8 52000000     MOV EAX, <Index>
                    33C9            XOR ECX,ECX
                    8D5424 04       LEA EDX,DWORD PTR SS:[ESP+0x4]
                    64:FF15 C000000>CALL NEAR DWORD PTR FS:[0xC0]
                    83C4 04         ADD ESP,0x4
                    C2 2C00         RETN 0x2C

    Fast check, this can be improved by using a disassembler

    */
    BytesAddress = (PBYTE)FunctionAddress;
    
    if (BytesAddress[0] == 0xB8 &&      // MOV
        BytesAddress[7] == 0x8D &&      // LEA
        BytesAddress[0xB] == 0x64 &&    // CALL
        BytesAddress[0xC] == 0xFF &&    
        BytesAddress[0xD] == 0x15 &&    
        BytesAddress[0x12] == 0x83 &&   // ADD
        (BytesAddress[0x15] == 0xC2 || BytesAddress[0x15] == 0xC3)  // RET
        )
    {
        Index = *((PULONG)&BytesAddress[1]);
        NewWow64Entry = AllocWow64Entry(FunctionName, Index);
        if (NewWow64Entry)
        {
            INSERT_TAIL_LIST(&WoW64ListHead, &NewWow64Entry->ListEntry);
            return TRUE;
        }
    }

    return FALSE;
}

BOOL EnumWow64SystemFunctions()
{
    HMODULE NtdllBaseAddress;
    PIMAGE_DOS_HEADER DOSHeader;
    PIMAGE_NT_HEADERS NTHeaders;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;
    PULONG NameTable;
    PUSHORT OrdinalTable;
    PULONG AddressTable;
    USHORT Ordinal;
    PSTR SymbolName;
    PVOID SymbolAddress;

    NtdllBaseAddress  = GetModuleHandle(_T("ntdll"));
    DOSHeader = (PIMAGE_DOS_HEADER)NtdllBaseAddress;
    NTHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)NtdllBaseAddress + DOSHeader->e_lfanew);

    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)NtdllBaseAddress + NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    NameTable = (PULONG)((ULONG_PTR)NtdllBaseAddress + ExportDirectory->AddressOfNames);
    OrdinalTable = (PUSHORT)((ULONG_PTR)NtdllBaseAddress + ExportDirectory->AddressOfNameOrdinals);
    AddressTable = (PULONG)((ULONG_PTR)NtdllBaseAddress + ExportDirectory->AddressOfFunctions);

    for (DWORD n = 0; n < ExportDirectory->NumberOfNames; n++)
    {
        Ordinal = OrdinalTable[n];
        if (Ordinal &&
            (!(Ordinal >= ExportDirectory->NumberOfFunctions)))
        {
            SymbolName = (PSTR)((ULONG_PTR)NtdllBaseAddress + NameTable[n]);
            SymbolAddress = (PVOID)((ULONG_PTR)NtdllBaseAddress + AddressTable[Ordinal]);
            AddWoW64FunctionEntry(SymbolName, SymbolAddress);
        }
    }

    return TRUE;
}

PWSTR FormatCallString(
    _In_ _Printf_format_string_ PWSTR Format,
    ...
    )
{
    va_list argptr;
    va_start(argptr, Format);
    return FormatCallStringEx(Format, argptr);
}

PWSTR FormatCallStringEx(
    _In_ _Printf_format_string_ PWSTR Format,
    _In_ va_list ArgPtr
    )
{
    PWSTR String;
    ULONG Length;
    SIZE_T BufferSize;

    Length = _vscwprintf(Format, ArgPtr);
    if (Length == -1)
    {
        return NULL;
    }

    BufferSize = (Length * sizeof(WCHAR)) + sizeof(WCHAR);
    String = (PWSTR)HeapAlloc(ProcessHeapHandle, HEAP_ZERO_MEMORY, BufferSize);

    if (String)
    {
        _vsnwprintf(String, Length, Format, ArgPtr);
    }

    return String;
}

PWSTR fMultiCharToUnicode(_In_ PSTR Ansi)
{
    PWSTR Pointer;
    ULONG Length;

    Length = MultiByteToWideChar(CP_UTF8, 0, Ansi, -1, NULL, 0);
    Pointer = (PWSTR)HeapAlloc(ProcessHeapHandle, HEAP_ZERO_MEMORY, Length * sizeof(WCHAR));
    if (Pointer)
    {
        if (!MultiByteToWideChar(CP_UTF8, 0, Ansi, -1, Pointer, Length))
        {
            HeapFree(ProcessHeapHandle, 0, Pointer);
            Pointer = NULL;
        }
    }

    return Pointer;
}

VOID __stdcall IndexLookupPrint(_In_ DWORD Index)
{
    PLIST_ENTRY List;
    PWOW64_SYSCALL_INFO Entry;
    PWSTR UnicodeName;
    PWSTR DebugString;
    
    List = WoW64ListHead.Flink;

    // synchronization is unnecessary in this code
    while (List != &WoW64ListHead)
    {
        Entry = CONTAINING_RECORD(List, WOW64_SYSCALL_INFO, ListEntry);
        if (Entry->Index == Index)
        {
            UnicodeName = fMultiCharToUnicode(Entry->FunctionName);
            if (UnicodeName)
            {
                DebugString = FormatCallString(_T("Call:%s (0x%X)"), UnicodeName, Index);
                if (DebugString)
                {
                    OutputDebugStringW(DebugString);
                    HeapFree(ProcessHeapHandle, 0, DebugString);
                }

                HeapFree(ProcessHeapHandle, 0, UnicodeName);
            }

            break;
        }

        List = List->Flink;
    }
}

VOID __declspec(naked) X86SwitchTo64BitModeProxy()
{
    __asm
    {
        pushad
        mov edi, JmpSwitchBaseAddress
        mov esi, dword ptr fs:[0xC0]
        mov dword ptr fs:[0xC0], edi
        push eax
        call IndexLookupPrint
        mov dword ptr fs:[0xC0], esi
        popad
        jmp JmpSwitchBaseAddress;
    }
}
Nice, clean. I like it.

Good job.

Ironically intercepting sys calls on wow64 is more straight forward than on x86. KiFastSystemCall isn't large enough for a five byte relative jump instruction.

I like the idea of not using a LDE for Nt* Functions. You can always guarantee the patch size.

I still cringe at Microsoft generic linked list implementation. oh well.
(Jun 30, 2015 11:43 PM)hezboula123 Wrote:  Nice, clean. I like it.

Good job.

Ironically intercepting sys calls on wow64 is more straight forward than on x86. KiFastSystemCall isn't large enough for a five byte relative jump instruction.

I like the idea of not using a LDE for Nt* Functions. You can always guarantee the patch size.
that's true, it requires less than 5-bytes long so you can make it work but that's even possible. I may add it to the project so it work in both 32 and 64b-bit Windows.
(Jun 30, 2015 11:52 PM)excr4sh Wrote:  that's true, it requires less than 5-bytes long so you can make it work but that's even possible. I may add it to the project so it work in both 32 and 64b-bit Windows.

Definitely possible. backwards short jumps that lead to relative 4 byte jumps would be a solution.

CONTAINING_RECORD(List, WOW64_SYSCALL_INFO, ListEntry);

Let me guess what this does, considering type of listentry exists in type of wow64_syscall_info this macro finds the corresponding 'list' pointer along with the other data types defined in the type of 'wow64_syscall_info' structure in memory then returns the base (type of wow64_syscall_info)?

I'm guessing since the address of the list entry is +8 of the structure you can just subtract 8 from the pointer, it's essentially what the macro is doing i believe.

Edit:

Just looked up the macro, looks like i was right.

(type *)((char*)(address) -(unsigned long)(&((type *)0)->field)))
(Jun 30, 2015 11:43 PM)hezboula123 Wrote:  Nice, clean. I like it.

Good job.

Ironically intercepting sys calls on wow64 is more straight forward than on x86. KiFastSystemCall isn't large enough for a five byte relative jump instruction.

I like the idea of not using a LDE for Nt* Functions. You can always guarantee the patch size.

I still cringe at Microsoft generic linked list implementation. oh well.

You can make a two byte relative jump to KiIntSystemCall and perform the five byte patch from there. It's more straight forward than X86SwitchTo64BitMode. The only real downside I see to X86SwitchTo64BitMode is that it's relative to the thread's stack. Where as KiFastSystemCall, all the threads rely on a single global function.
(Jul 1, 2015 01:02 AM)RISC Wrote:  You can make a two byte relative jump to KiIntSystemCall and perform the five byte patch from there. It's more straight forward than X86SwitchTo64BitMode. The only real downside I see to X86SwitchTo64BitMode is that it's relative to the thread's stack. Where as KiFastSystemCall, all the threads rely on a single global function.
You talk about this:
0:000:x86> dt ntdll32!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void

The real Hook is set at wow64cpu!X86SwitchTo64BitMode, WOW32Reserved is a pointer which points to that direction.
(Jul 1, 2015 01:37 AM)excr4sh Wrote:  You talk about this:
0:000:x86> dt ntdll32!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void

The real Hook is set at wow64cpu!X86SwitchTo64BitMode, WOW32Reserved is a pointer which points to that direction.

Ah, I wasn't reading the code correctly. I thought you were overwriting the pointer itself.
(Jul 1, 2015 01:49 AM)RISC Wrote:  Ah, I wasn't reading the code correctly. I thought you were overwriting the pointer itself.
Imagine that one thread calls a function that goes through the hook, in my proxy or sub-sequent calls you can make a call to some function of the system such as CreateFile, that call would pass right through NtCreateFile -> FS: [0xC0] -> My hook, proxy function -> and back to CreateFile, This would cause a infinite loop and in most cases, a Stack Overflow that is why I modify the pointer temporarily.
good share if RISC didnt kill it must be hq
Oh, lookie, one of those rare actually security related posts. I don't know anything about injection past the 5 byte jmp so I'll just crawl back into my mousehole.
(Jul 1, 2015 02:18 AM)excr4sh Wrote:  Imagine that one thread calls a function that goes through the hook, in my proxy or sub-sequent calls you can make a call to some function of the system such as CreateFile, that call would pass right through NtCreateFile -> FS: [0xC0] -> My hook, proxy function -> and back to CreateFile, This would cause a infinite loop and in most cases, a Stack Overflow that is why I modify the pointer temporarily.

You could easily avoid the infinite loop.
C0h is a pointer to the function.
Original: Nt->C0h->Switch
Yours:Nt->C0h->Hook
Another option:Nt->My Pointer to C0h->C0h->Switch

Where My Pointer simply handles the stack before it reaches X86SwitchTo64BitMode.

The first scenerio which you're doing is the more practical solution since you'd avoid having to replace the pointer in every thread's context. The second scenerio works just as well, where it's not a complete hook, you'd have to return tot he original x86switchto64bitmode once you've dealt with the argument.
(Jul 1, 2015 08:59 AM)RISC Wrote:  You could easily avoid the infinite loop.
C0h is a pointer to the function.
Original: Nt->C0h->Switch
Yours:Nt->C0h->Hook
Another option:Nt->My Pointer to C0h->C0h->Switch

Where My Pointer simply handles the stack before it reaches X86SwitchTo64BitMode.

The first scenerio which you're doing is the more practical solution since you'd avoid having to replace the pointer in every thread's context. The second scenerio works just as well, where it's not a complete hook, you'd have to return tot he original x86switchto64bitmode once you've dealt with the argument.
By "Nt->My Pointer to C0h->C0h->Switch" you mean to set a hook in the native function?

In any case I am sure there are several workarounds for this issue.
BTW, for those who want to download the project I'm currently having problems with my github account:
[img]http://s29.postimg.org/pm3jixsbb/image.jpg[/img]

Project:
https://www.sendspace.com/file/ga7lm8
I'd take that as a compliment that you're writing code that only robots could write.
Code
mov dword ptr fs:[0xC0], edi
      push eax
      call IndexLookupPrint
      mov dword ptr fs:[0xC0], esi

So let's get this straight, if you're going to use any functions such as createfile as you suggested earlier to prevent a catastrophic infinite loop you'd just temporarily switch the pointer
then you'd call createfile under the mov dword ptr fs:[0xc0], edi

Seems like a good solution but what happens if let's say another thread calls any function while your pointer is replaced, you won't get the call.
while 0CH = your actual switch trampoline, it won't touch the hook.
(Jul 1, 2015 11:24 AM)hezboula123 Wrote:  Seems like a good solution but what happens if let's say another thread calls any function while your pointer is replaced, you won't get the call.
Each thread has its own TEB:
0:012> ~
0 Id: 668.20c Suspend: 1 Teb: 000000`7efdb000 Unfrozen
1 Id: 668.ea4 Suspend: 1 Teb: 000000`7efd8000 Unfrozen
2 Id: 668.df0 Suspend: 1 Teb: 000000`7efd5000 Unfrozen
3 Id: 668.f78 Suspend: 1 Teb: 000000`7efad000 Unfrozen
4 Id: 668.ac Suspend: 1 Teb: 000000`7efaa000 Unfrozen
5 Id: 668.69c Suspend: 1 Teb: 000000`7efa7000 Unfrozen
6 Id: 668.dbc Suspend: 1 Teb: 000000`7efa4000 Unfrozen
7 Id: 668.c94 Suspend: 1 Teb: 000000`7efa1000 Unfrozen
8 Id: 668.cc4 Suspend: 1 Teb: 000000`7ef9e000 Unfrozen
9 Id: 668.cec Suspend: 1 Teb: 000000`7ef9b000 Unfrozen
10 Id: 668.4b0 Suspend: 1 Teb: 000000`7ef98000 Unfrozen
11 Id: 668.8b8 Suspend: 1 Teb: 000000`7ef95000 Unfrozen
. 12 Id: 668.f9c Suspend: 1 Teb: 000000`7ef92000 Unfrozen


On WoW64 Processes, threads have two TEB:
0:012> !wow64exts.info

PEB32: 0x7efde000
PEB64: 0x7efdf000

Wow64 information for current thread:

TEB32: 0x7ef94000
TEB64: 0x7ef92000

32 bit, StackBase : 0x5110000
StackLimit : 0x510c000
Deallocation: 0x5010000

64 bit, StackBase : 0x464fd20
StackLimit : 0x464c000
Deallocation: 0x4610000


Possibly Related Threads...
Thread / Author Replies Views Last Post
by ByteDoktorFeb 26, 2025 01:24 PM 0 147 Feb 26, 2025 01:24 PM
Last Post: ByteDoktor
[C] by georgeziozasJan 19, 2021 12:14 AM 2 181 Jan 19, 2021 12:14 AM
Last Post: Juice
[C] by HardStoNNov 13, 2020 01:22 AM 9 290 Nov 13, 2020 01:22 AM
Last Post: cghhhhasd