g.利用导出表的AddressOfFunctions , AddressOfNames, AddressOfNameOrdinals这三个表来寻找API地址。
CLD;清零CF标志位,使后面的LODSD,STOSD等方向确定
push 0x1e380a6a ;hash code of MessageBoxA
push 0x4fd18963 ;hash code of ExitProcess
push 0x0c917432 ;hash code of LoadLibraryA;以上HASH方式即循环右移7位累加
mov esi, esp;esi指向的是这3个hash码
lea edi, [esi - 0xc];edi现在不用管,以后存储找到的API地址使会用到
;make some stack space
xor ebx, ebx
mov bh, 0x04
sub esp, ebx
;push a pointer to "user32" onto stack
mov bx, 0x3233;push '32'
push ebx
push 0x72657375; push 'user'
push esp
xor edx, edx;以上这段代码的目的是为调用LoadLibraryA预先准备好参数
;下面这段代码我着重说明一下:
;fs:[edx + 0x30]指向的是PEB
;PEB中偏移0x0c处即指向PEB_LDR_DATA的指针
;PEB_LDR_DATA结构如下
typedef struct _PEB_LDR_DATA
{
ULONG Length; // +0x00
BOOLEAN Initialized; // +0x04
PVOID SsHandle; // +0x08
LIST_ENTRY InLoadOrderModuleList; // +0x0c
LIST_ENTRY InMemoryOrderModuleList; // +0x14
LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24
;不难看出我们所需要的InInitializationOrderModuleList在偏移为0x1c处
;至于如何由
InInitializationOrderModuleList得到DLLBASE我们来看下这两个结构
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
WORD LoadCount;
WORD TlsIndex;
union
{
LIST_ENTRY HashLinks;
struct
{
PVOID SectionPointer;
ULONG CheckSum;
};
};
union
{
ULONG TimeDateStamp;
PVOID LoadedImports;
};
_ACTIVATION_CONTEXT * EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY ForwarderLinks;
LIST_ENTRY ServiceTagLinks;
LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
;首先得到
InInitializationOrderModuleList之后,由于它是一个LIST_ENTRY结构,且结构的第一个成员是FLINK,所以直接用代码mov ecx,[ecx]即可以得到第一个成员变量(即链表上面第二个节点)。这里要注意一下,在win7环境中,第3个节点才是kernel32.dll所在。
;那么得到LIST_ENTRY之后为什么使用mov ebp, [ecx + 0x08]就能得到kernel32.dll的基地址呢,仔细看LDR_DATA_TABLE_ENTRY结构,实际上
InInitializationOrderModuleList上的节点也是这个结构的成员。由于在这个结构中,DLLBASE紧接着
InInitializationOrderModuleList,所以加8就能得到其地址了。换言之,如果想用
InOrderList得到DLLBASE,就得加0x18。
;find base_addr of kernel32.dll
mov ebx, fs:[edx + 0x30]
mov ecx, [ebx + 0x0c]
mov ecx, [ecx + 0x1C]
mov ecx, [ecx]
mov ebp, [ecx + 0x08]
这段代码光看的话很容易进入误区,就是call [edi - 0x8]为什么就是调用
LoadLibraryA呢?实际上,由于前面
LoadLibraryA是在那3个HASH码的底部,所以这段代码是先被跳过的,等到后面LoadLibraryA以及ExitProcess都寻址完毕后才会调用。还记得前面讲过EDI指向的是已经被寻址的API的地址吗?所以这里
call [edi -0x8]
很好理解。
find_lib_functions:
lodsd
cmp eax, 0x1e380a6a
jne find_functions
xchg eax, ebp
call [edi -0x8]
xchg eax, ebp
;这个地方弄明白这几个常量的含义:
1.0x3c它是基于DLLBASE的指向PE头的偏移
2.0X78它是基于DLLBASE的指向导出表的偏移
3.0X20它是基于DLLBASE的指向函数名称表AddressOfNames的偏移
find_functions:
pushad ;preserve registers
mov eax, [ebp + 0x3c]
mov ecx, [ebp + eax + 0x78]
add ecx, ebp
mov ebx, [ecx + 0x20]
add ebx, ebp
xor edi, edi
;这段代码是逐次取得名称表中的各项
next_function_loop:
inc edi
mov esi, [ebx + edi * 4]
add esi, ebp
cdq
;然后用找到的各项生成HASH码
hash_loop:
movsx eax, byte ptr[esi]
cmp al, ah;这里比较难理解,实际上是为了判断名称是否结束(由于shellcode中不能出现结束符0,所以用这句话代替)
jz compare_hash
ror edx, 7
add edx, eax
inc esi
jmp hash_loop
compare_hash:
cmp edx, [esp + 0x1c];这里的[esp + 0x1c]为什么取得就是要比较的HASH呢,这里实际上是之前用PUSHAD将寄存器组压栈之后,这里在使用当时压入的EAX
jnz next_function_loop
;这里要介绍一下通过那三个表
AddressOfFunctions , AddressOfNames, AddressOfNameOrdinals寻址API的方法。首先在
AddressOfNames中获得对应名称的项号,然后在
AddressOfNameOrdinals中通过该项号找到真正的在
AddressOfFunctions
中的项号,最后在
AddressOfFunctions
中寻址。即,地址 =
AddressOfFunctions[
AddressOfNameOrdinals[名称表中的项号]]。这里就比较疑惑了,为什么中间要走
AddressOfNameOrdinals这个表呢,实际上,由于不是所有的API都有名字,所以说
AddressOfNames的项数肯定要比
AddressOfFunctions
中少,且它们并不是一一对应的,所以要这么做。
mov ebx, [ecx + 0x24];名称序号表的偏移
add ebx, ebp
mov di, [ebx + 2 * edi];得到函数地址表的项号
mov ebx, [ecx + 0x1c];函数地址表的偏移
add ebx, ebp
add ebp, [ebx + 4 * edi];得到函数地址
xchg eax, ebp
pop edi
stosd
push edi
popad
cmp eax, 0x1e380a6a
jne find_lib_functions
function_call:
xor ebx, ebx
push ebx
push 0x74736577
push 0x6c696166
mov eax, esp
push ebx
push eax
push eax
push ebx
call [edi - 0x04]
push ebx
call [edi - 0x08]
nop
nop
nop
nop