Gaining important datas from PEB under NT boxes -Ratter/29A[6] March 2002 翻译 by nEINEI/vxjump.net [0x01] Intro [0x02] Operating system families differentiating [0x03] Getting the kernel32.dll base from PEB [0x04] Getting kenel32.dll image base [0x05] Getting the current directory from PEB [0x06] Getting current directory [0x07] Closing [0X01]Intro 经过这几年的使用,大家早已熟悉SEH的利用技巧了[译注:大意是指利用SEH技巧获 得kernel32基址一类的情况,也可能指vx编码中使用的那个技巧,附录中我会给出]。当你 设置一个异常栈帧的时候,在你工程或代码里面或多或少的都有会使用到FS这个段选择子。 这个选择子可以指向了TEB线程环境块,这个结构包含了很多有用的结构体和值,对我们来 说还有更重要的一个,也就是进程环境块。 struct TEB { struct _NT_TIB NtTib; void* EnvironmentPointer; struct _CLIENT_ID ClientId; void* ActiveRpcHandle; void* ThreadLocalStoragePointer; struct _PEB* ProcessEnvironmentBlock; ; offset 30h .... struct _ACTIVATION_CONTEXT_STACK ActivationContextStack; }; 这是第一次使用PEB在病毒编码中,由于在NT系统中PEB始终被映射在地址7FFDF000h空间 (始终低于80000000h地址空间,因为那时内核内存及驱动所在的空间),但在win9x中它始终在 高于80000000h的内存共享区域空间里面,我们可以利用这特点来鉴别病毒运行时所处于的操作 系统家族情况,下面一个代码片段是从Win32k.Taichi中选取的。 [0x02] Operating system families differentiating mov eax, dword ptr fs:[30h] ; gimme the PEB pointer test eax, eax ; it is above 80000000h ? js jmp_to_host ; if yep end ... ; runned under WinNT box [0x03] Getting the kernel32.dll base from PEB 对我们来说还有两个重要的数据结构,PEB - PEB_LDR_DATA 和 RTL_USER_PROCESS_PARAMETERS , 首先使用PEB_LDR_DATA 中偏移0x0c 来获得 PEB_LDR_DATA kernel32.dll image base. struct PEB_LDR_DATA { DWORD Length; ; 0 BYTE Initialized; ; 4 void* SsHandle; ; 8 struct LIST_ENTRY InLoadOrderModuleList; ; 0ch struct LIST_ENTRY InMemoryOrderModuleList; ; 14h struct LIST_ENTRY InInitializationOrderModuleList; ; 1ch }; struct LIST_ENTRY { struct LIST_ENTRY* Flink; ; 0 struct LIST_ENTRY* Blink; ; 4 }; 刚开始看起来可能比较乱,但是请相信我,我们一块深入的看一下PEB_LDR_DATA这个 结构。我的需求是要利用这些数据,PEB:PEB_LDR_DATA:1ch,这个列表是一个初始化模块的 顺序列表(ie dlls),因为是LIST_ENTRY结构,那究竟是向前还是向后呢,我会向前使用第一个 模块,如果我们需要的是ntdll.dll的image base,那么这正是我们需要的。但是我们需要的 kernel32.dll的image base啊。没关系继续往前,就是我们需要的了。让我们看一个代码片段 (希望这样能更容易理解一些) [0x04] Getting kenel32.dll image base mov eax, dword ptr fs:[30h] ; PEB base in eax mov eax, dword ptr [eax+0ch] ; goto PEB_LDR_DATA mov esi, dword ptr [eax+1ch] ; get the first entry in the ; InitOrderModuleList ; now the esi points to the LIST_ENTRY entry which also contains ; (besides others) the image base of ntdll.dll ; *esi ; dd *forwards_in_the_list ; esi+0 ; dd *backwards_in_the_list ; +4 ; dd imagebase_of_ntdll.dll ; +8 ; ... ; dd imagetimestamp ; +44h lodsd ; we go forwards mov ebx, dword ptr [eax+08h] ; and finally we get the image base ; of kernel32.dll 当然,顺着这里链表我们可以继续走,我们可以发现其它模块的各种信息(如果其它 的模块对我们有用的话...[译注:指病毒编写中的需求]) [0x05] Getting the current directory from PEB 已经是时候了,我们看一下RTL_USER_PROCESS_PARAMETERS结构,这个结构偏移在PEB:10h , 先看一下入口: struct RTL_USER_PROCESS_PARAMETERS { DWORD MaximumLength; DWORD Length; DWORD Flags; DWORD DebugFlags; void* ConsoleHandle; DWORD ConsoleFlags; void* StandardInput; void* StandardOutput; void* StandardError; struct CURDIR CurrentDirectory; struct UNICODE_STRING DllPath; struct UNICODE_STRING ImagePathName; struct UNICODE_STRING CommandLine; void* Environment; DWORD StartingX; DWORD StartingY; DWORD CountX; DWORD CountY; DWORD CountCharsX; DWORD CountCharsY; DWORD FillAttribute; DWORD WindowFlags; DWORD ShowWindowFlags; struct UNICODE_STRING WindowTitle; struct UNICODE_STRING DesktopInfo; struct UNICODE_STRING ShellInfo; struct UNICODE_STRING RuntimeData; }; struct CURDIR { struct UNICODE_STRING DosPath; void* Handle; }; struct UNICODE_STRING { WORD Length; WORD MaximumLength; DWORD* Buffer; }; 你可能已经看到了非常有用的数据了,例如CommandLine, StandardInput, StandardOutput ..., 我们利用这个结构获得当前目录,这个不需要太多解释,直接看代码了。 [0x06] Getting current directory mov eax, dword ptr fs:[30h] ; goto PEB mov eax, dword ptr [eax+10h] ; goto RTL_USER_PROCESS_PARAMETERS add eax, 24h ; goto CurrentDirectory mov eax, dword ptr [eax+4] ; gimme unicode_buffer ; now in eax we have the pointer to the unicode current directory ; you can convert it to ansi or work with it as it is ... [0x07] Closing 你已经看到了,在TEB,PEB中有很多有用的信息,大量的API函数[译注:作者的意思似乎 是想表达,利用FS就可以获得需要大量API才能获得信息这一点太棒了]都在这些“感人”的结构 当中,继续探索逆向吧(主要是ntdll.dll),告诉我们你得到了什么... 附录: SEH_SetupFrame_ 技巧: @SEH_SetupFrame_ macro p1, p2, p3, p4, p5, p6, p7, p8, p9 local set_new_eh call set_new_eh irp param, <<&p1>, <&p2>, <&p3>, <&p4>, <&p5>, <&p6>, <&p7>, <&p8>, <&p9>> ifb exitm endif param endm set_new_eh: xor edx, edx push dword ptr fs:[edx] mov dword ptr fs:[edx],esp endm 使用方式: pushad @SEH_SetupFrame_ , , pushad @SEH_SetupFrame 参考文献:原文 http://vx.netlux.org/29a/29a-6/29a-6.224