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