雖然最後事情的結果很鬱悶,但中間的過程還是有些收穫的,
在編shellcode的時候,很重要的就是要找到要用的函數在記憶體中的位置,網上有很多寫好的模板,方法就
是通過peb和SEH找,找到的模板是用peb的。
主要目的就是找到kernel.dll在記憶體中的位置,進而找到GetProcAddress()的位置,有了這兩個,就可以
有LoAdLibrAry(),然後其他的函數地址問題就都解決了
FS:0是teb的位置,teb位移0x30的地方是peb
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA LoaderData;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PVOID FastPebLock;
PPEBLOCKROUTINE FastPebLockRoutine;
PPEBLOCKROUTINE FastPebUnlockRoutine;
ULONG EnvironmentUpdateCount;
PVOID *KernelCallbackTable;
PVOID EventLogSection;
PVOID EventLog;
PPEB_FREE_BLOCK FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PVOID *ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
BYTE Spare2[0x4];
LARGE_INTEGER CriticalSectionTimeout;
ULONG HeapSegmentReserve;
ULONG HeapSegmentCommit;
ULONG HeapDeCommitTotalFreeThreshold;
ULONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;
ULONG MaximumNumberOfHeaps;
PVOID **ProcessHeaps;
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
PVOID GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
ULONG OSBuildNumber;
ULONG OSPlatformId;
ULONG ImageSubSystem;
ULONG ImageSubSystemMajorVersion;
ULONG ImageSubSystemMinorVersion;
ULONG GdiHandleBuffer[0x22];
ULONG PostProcessInitRoutine;
ULONG TlsExpansionBitmap;
BYTE TlsExpansionBitmapBits[0x80];
ULONG SessionId;
} PEB, *PPEB;
PEB位移0x0c的地方就是PPEB_LDR_DATA LoaderData;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
其中有三個鏈表,每個元素都是結構
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
通過LIST_ENTRY把每個進程用到的MODULE串起來,每個MODULE就是可執行檔或dll在記憶體中的映像,通過這
個連可以把進程用到的dll全都走一遍,而shellcode需要的是kernel32.dll的,它總是位於鏈表InInitializa
tionOrderModuleList的第一個元素
mov eax, fs:0x30 // PEB
mov eax, [eax + 0x0c] // PROCESS_MODULE_INFO
mov esi, [eax + 0x1c] // InInitOrder.flink
lodsd
//得到kernel.dll所在的LDR_MODULE結構的InInitializationOrderModuleList地址
mov eax,[eax+8] //一個LIST_ENTRY8個位元組
現在的eAx中就是kernel.dll在記憶體映像的開始位置,也就是它的hAndle值
找到了kernel.dll,下面的部分就和PE檔案的格式有關了,
mov ebx,eax // 取kernel32.dll的起始地址
mov esi,dword ptr [ebx+0x3C] //u 在e_lfanew中得到pe heAder
mov esi,dword ptr [esi+ebx+0x78] //u export directory rvA
add esi,ebx
mov edi,dword ptr [esi+0x20] //u struct _IMAGE_EXPORT_DIRECTORY
//中AddressOfNames; // RVA from base of image
add edi,ebx
mov ecx,dword ptr [esi+0x14] //u AddressOfFunctions; // RVA from base of image
xor ebp,ebp
push esi
search_GetProcAddress:
push edi
push ecx
mov edi,dword ptr [edi]
add edi,ebx // 把輸出函數名表起始地址存人edi
mov esi,edx // 指令表起始地址存入esi
//mov ecx,0Eh // 函數getprocAddress長度為0Eh
push 0xE
pop ecx
repe cmps byte ptr [esi],byte ptr [edi]
je search_GetProcAddress_ok
pop ecx
pop edi
add edi,4
inc ebp
loop search_GetProcAddress
search_GetProcAddress_ok:
pop ecx
pop edi
pop esi
mov ecx,ebp
mov eax,dword ptr [esi+24h] //u AddressOfNameOrdinals; // RVA from base of image
add eax,ebx
shl ecx,1
add eax,ecx
xor ecx,ecx
mov cx,word ptr [eax]
mov eax,dword ptr [esi+1Ch] //AddressOfFunctions; // RVA from base of image
add eax,ebx
shl ecx,2
add eax,ecx
mov eax,dword ptr [eax]
add eax,ebx
在pe檔案中,最開始是IMAGE_DOS_HEADER結構
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
mov esi,dword ptr [ebx+0x3C]這一句就是得到LONG e_lfanew,esi指向pe頭,由於是相對與檔案開始
的位移,所以esi+ebx才是kernell.dll的pe頭在記憶體中的位置,再加0x78,就指向DAtADirectory,
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
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;
DAtADirectory第一個就是ExportDirectory,,加ebx就是在記憶體中的位置,每個Directory的開頭都是結構
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;
AddressOfNAmes和它後面的地址是一個指標數組,每個元素都指向一個函數名字串的rvA,用的時候可以這
樣
(char*)((*(int*)Addr)+bAseAddress)
AddressOfNAmeOrdinAls與AddressOfNAmes一一對應,是函數在AddressOfFunctions裡的序號
在調用這裡面的函數時,如果是用函數名在找的話,先在AddressOfNAmes找到對應的項,得到在這個指標
數組中的位置,用這個位置在AddressOfNAmeOrdinAls中索引出響應的元素,
mov ecx,ebp
mov eax,dword ptr [esi+24h] //u AddressOfNameOrdinals;
add eax,ebx
shl ecx,1
add eax,ecx
也就是這幾句的意思,AddressOfNAmeOrdinAls數組中每個元素大小都是WORD,所以shl ecx,1
xor ecx,ecx
mov cx,word ptr [eax]
得到cx用來在AddressOfFunctions;中索引,AddressOfFunctions中每一個元素的內容就是函數的rvA了,加上
kernell.dll的基址就得到在記憶體中地址了
add eax,ebx
shl ecx,1
add eax,ecx
xor ecx,ecx
mov cx,word ptr [eax]
mov eax,dword ptr [esi+1Ch] //AddressOfFunctions; // RVA from base of image
add eax,ebx
shl ecx,2
add eax,ecx
mov eax,dword ptr [eax]
add eax,ebx
得到GetProcAddress的地址後,用它可以得到LoAdLibrAry的地址,因為LoAdLibrAry也是kernel.dll裡匯出的
函數,,然後有了這兩個函數,,其他啥函數都不發愁了