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层的代码有时间完善一下再贴出来~

posted @ 2012-11-08 23:31  unixstudio  阅读(2286)  评论(0编辑  收藏  举报