Presentation is loading. Please wait.

Presentation is loading. Please wait.

Revealing Stealth Malware UMD CMSC389M Xeno Kovah – Jan. 2013 xkovah at gmail Subject line starting with "UMD:" 1.

Similar presentations


Presentation on theme: "Revealing Stealth Malware UMD CMSC389M Xeno Kovah – Jan. 2013 xkovah at gmail Subject line starting with "UMD:" 1."— Presentation transcript:

1 Revealing Stealth Malware UMD CMSC389M Xeno Kovah – Jan xkovah at gmail Subject line starting with "UMD:" 1

2 All materials is licensed under a Creative Commons "Share Alike" license. 2

3 Outline Note to self, go back and show FindFirstFile() struct again Type 2 – change things that shouldn't be changed – Windows Portable Executable(PE) file format background – Import Address Table (IAT) or Export Address Table (EAT) hooks A userspace OR kernel technique 3

4 A portrait of the rootkit as a young man in the middle 4 (CC BY-NC-SA 2.0) image by thrill kills sunday pills

5 Normal Inter-Module Function Call 5 … push 1234 call [0x40112C] add esp, 4 … Import Address Table 0x40112C:SomeFunc 0x401130:SomeJunk 0x401134:ScumDunk … SomeFunc: mov edi, edi push ebp mov ebp, esp sub esp, 0x20 … ret WickedSweetApp.exe WickedSweetLib.dll 1 2

6 Normal Inter-Module Function Call 6 … push 1234 call [0x40112C] add esp, 4 … Import Address Table 0x40112C:EvilFunc 0x401130:SomeJunk 0x401134:ScumDunk … WickedSweetApp.exe 1 4 EvilFunc: … call SomeFunc() … ret WickedWickedDll.dll … SomeFunc: mov edi, edi push ebp mov ebp, esp sub esp, 0x20 … ret WickedSweetLib.dll 2 3

7 Normal Inter-Module Function Call 7 … push 1234 call [0x40112C] add esp, 4 … Import Address Table 0x40112C:MySomeFunc 0x401130:SomeJunk 0x401134:ScumDunk … WickedSweetApp.exe 1 4 MySomeFunc: … call SomeFunc() … ret WickedWickedDll.dll … SomeFunc: mov edi, edi push ebp mov ebp, esp sub esp, 0x20 … ret WickedSweetLib.dll 2 3

8 So what functions should you hook? The same ones as last time! Derp! 8

9 Example use of AppInit_DLLs for DLL injection –This will hook NtQuerySystemInformation(), which is what taskmgr.exe uses in order to list the currently running processes. It will replace this with HookedNtQuerySystemInformation(), which will hide calc.exe –I modified that code to use IAT hooking rather than inline (which is much simpler actually) Steps: –Compile AppInitHookIAT.dll –Place at C:\tmp\AppInitHookIAT.dll for simplicity –Use regedit.exe to add C:\tmp\AppInitHookIAT.dll as the value for the key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows\AppInit_DLLs and set the other values from the previous slide –Start calc.exe, start taskmgr.exe, confirm that calc.exe doesn't show up in the list of running processes. –Remove C:\tmp\AppInitHookIAT.dll from AppInit_DLLs and restart taskmgr.exe. –Confirm calc.exe shows up in the list of running processes. –(This is a basic "userspace rootkit" technique. Because of this, all entries in this registry key should always be looked upon with suspicion.) 9

10 10 But what do you do? I'm an importer!

11 IAT Hooks We now need to pull in a minimal breeze-through of content from the Life of Binaries OpenSecurityTraining class. Specifically we want to know how an attacker can find the Import/Export Address Tables in order to modify them. An alternative view of what I'm about to show is here: nce/Windows%20Memory%20Layout,%20User- Kernel%20Address%20Spaces.pdf (I would use this, but I want to cut it down to only the minimal information we need to know) 11

12 12 taskmgr.exe's memory space (not to scale :P) Kernel (> 0x ) Userspace (<= 0x7FFFFFFF) TEB (Thread Environment Block) struct PEB (Process Environment Block) struct PEB_LDR_DATA struct LDR_DATA_TABLE_ENTRY linked list entry taskmgr.exe (mapped PE file) ntdll.dll (mapped PE file) kernel32.dll (mapped PE file)... user32.dll (mapped PE file) FS:[0] The kernel ensures that the FS CPU segment register points at a segment that has its base address starting at the TEB (e.g. first 4 bytes can be read with "mov eax, FS:[0]") You learn more about segment registers in the Intermediate x86 OST class On Win XP

13 13 Kernel (> 0x ) Userspace (<= 0x7FFFFFFF) TEB (Thread Environment Block) struct PEB (Process Environment Block) struct PEB_LDR_DATA struct LDR_DATA_TABLE_ENTRY linked list entry taskmgr.exe (mapped PE file) ntdll.dll (mapped PE file) kernel32.dll (mapped PE file)... user32.dll (mapped PE file) Common shellcode to find kernel32.dll (because it has lots of useful functions) Simplified from in32-shellcode.pdf and used in the LoB tutorial virus. //put addr of PEB into eax mov eax, fs:[0x30]; //put addr of PEB_LDR_DATA into eax mov eax, [eax + 0xC]; //put addr of first LDR_DATA_TABLE_ENTRY //in InInitializationOrderModuleList into esi mov esi, [eax + 0x1C]; //lodsd is like "mov eax, [esi]" //presumably chosen for small code size //Put addr of 2nd LDTE (abbrev) to eax lodsd; //get base address of kernel32 out of //LDR_DATA_TABLE_ENTRY struct mov eax, [eax + 0x8]; On Win XP

14 14 taskmgr.exe's memory space (not to scale :P) Kernel (> 0xFFFFF ) Userspace (<= 0x00007FFFFFFFFFFF) TEB (Thread Environment Block) struct PEB (Process Environment Block) struct PEB_LDR_DATA struct LDR_DATA_TABLE_ENTRY linked list entry taskmgr.exe (mapped PE file) ntdll.dll (mapped PE file) kernel32.dll (mapped PE file)... user32.dll (mapped PE file) GS:[0] The kernel ensures that the GS CPU segment register points at a segment that has its base address starting at the TEB (e.g. first 4 bytes can be read with "mov eax, GS:[0]") You learn more about segment registers in the Intermediate x86 OST class On Win 7 x64

15 15 TEB (Thread Environment Block) struct PEB (Process Environment Block) struct PEB_LDR_DATA struct LDR_DATA_TABLE_ENTRY linked list entry taskmgr.exe (mapped PE file) ntdll.dll (mapped PE file) kernel32.dll (mapped PE file)... user32.dll (mapped PE file) Example shellcode to find kernel32.dll Adapted from shellcode/source/browse/trunk/w64-exec- calc-shellcode.asm?r=3 //put addr of PEB into rsi mov rsi, gs:[0x60]; //put addr of PEB_LDR_DATA into rsi mov rsi, [rsi + 0x18]; //put LDR_DATA_TABLE_ENTRY[0] (taskmgr) //in InLoadOrderModuleList into rsi mov rsi, [rsi + 0x10]; //lodsq is like "mov rax, [rsi]" //rax = LDR_DATA_TABLE_ENTRY[1] (ntdll) lodsq; //rsi = LDR_DATA_TABLE_ENTRY[2] (kernel32) mov rsi, [rax] //get base address of kernel32 out of //LDR_DATA_TABLE_ENTRY struct mov rdi, [rsi + 0x30]; On Win 7 x64 Kernel (> 0xFFFFF ) Userspace (<= 0x00007FFFFFFFFFFF)

16 Let's look at some structs! (yaaaay!) Use the "dt" (display type) command in windbg to show a structure definition By convention Windows tends to name structs like typedef struct _FOO{ char a[4] } FOO, *PFOO; So you should generally look for structs assuming an underscore on the front of the name 16

17 Let's look at some structs! (yaaaay!) Don't forget your windbg superstition, always check your File->Symbol File Path is set to the SRV*… address from the day 1 part 2 slides, and then do a ".reload" in order to make sure your symbols are loaded. Then you can do things like dt _TEB – in which case it has to search all modules for the symbol, thankfully ntdll is the first one it searches dt ntdll!_TEB Or to interpret some particular location as a TEB, first do !teb so it will dump the address for you and then do dt ntdll!_TEB 17

18 Then what? Well, let's go try to hack the IAT When you get the base address of the module you're interested in, it turns out you can parse the PE headers which will still typically be mapped into memory. PE = Portable Executable = Windows' binary format This is where you *really* need LoB to due it justice, but you'll get the quick and dirty version 18

19 19 Let's say you want to: Modify the code called when taskmgr.exe tries to call functions like NtQuerySystemInfo() You need to find the IAT entries that point at that function IMAGE_DOS_HEADER struct IMAGE_NT_HEADER struct OptionalHeader.DataDirectory[0] OptionalHeader.DataDirectory[1] OptionalHeader.DataDirectory[2]... IMAGE_OPTIONAL_HEADER struct IMAGE_IMPORT_DESCRIPTOR (advapi32.dll) IMAGE_IMPORT_DESCRIPTOR (kernel32.dll) IMAGE_IMPORT_DESCRIPTOR (ntdll.dll)... IMAGE_NT_HEADER struct Import Names Table (INT) Import Addresses Table (IAT) One per module that is imported from 1 st entry points at imports info

20 20 You are here :D Image by Ero Carrera Care about this

21 21 Image by Ero Carrera Care about this

22 22 Image by Ero Carrera Care about this

23 FYI tho The optional header is different size in 32 bit binaries vs. 64 bit ones. So the previous and next slide is 32 bit. 2 slides forward is 64 bit Calculate offsets accordingly 23

24 typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 24 From winnt.h

25 typedef struct _IMAGE_OPTIONAL_HEADER64 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; ULONGLONG ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; ULONGLONG SizeOfStackReserve; ULONGLONG SizeOfStackCommit; ULONGLONG SizeOfHeapReserve; ULONGLONG SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; 25 From winnt.h

26 26 Image by Ero Carrera

27 27 Image by Ero Carrera

28 Import Descriptor (from winnt.h) typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) //Xeno Comment: In reality a PIMAGE_THUNK_DATA }; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) //Xeno Comment: In reality a PIMAGE_THUNK_DATA } IMAGE_IMPORT_DESCRIPTOR; While the things in blue are the fields filled in for the most common case, we will actually have to understand everything for this structure, because you could run into all the variations. 28 I think they meant "INT"

29 29 Image by Ero Carrera

30 Graphical style borrowed from the Matt Pietrek articles 30 OriginalFirstThunk TimeDateStamp ForwarderChain Name FirstThunk … IMAGE_IMPORT_DESCRIPTOR Zero-filled IMAGE_IMPORT_DESCRIPTOR entry terminates the array ntdll.dll Import Names Table (IMAGE_THUNK_DATA array) Import Address Table (IMAGE_THUNK_DATA array) 0x014B, NtQuerySysInfo 0x040B, RtlInitUnicodeString 0x01DA, IofCompleteRequest Array of IMAGE_IMPORT_BY_NAME Structures stored wherever in the file Review: Import data structures ON DISK

31 Graphical style borrowed from the Matt Pietrek articles 31 OriginalFirstThunk TimeDateStamp ForwarderChain Name FirstThunk … IMAGE_IMPORT_DESCRIPTOR Zero-filled IMAGE_IMPORT_DESCRIPTOR entry terminates the array ntdll.dll Import Names Table (IMAGE_THUNK_DATA array) Import Address Table (IMAGE_THUNK_DATA array) 0x014B, NtQuerySysInfo 0x040B, RtlInitUnicodeString 0x01DA, IofCompleteRequest Array of IMAGE_IMPORT_BY_NAME Structures stored wherever in the file Review: Import data structures IN MEMORY AFTER IMPORTS RESOLVED IAT entries now point to the full virtual addresses where the functions are found in the other modules (just ntoskrnl.exe in this case)

32 IAT Hooking When the IAT is fully resolved, it is basically an array of function pointers. Somewhere, in some code path, there's something which is going to take an IAT address, and use whatever's in that memory location as the destination of the code it should call. What if the "whatever's in that memory location" gets changed after the OS loader is done? What if it points at attacker code? 32

33 IAT Hooking 2 Well, that would mean the attacker's code would functionally be "man-in-the-middle"ing the call to the function. He can then change parameters before forwarding the call on to the original function, and filter results that come back from the function, or simply never call the original function, and send back whatever status he pleases. –Think rootkits. Say you're calling FindNextFile(). It looks at the file name and if you're asking for a file it wants to hide, it simply returns "no file found." But how does the attacker change the IAT entries? This is a question of assumptions about where the attacker is. 33

34 IAT Hooking 3 In a traditional memory-corrupting exploit, the attacker is, by definition, in the memory space of the attacked process, upon successfully gaining arbitrary code execution. The attacker can now change memory such as the IAT for this process only, because remember (from OS class or Intermediate x86) each process has a separate memory space. If the attacker wants to change the IAT on other processes, he must be in their memory spaces as well. Typically the attacker will format some of his code as a DLL and then perform "DLL Injection" in order to get his code in other process' memory space. The ability to do something like DLL injection is generally a prerequisite in order to leverage IAT hooking across many userspace processes. In the kernel, kernel modules are generally all sharing the same memory space with the kernel, and therefore one subverted kernel module can hook the IAT of any other modules that it wants. 34

35 DLL Injection See for more ways that this can be achieved on Windows/*nix We're going to use the AppInit_DLLs way of doing this for simplicity Note: AppInit_DLLs' behavior has changed in releases > XP, it now has to be enabled with Administrator level permissions. Must set the DLL in question in the registry key: –HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows\AppInit_DLLs –Use comma delimitation if there is an existing entry Must also set the following key to 1 –HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows\LoadAppInit_DLLs Must also set the following key to 0 –HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows\RequireSignedAppInit_DLLs 35

36 Example use of AppInit_DLLs for DLL injection –This will hook NtQuerySystemInformation(), which is what taskmgr.exe uses in order to list the currently running processes. It will replace this with HookedNtQuerySystemInformation(), which will hide calc.exe –I modified that code to use IAT hooking rather than inline (which is much simpler actually) Steps: –Compile AppInitHookIAT.dll –Place at C:\tmp\AppInitHookIAT.dll for simplicity –Use regedit.exe to add C:\tmp\AppInitHookIAT.dll as the value for the key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT \CurrentVersion\Windows\AppInit_DLLs and set the other values from the previous slide –Start calc.exe, start taskmgr.exe, confirm that calc.exe doesn't show up in the list of running processes. –Remove C:\tmp\AppInitHookIAT.dll from AppInit_DLLs and restart taskmgr.exe. –Confirm calc.exe shows up in the list of running processes. –(This is a basic "userspace rootkit" technique. Because of this, all entries in this registry key should always be looked upon with suspicion.) 36

37 Process Explorer us/sysinternals/bb aspx Another useful sysinternals tool for seeing what processes are running, what DLLs are loaded in those processes, and what kernel modules are loaded 37

38 This button right here will toggle back and forth between showing DLLs or "handles" at the bottom (when in "System" process the "DLLs" are actually the kernel modules) This is it toggled

39 Expanding on using dt in windbg & using it with CFF Explorer You can use dt to get from the _TEB to the _PEB to the _PEB_LDR_DATA to the _LDR_DATA_TABLE_ENTRY[] by combining the topics in slides 14-17, but once you get to the module, how do you get to the IAT in windbg by combining that information with the PE structs and/or CFF Explorer data. 39

40 IMAGE_DOS_HDR IMAGE_OPTIONAL_HEADER

41 IMAGE_DOS_HDR Offset into the file Value in the struct at that offset Care about this

42 Assume you already found the base address of regedit by walking from the TEB (that's how lm does it). In this case it's 0x ff So now you want to make sure you're getting the right values for the right offsets. We can use our friendly friend "dt" again to "cast" this memory location to an IMAGE_DOST_HEADER, and then confirm we see the same thing as in CFF explorer from the previous slide. Decimal (0n prefix here) 0n232 = 0xE8, and they're both at the same offset into the struct, 0x3c. (FYI a fast way to convert between bases in windbg is ".formats 0n232")

43 Offset into the file Value in the struct at that offset Care about this This is an RVA, a Relative Virtual Address. Meaning it's a relative offset from the base to get to the next structure The sometimes good, sometimes bad thing about CFF Explorer is that although the Optional Header is embedded in the NT Header, and the data directory is embedded in the Optional Header, it puts them as separate looking things. But the key point is that you have to go DD[0] DD[1] DD[2] …

44 Alright let's use that 0xE8 offset from the DOS header to get to the NT header And then go to the optional header So you see, all this is just C-style casting of data to a particular structure definition. You can do the same sort of thing in your C code. And if you add it all up, you will see that the offset to the DD[1].VirtualAddress field is 0x178 like shown on the previous slide And then the data directory Well that's entry [0], like shown on the previous slide, but we want entry [1]

45 Disclaimer Don't just hardcode these values. Cast to memory to structs and pull fields from structs. Because if you don't, then what might work on regedit might not work on some other app (e.g. the offset to the start of the NT header on calc.exe is 0xF0 not 0xE8). You want to write code that is maximally compatible. 45

46 Disclaimer 2 How did I know it was _IMAGE_NT_HEADER64 instead of just _IMAGE_NT_HEADER? Well, sometimes you need to go back to the source to find the correct struct name. As previously said, the structs for PE headers are defined in winnt.h (see next slide) 46

47 typedef struct _IMAGE_NT_HEADERS64 { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER64 OptionalHeader; } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; #ifdef _WIN64 typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS; typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS; #else typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS; typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS; #endif 47

48 SO! You've got an offset, 0x20e70 to get to the _IMAGE_IMPORT_DESCRIPTOR. Unfortunately for you, at this point you're SOL, because that struct isn't defined in any of the modules! Time to reinforce that understanding of data type sizes. If the structure looks like this (cut down from slide 28): typedef struct _IMAGE_IMPORT_DESCRIPTOR { DWORD OriginalFirstThunk; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; So, is that the right module? IDK, you tell me :P Then that means the RVA to the OriginalFirstThunk (INT) table is at 0x The RVA to the Name is at 0x And the RVA to the FirstThunk (IAT) table is at 0x1F000. But let's check the name first because we don't even know if this is the right module yet.

49 But let's assume that's the right module, then we want to try and find the IAT entry that has the same index as a particular INT entry which points at a particular function we want to hook. So lets look at the INT. Again, that OriginalFirstThunk was an RVA, which means it's a base- relative offset to an array, each entry of which is yet another RVA (but this time 64 bit) pointing at a hint/name structure (see slides 30/31). There are some nice hint/name structures! And if we then pull out the first 64 bit RVA

50 Sanity checking that all against CFF Explorer… _IMAGE_IMPORT_DESCRIPTOR array Entries in the INT pointed to by the thing you're currently clicking in the IMAGE_IMPORT_DESCRIPTOR array Entries in the IAT pointed to by the thing you're currently clicking in the IMAGE_IMPORT_DESCRIPTOR array Hint/name pointed to by INT

51 For more help Read this (can skip the exports section but most of the rest of it is relevant) db.com/download_pdf/18576/ 51

52 Beyond this point is not considered in scope for the class 52

53 53 Only imports? I'm an importer/e xporter!

54 Exporting Functions & Data For a library to be useful, other code which wants to use its functions must be able to import them, as already talked about. There are two options to export functions and data. They can be exported by name (where the programmer even has the option to call the exported name something different than he himself calls it), or they can be exported by ordinal. An ordinal is just an index, and if a function is exported by ordinal, it can only be imported by ordinal. While exporting by ordinal saves space, by not having extra strings for the names of symbols, and time by not having to search the strings, it also puts more work on the programmer which wants to import the export. But it can also be a way to make a private (undocumented) API more private. 54

55 55 Let's say you want to: Find functions kernel32.dll exports, so you can call them You need to find the EAT entries that point at those functions IMAGE_DOS_HEADER struct IMAGE_NT_HEADER struct OptionalHeader.DataDirectory[0] OptionalHeader.DataDirectory[1] OptionalHeader.DataDirectory[2]... IMAGE_OPTIONAL_HEADER struct IMAGE_EXPORT_DIRECTORY IMAGE_NT_HEADER struct Export Addresses Table (EAT) Export Names Table (ENT) 0 th entry points at exports info NameOrdinal Table Actual strings pointed to by ENT

56 56 You are here :D Image by Ero Carrera Care about this

57 57 Image by Ero Carrera Care about this

58 58 Image by Ero Carrera Care about this

59 59 X X X X Image by Ero Carrera

60 Exports from winnt.h typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; 60

61 Exports 2 The TimeDateStamp listed here is what's actually checked against when the loader is trying to determine if bound imports are out of date for instance. Can be different from the one in the File Header (see ntdll.dll). Presumably (wasn't able to confirm), the linker only updates this if there are meaningful changes to the RVAs or order for exported functions. That way, the TimeDateStamp “version” can stay backwards compatible as long as possible. NumberOfFunctions will be different from NumberOfNames when the file is exporting some functions by ordinal (talked about later). By knowing the number of names, when searching for an import by name, the loader can do a binary search. 61

62 Exports 3 Base is the number to subtract from an ordinal to get the zero-indexed offset into the AddressOfFunctions array. Because ordinals start at 1 by default, this is usually 1. However ordinals could start at 10 if the programmer wants them to, and therefore Base would then be set to 10. AddressOfFunctions is an RVA which points to the beginning of an array which holds DWORD RVAs which point to the start of the exported functions. The pointed-to array should be NumberOfFunctions entries long. This would be the Export Address Table (EAT) like the flip side of the Import Address Table (IAT). Eat! I atè! :P 62

63 Exports 4 AddressOfNames is an RVA which points to the beginning of an array which holds DWORD RVAs which point to the strings which specify function names. The pointed-to array should be NumberOfNames entries long. This would be the Export Names Table (ENT) like the flipside of the Import Names Table (INT). AddressOfNameOrdinals is an RVA which points to the beginning of an array which holds WORD (16 bit) sized ordinals. The entries in this array are already zero-indexed indices into the EAT, and therefore are unaffected by Base. 63

64 Ordinal says what? When importing by name, like I said, it can do a binary search over the strings in the ENT, because nowadays, they're lexically sorted. “Back in the day” they weren't sorted. Back then, it was strongly encouraged to “import by ordinal”, that is, you could specify “I want ordinal 5 in kernel32.dll” instead of “I want AddConsoleAliasW in kernel32.dll”, because if the names aren't sorted, you're doing a linear search. You can still import by ordinal if you choose, and that way your binary/library will load a bit faster. Even if you're importing by name, it is actually just finding the index in the ENT, and then selecting the same index in the AddressOfNameOrdinals, and then reading the value from the AddressOfNameOrdinals to use as an index into the EAT. Generally speaking, the downside of importing by ordinal is that if the ordinals change, your app breaks. That said, the developer who's exporting by ordinal has incentive to not change them, unless he wants those apps to break (e.g. to force a deprecated API to not be used any more). 64

65 More about Base/ordinals relation 65 Characteristics TimeDateStamp MajorVersion MinorVersion Name Base = 1 NumberOfFunctions NumberOfNames AddressOfFunctions AddressOfNames AddressOfNameOrdinals IMAGE_EXPORT_DIRECTORY 0x A0x x x00004BC60x00004ED60x A EAT Modified graphical style borrowed from Matt Pietrek articles ACLEDIT.dll (note the lexical order, note to self, talk about lexical ordering necessitating the ordinal table) Index 0 Ordinal 1 Index 1 Ordinal 2 Index 2 Ordinal 3 Index 3 Ordinal 4 Index 4 Ordinal 5 Index 5 Ordinal 6

66 More about Base/ordinals relation 66 Characteristics TimeDateStamp MajorVersion MinorVersion Name Base = 37 NumberOfFunctions NumberOfNames AddressOfFunctions AddressOfNames AddressOfNameOrdinals IMAGE_EXPORT_DIRECTORY 0x A0x x x00004BC60x00004ED60x A EAT Modified graphical style borrowed from Matt Pietrek articles ACLEDIT.dll (note the lexical order, note to self, talk about lexical ordering necessitating the ordinal table) Index 0 Ordinal 37 Index 1 Ordinal 38 Index 2 Ordinal 39 Index 3 Ordinal 40 Index 4 Ordinal 41 Index 5 Ordinal 42

67 Talk the walk (search for import EditOwnerInfo by name and then by ordinal) 67 Characteristics TimeDateStamp MajorVersion MinorVersion Name Base NumberOfFunctions NumberOfNames AddressOfFunctions AddressOfNames AddressOfNameOrdinals IMAGE_EXPORT_DIRECTORY DLLMainEditAuditInfoEditOwnerInf o EditPermissionInfoFMExtensionProcWSedDiscretionaryActEditor 0x x000138E40x000138F20x x B0x C 0x A0x x x00004BC60x00004ED60x A 0x00030x00000x00010x00020x0005 EAT ENT NameOrdinals Modified graphical style borrowed from Matt Pietrek articles ACLEDIT.dll (note the lexical order, note to self, talk about lexical ordering necessitating the ordinal table)

68 How does one go about specifying an export? us/library/hyx1zcd3(VS.80).aspx “There are three methods for exporting a definition, listed in recommended order of use: –The __declspec(dllexport) keyword in the source code –An EXPORTS statement in a.def file –An /EXPORT specification in a LINK command” 68

69 From HelloWorldDLL's DllMain.c __declspec(dllexport) void __cdecl SayHello( HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { MessageBox(0, "Hello World!", 0, 0); }

70 Where to specify a.def file 70

71 Forwarded Exports There is an option to forward a function from one module to be handled by another one (e.g. it might be used if code was refactored to move a function to a different module, but you wanted to maintain backward compatibility.) As we just saw, normally AddressOfFunctions points to an array of RVAs which point at code. However, if a RVA in that array of RVAs points into the exports section (as defined by the base and size given in the data directory entry), then the RVA will actually be pointing at a string of the form DllToForwardTo.FunctionName 71

72 Kernel32.dll forwarded (to ntdll.dll) exports 72

73 How does one go about forwarding exports? Statement in.def file of the form EXPORTS FunctionAlias=OtherDLLName.RealFunction or /export linker option /export:FunctionAlias=OtherDLLName.RealFunction Can even specify a linker comment in the code with #pragma comment(linker, "/export:FunctionAlias=OtherDLLName.RealFunction") 73

74 PE aside: Forwarded Exports There is also the capability in PE files to export a function name, but then say to the OS loader, "Actually, foo.dll's FunctionFoo() is what really implements this function. Use that instead." This can be used to set up a MitM situation with trojan DLLs. And where there's MitM there's the potential to hide stuff. 74

75 Relevance to Stuxnet Stuxnet used forwarded exports for the 93 of 109 exports in s7otbxdx.dll which it didn't need to intercept. 75 See notes for citation

76 Stuxnet trojaned DLL 76 See notes for citation

77 Function Redirection Tutorial ntercept_apis_dll_redirection.pdf Basically talks about making a trojan DLL which hooks or reimplements some functions for the intercepted DLL, and then forwards the rest on to the original. Basically exactly what Stuxnet did for the trojan PLC accessing DLL. 77


Download ppt "Revealing Stealth Malware UMD CMSC389M Xeno Kovah – Jan. 2013 xkovah at gmail Subject line starting with "UMD:" 1."

Similar presentations


Ads by Google