HOOK IAT RING3
IAT(Import Address Table,输入地址表):当PE文件装入的时候,Windows加载器的工作之一就是定位所有被输入的函数和数据,并且让正在被装入的文件可以使用那些地址,输入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于相关的dll等文件中,当然只有被程序调用到的函数才会出现在IAT中(EAT是PE中所有导出的函数或者变量,注意区别,一般的EXE文件不会有导出表,但并不是说EXE不能导出函数或者变量)
所谓的HOOK IAT就是钩住感兴趣的函数,然后改变程序的执行流程或者对该函数进行监控
PE结构的输入表以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)的数组开始,每个被PE文件隐式加载进来的dll都有一个IID,在这个数组中并没有字段指出该结构数组的项数,但它的最后一个单元是NULL,可以由此计算出该数组的项数,IID结构如下
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) } DUMMYUNIONNAME; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
- OriginalFirstThunk:包含指向输入名称表(INT,Import Name Table)的RVA,INT是一个IMAGE_THUNK_DATA结构的数组,数组中的每个IMAGE_THUNK_DATA结构指向IMAGE_IMPORT_BY_NAME结构,数组最后一个以内容为0的IMAGE_THUNK_DATA结构结束
- Name:DLL名字的指针,是个以00结尾的ASCII字符的RVA地址,该字符串包含输入的dll名
- FirstThunk:包含指向输入地址表(IAT)的RVA,IAT是一个IMAGE_THUNK_DATA结构的数组
IMAGE_THUNK_DATA结构如下所示
typedef struct _IMAGE_THUNK_DATA { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA; typedef IMAGE_THUNK_DATA * PIMAGE_THUNK_DATA;
IMAGE_THUNK_DATA中的u1->AddressOfData得到的是IMAGE_IMPORT_BY_NAME的指针,IMAGE_IMPORT_BY_NAME结构如下
typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; CHAR Name[1]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
其中的Name成员是函数名相对于程序的加载地址的RVA
因此查找感兴趣的导入函数的地址的方法分为两步
- 搜索IID数组中的Name成员与导入函数所在的模块相同的IID,比如要HOOK MessageBoxA的话就需要找到Name为user32.dll的IID项
- 利用IID数组中的两个平行指针OriginalFirstThunk和FirstThunk----〉IMAGE_THUNK_DATA32----〉IMAGE_IMPORT_BY_NAME---->Name,匹配要HOOK的函数名
这样通过查找函数过程中的偏移Offset来得到存放函数地址的内存所在的IMAGE_THUNK_DATA,最后修改一下其中的AddressOfData即可,从而调用我们预先设好函数
IAT HOOK可分别应用于ring0和ring3,其中要特别注意进程空间的隔离,在ring0中的话KeStackAttachProcess到感兴趣的进程中或者调用PsSetLoadImageNotifyRoutine设置一个回调函数当有进程被加载的时候修改IAT,而在ring3中的如果要HOOK其他进程的IAT的话就比较的麻烦,需要通过ReadProcessMemory和WriteProcessMemory来进行操作
这里先贴出ring3的完整代码吧,测试环境为WINXPSP3,其中用到了一个比较新颖的想法就是加入了shellcode的东西,HOOK的是calc.exe(windows自带计算器)的GetClipboardData,当ctrl+v粘贴的时候弹出一个空的MessageBox窗口(看似简单不过里面的涉及到的东西挺多的),程序的流程如下
- 找到calc.exe进程的PEB->ImageBase(该程序运行之前calc.exe要事先启动)
- 定位user32.dll中GetClipboardData函数地址的存放地址
- 更改该地址处的内容(此处是一个RVA怎么更改请看代码),使其转向我们的shellcode
那么这段shellcode放在哪?我们知道PE结构中有好多区块,比如.text(默认的代码区),.data(默认的数据区)等等,当区块被加载到内存的时候,区块总是至少以一个页边界处开始,在x86系列的CPU中,页是按照4KB的字节来排列的,这样区块与页边界之间肯定就有空闲的间隙,我们可以利用这点将shellcode放到这些间隙里面
利用shellcode的好处是一些反IAT HOOK检测工具可以通过定位被HOOK函数的转向地址来确定可疑的模块,如果我们把shellcode写入被HOOK的进程里面,那么这个可疑模块就是程序本身,这样就能bypass掉这些工具
好了,废话不多说,下面贴出代码
#include <windows.h> #include <stdio.h> #include <malloc.h> #include <TlHelp32.h> #define BUFFERSIZE 0x1000 //max IDD num is 4096/20=204 #define MAXNAMELEN 0x100 #define MAXTUNKSIZE 0x400 //that's to say the max number of fucntion is 0x100 #define RVATOVA(IMAGEBASE,RVA) ((ULONG_PTR)(IMAGEBASE)+(ULONG_PTR)(RVA)) typedef __success(return>=0) LONG NTSTATUS; #define NT_SUCCESS(status) (((NTSTATUS)(status))>=0) #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) typedef NTSTATUS (__stdcall *NTQUERYINFORMATIONPROCESS)(HANDLE,ULONG,PVOID,ULONG,PULONG); typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; ULONG_PTR PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID Reserved3; } PROCESS_BASIC_INFORMATION,*PPROCESS_BASIC_INFORMATION; typedef struct _PEB{ PVOID Reserved1[2]; ULONG_PTR ImageBase; }PEB,*PPEB; NTQUERYINFORMATIONPROCESS NtQueryInformationProcess; BYTE Buffer[BUFFERSIZE]; //thie shellcode only pop up a messagebox unsigned char shellcode[]= "\x64\x8b\x3d\x30\x00\x00\x00\x8b" "\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08" "\x8b\x77\x20\x8b\x3f\x80\x7e\x0c" "\x33\x75\xf2\x8b\xf8\x03\x78\x3c" "\x8b\x57\x78\x03\xd0\x8b\x7a\x20" "\x03\xf8\x33\xc9\x8b\x34\x8f\x03" "\xf0\x41\x81\x3e\x47\x65\x74\x50" "\x75\xf2\x81\x7e\x04\x72\x6f\x63" "\x41\x75\xe9\x8b\x7a\x1c\x03\xf8" "\x49\x8b\x3c\x8f\x8b\xd8\x03\xc7" "\x8b\xc8\x51\x6a\x00\x68\x61\x72" "\x79\x41\x68\x4c\x69\x62\x72\x68" "\x4c\x6f\x61\x64\x54\x53\xff\xd1" "\x83\xc4\x10\x59\x51\x68\x6c\x6c" "\x00\x00\x68\x33\x32\x2e\x64\x68" "\x75\x73\x65\x72\x54\xff\xd0\x83" "\xc4\x0c\x59\x8b\xd8\x68\x6f\x78" "\x41\x00\x68\x61\x67\x65\x42\x68" "\x4d\x65\x73\x73\x54\x53\xff\xd1" "\x83\xc4\x0c\x6a\x00\x6a\x00\x6a" "\x00\x6a\x00\xff\xd0\xc3"; ULONG_PTR GetRemotePEB(HANDLE RemoteProcess) { HMODULE hNTDLL=LoadLibraryW(L"ntdll.dll"); if (!hNTDLL) { printf("%s:LoadLibraryExW Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } NtQueryInformationProcess=(NTQUERYINFORMATIONPROCESS)GetProcAddress(hNTDLL,"NtQueryInformationProcess"); if (!NtQueryInformationProcess) { printf("%s:GetProcAddress Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PROCESS_BASIC_INFORMATION *pBasicInfo=(PPROCESS_BASIC_INFORMATION)malloc(sizeof(PROCESS_BASIC_INFORMATION)); if (!pBasicInfo) { printf("%s:malloc Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } NTSTATUS status=STATUS_UNSUCCESSFUL; status=NtQueryInformationProcess(RemoteProcess,0,pBasicInfo,sizeof(PROCESS_BASIC_INFORMATION),NULL); if (!NT_SUCCESS(status)) { printf("%s:NtQueryInformationProcess Failed with error code=0x%08x\n",__FUNCTION__,status); return 0; } ULONG_PTR Peb=pBasicInfo->PebBaseAddress; free(pBasicInfo); return Peb; } ULONG_PTR GetRemoteImageBase(HANDLE RemoteProcess) { ULONG_PTR Peb=GetRemotePEB(RemoteProcess); BOOL bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)Peb,Buffer,sizeof(PEB),NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PPEB pPEB=(PPEB)Buffer; return pPEB->ImageBase; } ULONG_PTR GetRemoteNtHeader(HANDLE RemoteProcess) { ULONG_PTR ImageBase=GetRemoteImageBase(RemoteProcess); BOOL bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)ImageBase,Buffer,sizeof(IMAGE_DOS_HEADER),NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PIMAGE_DOS_HEADER pDosHeader=(PIMAGE_DOS_HEADER)Buffer; return ImageBase+pDosHeader->e_lfanew; } DWORD GetCalcProcessId() { HANDLE hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if (hProcessSnap==INVALID_HANDLE_VALUE) { printf("%s:CreateToolhelp32Snapshot Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PROCESSENTRY32 pe32; pe32.dwSize=sizeof(PROCESSENTRY32); if (!Process32First(hProcessSnap,&pe32)) { printf("%s:Process32First Failed with error code=%u\n",__FUNCTION__,GetLastError()); CloseHandle(hProcessSnap); return 0; } do { if (!_wcsicmp(pe32.szExeFile,L"calc.exe")) { CloseHandle(hProcessSnap); return pe32.th32ProcessID; } } while (Process32Next(hProcessSnap,&pe32)); CloseHandle(hProcessSnap); return 0; } ULONG_PTR GetFunctionMemoryInRemoteIAT(HANDLE RemoteProcess,CHAR* pModuleName,CHAR* pFunctionName) { ULONG_PTR ImageBase=GetRemoteImageBase(RemoteProcess); ULONG_PTR NtHeader=GetRemoteNtHeader(RemoteProcess); BOOL bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)NtHeader,Buffer,sizeof(IMAGE_NT_HEADERS),NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)Buffer; ULONG_PTR DesEntry=ImageBase+pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; DWORD DesSize=pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size; bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)DesEntry,Buffer,DesSize,NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PIMAGE_IMPORT_DESCRIPTOR pDesEntry=(PIMAGE_IMPORT_DESCRIPTOR)Buffer; BYTE ModuleName[MAXNAMELEN]; while(pDesEntry->Name) { bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)(RVATOVA(ImageBase,pDesEntry->Name)),ModuleName,strlen(pModuleName)+1,NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } if(!_stricmp(pModuleName,(CHAR*)ModuleName)) { printf("Module Find!\n"); PIMAGE_THUNK_DATA OriginalFirstTunk=(PIMAGE_THUNK_DATA)(RVATOVA(ImageBase,pDesEntry->OriginalFirstThunk)); PIMAGE_THUNK_DATA FirstTunk=(PIMAGE_THUNK_DATA)(RVATOVA(ImageBase,pDesEntry->FirstThunk)); BYTE OriginalFistTunkData[MAXTUNKSIZE]; BYTE FirstTunkData[MAXTUNKSIZE]; BYTE ImportByName[MAXNAMELEN+sizeof(WORD)]; bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)OriginalFirstTunk,OriginalFistTunkData,MAXTUNKSIZE,NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } OriginalFirstTunk=(PIMAGE_THUNK_DATA)OriginalFistTunkData; bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)FirstTunk,FirstTunkData,MAXTUNKSIZE,NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } FirstTunk=(PIMAGE_THUNK_DATA)FirstTunkData; DWORD dwOffset=0; while (OriginalFirstTunk->u1.AddressOfData) { bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)(RVATOVA(ImageBase,OriginalFirstTunk->u1.AddressOfData)),&ImportByName,strlen(pFunctionName)+1+sizeof(WORD),NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } if (!_stricmp(pFunctionName,(CHAR*)(ImportByName+sizeof(WORD)))) { printf("function find!\n"); printf("address of %s=0x%08x\n,mem=0x%08x\n",pFunctionName,FirstTunk->u1.Function,FirstTunk); return ImageBase+pDesEntry->FirstThunk+dwOffset*sizeof(IMAGE_THUNK_DATA); } dwOffset++; OriginalFirstTunk++; FirstTunk++; } return 0; } pDesEntry++; } return 0; } ULONG_PTR GetInjectHandlerAddr(HANDLE RemoteProcess,DWORD dwHandleSize) { ULONG_PTR ImageBase=GetRemoteImageBase(RemoteProcess); ULONG_PTR NtHeader=GetRemoteNtHeader(RemoteProcess); BOOL bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)NtHeader,Buffer,sizeof(IMAGE_NT_HEADERS),NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PIMAGE_NT_HEADERS pNtHeader=(PIMAGE_NT_HEADERS)Buffer; DWORD NumberOfSections=pNtHeader->FileHeader.NumberOfSections; ULONG_PTR ImageSectionHeader=NtHeader+sizeof(IMAGE_NT_HEADERS); bSuccess=ReadProcessMemory(RemoteProcess,(LPCVOID)ImageSectionHeader,Buffer,sizeof(IMAGE_SECTION_HEADER)*NumberOfSections,NULL); if (!bSuccess) { printf("%s:ReadProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } PIMAGE_SECTION_HEADER pSectionHeader=(PIMAGE_SECTION_HEADER)Buffer; for (DWORD i=0;i<NumberOfSections;i++) { if (!_stricmp((char*)pSectionHeader->Name,".text")) { printf("find .text block!\n"); break; } pSectionHeader++; } return ImageBase+pSectionHeader->VirtualAddress+pSectionHeader->SizeOfRawData-dwHandleSize; } BOOL InjectCodeAndModifyIAT(HANDLE RemoteProcess,ULONG_PTR FunctionMemory,ULONG_PTR HandlerAddr) { //Modify IAT BOOL bSuccess=WriteProcessMemory(RemoteProcess,(LPVOID)FunctionMemory,(LPCVOID)&HandlerAddr,sizeof(HandlerAddr),NULL); if (!bSuccess) { printf("%s:WriteProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return FALSE; } //Inject shellcode into .text block bSuccess=WriteProcessMemory(RemoteProcess,(LPVOID)HandlerAddr,(LPCVOID)shellcode,sizeof(shellcode),NULL); if (!bSuccess) { printf("%s:WriteProcessMemory Failed with error code=%u\n",__FUNCTION__,GetLastError()); return FALSE; } return TRUE; } int main() { DWORD ProcessId=GetCalcProcessId(); HANDLE RemoteProcess=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,FALSE,ProcessId); if (!RemoteProcess) { printf("%s:OpenProcess Failed with error code=%u\n",__FUNCTION__,GetLastError()); return 0; } DWORD addrMemory=GetFunctionMemoryInRemoteIAT(RemoteProcess,"user32.dll","GetClipboardData"); printf("GetFunctionMemoryInRemoteIAT addr=0x%08x\n",addrMemory); DWORD HandlerSize=sizeof(shellcode); DWORD HandlerAddr=GetInjectHandlerAddr(RemoteProcess,HandlerSize); printf("HandlerAddr=0x%08x\n",HandlerAddr); if (!InjectCodeAndModifyIAT(RemoteProcess,addrMemory,HandlerAddr)) printf("%s:InjectCode Failed with error code=%u\n",__FUNCTION__,GetLastError()); else printf("Inject Success!\n"); return 0; }
后面有时间的话应该会写一篇shellcode的入门文章,仅仅是入门,因为我也是菜鸟。。。
至于ring0层的代码有时间完善一下再贴出来~