Tricks of the Hackers: API Hooking and DLL Injection
Total Page:16
File Type:pdf, Size:1020Kb
24.09.2009 API Hooking Tricks of the Hackers: 2 API Hooking and DLL Injection Intercepting API calls is a mechanism for testing Dr. Wolfgang Koch monitoring Friedrich Schiller University Jena and reverse engineering Department of Mathematics and as well as for altering the behavior of the Computer Science operating system Jena, Germany or of 3rd party products, [email protected] without having their source code available. API Hooking Literature Books 3 4 Intercepting API calls is a mechanism for “The Windows-API Book” : Jeffrey Richter, altering the behavior of programs or of the Christophe Nasarre operating system WINDOWS via C/C++ 5th edition widely used by hackers and other “bad guys” Redmond, Wash : Microsoft Press, 2008 ISBN-13: 978-0-7356-2424-5 820 p. + Companion content Web page 1 24.09.2009 Literature Books Literature 5 6 Ivo Ivanov: API hooking revealed, 2002 The WDM Bible : http://www.codeproject.com/KB/system/hooksys.aspx Walter Oney Robert Kuster: Three Ways to Inject Your Code Programming the Microsoft Windows Driver Model into Another Process, 2003 2nd edition http://www.codeproject.com/KB/threads/winspy.aspx Redmond, Wash : Microsoft Press, 2003 Seung-Woo Kim - Intel® Software Network: Intercepting ISBN: 0-7356-1803-8 System API Calls, 2004 http://software.intel.com/en-us/articles/ intercepting-system-api-calls/ 846 p. + CD-ROM Literature Literature Executable File Format 7 8 Anton Bassov: Microsoft MSDN man pages and “ white papers ”: Process-wide API spying - an ultimate hack, 2004 http://msdn.microsoft.com/library http://www.codeproject.com/KB/system/api_spying_hack.aspx An In-Depth Look into the Win32 Portable Executable Kernel-mode API spying - an ultimate hack, 2005 File Format, Matt Pietrek, MSDN Magazine, Feb. 2002 http://www.codeproject.com/KB/system/kernelspying.aspx http://msdn.microsoft.com/en-us/magazine/cc301805.aspx Newsgroups: comp.os.ms-windows.programmer.nt.kernel-mode Microsoft Portable Executable and Common Object microsoft.public.development.device.drivers File Format Specification, Revision 8.0 - May 2006 http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx 2 24.09.2009 API Hooking API Hooking 9 10 API Routine Call: API - Application Programming Interface Wikipedia: ... push Parameter_2 push Parameter_1 API - is an interface that defines the ways by which an call <Addr_of_API_Routine> application program may request services from mov eax, Result libraries and/or operating systems . ... An API determines the vocabulary and calling // API Routine (in DLL) conventions the programmer should employ to use push ebp mov esp, ebp the services. It may include specifications for routines , ... // service data structures , object classes and protocols … ret API Hooking API Hooking 11 12 API Routine Call - hooked: API Routine Call – hooked (2): // proxy function //proxy function push ebp push ebp ... ... // other stuff ... ... // pre proc. push Parameter_2 ret push Parameter_2 call original API push Parameter_1 push Parameter_1 ... // post proc. ret callcall <Addr_of_API_Routine><Addr_of_new_Routine > call <Addr_of_API_Routine><Addr_of_new_Routine > mov eax, Result mov eax, Result ... ... //API Routine (in DLL) //API Routine (in DLL) push ebp push ebp mov esp, ebp mov esp, ebp We would have to overwrite every ... // service • Parameters of the API routine ... // service call to the API routine in the code ret • Win32 API : _stdcall – ret n ret 3 24.09.2009 API Hooking API Hooking 13 14 API Routine Call – hooked Hooking API Routine Calls different way – overwriting code // proxy function call <Addr_of_API_Routine > (0xE8) push ebp ... ... // other stuff we would have to overwrite every call to the push Parameter_2 ret push Parameter_1 API routine in the code call <Addr_of_API_Routine> mov eax, Result In Windows executables indirect calls are applied: ... call ind(Ptr_To_Addr_of_API_Routine ) //API Routine (in DLL) jmppush< Addr_of_proxy_ebp The Pointer points to the address of the API Routine mov esp,Routine ebp > Difficulties when calling or jumping ... // service in the Import Address Table (IAT), where the addresses to the original API routine. ret of all functions, imported by the module, are stored. API Hooking API Hooking Locating the IAT 15 16 In Windows executables indirect calls are applied: see Portable Executable File Format: call ind(Ptr_To_Addr_of_API_Routine ) IMAGE_DOS_HEADER * dosheader = (IMAGE_DOS_HEADER *) hMod; (0xFF,0x15) IMAGE_OPTIONAL_HEADER * opthdr = (IMAGE_OPTIONAL_HEADER *) Reason: ((BYTE*)hMod + dosheader->e_lfanew + 24); The actual address of the API Routine is not known at compile time. IMAGE_IMPORT_DESCRIPTOR * descriptor = (IMAGE_IMPORT_DESCRIPTOR *) ((BYTE*) hMod + opthdr-> The Import Address Table is filled in, when a Library DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. (a DLL) is loaded. VirtualAddress ); Now we can change the addresses in the IAT while (descriptor->FirstThunk){ (only one per API routine) to our proxy functions. char * dllname = (char *)((BYTE*)hMod + descriptor->Name); 4 24.09.2009 API Hooking IAT_Entry_Addresses API Hooking IAT_Entry_Addresses 17 18 while (descriptor->FirstThunk){ IATentryaddress = (DWORD*) char * dllname = (char *)((BYTE*)hMod + descriptor->Name); ((BYTE*) hMod + descriptor->FirstThunk) + x; IMAGE_THUNK_DATA * thunk = (IMAGE_THUNK_DATA *) ((BYTE*) hMod + descriptor->OriginalFirstThunk); Now we can overwrite IAT entries of the target module with int x=0; the addresses of our user-defined proxy functions. while (thunk->u1.Function) { Each IAT entry replacement normally requires a separate char * functionname = (char*) ((BYTE*) hMod + (DWORD)thunk->u1.AddressOfData + 2); proxy function - a proxy function must know which particular DWORD * IATentryaddress = (DWORD*) ((BYTE*) hMod + API function it replaces so that it can invoke the original callee. descriptor->FirstThunk) + x; x++; thunk++; } Anton Bassov proposed the following method descriptor++; } API Hooking API Hooking 19 20 Anton Bassov proposed the following method 4 (unique) functions, one structure, 4 (unique) functions, one structure : 22 bytes of allocated (executable) memory per IAT entry: ProxyProlog(), ProxyEpilog() - Assembler Prolog(), Epilog() - normal C code 0xFF15 Addr_RelFkt struct RelocatedFunction 2 4 16 Bytes struct RelocatedFunction{ DWORD proxyptr; 0xFF15 Addr_RelFkt – DWORD functionptr; indirect call: call ind(Addr_RelFkt) char * dllname; i.e. calls RelocatedFunction.proxyptr char * functionname; Trick: The return address of this proxy function – on top of the }; stack – is the address of our struct RelocatedFunction !!! 5 24.09.2009 API Hooking Modifying IAT Entries API Hooking Modifying IAT Entries 21 22 while(thunk->u1.Function) { uchar * mptr = malloc(22); // <allocate memory> char * functionname = ... ; // <fill in memory and store original API address> DWORD * IATentryaddress = ... + x; struct RelocatedFunction * reloc = (struct RelocatedFunction *) (mptr+6); if ( alter(functionname) ){ <allocate memory> reloc->proxyptr = (DWORD) ProxyProlog; <fill in memory and store original API address> reloc->functionptr = *IATentryaddress; // orig. <modify *IATentryaddress > reloc->functionname = functionname; } reloc->dllname = dllname; mptr[0]= 0xFF; mptr[1]= 0x15; // JMP ind x++; thunk++; memmove( &mptr[2], &reloc, 4); } API Hooking Modifying IAT Entries API Hooking Modifying IAT Entries 23 24 uchar * mptr = malloc(22); Usually the IAT is writeable … . if (! WriteProcessMemory( GetCurrentProcess(), // < modify *IATentryaddress > IATentryaddress, &mptr, 4, NULL)) { DWORD byteswritten; DWORD dwOldProt; if ( VirtualProtect (IATentryaddress, 4, WriteProcessMemory ( GetCurrentProcess(), PAGE_READWRITE, &dwOldProt)) { IATentryaddress, WriteProcessMemory( GetCurrentProcess(), &mptr, 4, &byteswritten); IATentryaddress, &mptr, 4, NULL); VirtualProtect(IATentryaddress, 4, dwOldProt, Usually the IAT is writeable (it is filled in, when a Library &dwOldProt); } is loaded), but not the Export Directory. } 6 24.09.2009 API Hooking ProxyProlog API Hooking ProxyProlog 25 26 ProxyProlog() saves registers and flags and calls Prolog(). ProxyProlog() saves registers and flags and calls Prolog(). Then it restores the registers and flags and calls (in fact: returns to) __declspec(naked) void ProxyProlog() the original API routine (Prolog modifies the return address). creates pure function code without a stack frame __declspec(naked) void ProxyProlog() – intended for assembler code. { _asm{ push eax; push ebx; push ecx; push edx } I work with cygwin and gcc, there is no __declspec(naked). _asm{ mov ebx,esp; pushf; add ebx,16 } I just define void ProxyProlog() _asm{ push ebx; call Prolog; pop ebx; popf } and instad of reloc->proxyptr = (uint) ProxyProlog; _asm ( pop edx; pop ecx; pop ebx; pop eax; ret) I have } reloc->proxyptr = (uint)((BYTE*)ProxyProlog +3); API Hooking ProxyProlog API Hooking Prolog 27 28 ProxyProlog() saves registers and flags and calls Prolog(). Prolog () does the preprocessing part to the hooked routines _asm{ mov ebx,esp; pushf; add ebx,16 } – for example monitoring all API calls. _asm{ push ebx; call Prolog ; pop ebx; popf } It has access to the actual parameters of the routine as well as to the DLL name and the function name. Prolog is defined as void Prolog(DWORD * stackptr); – it has one parameter – If necessary Prolog() prepares for the call of the ebx , which holds the address of the top of stack post-processing part – ProxyEpilog() and Epilog() (the contents of esp ) just before saving the registers – Prolog() returns to ProxyProlog(),