zoukankan      html  css  js  c++  java
  • 导入表编程-枚举导入表

    导入表编程-枚举导入表

    思路:

    首先导入表的RVA地址,就在optional HeaderDataDirectory的第二个元素中。通过它我们定位到导入表。

        导入表类似一个二级索引。一级是一个模块目录(IMAGE_IMPORT_DESCRIPTOR数组,这里把目录理解为一个以全0字节为结束的数组),它的每个元素代表了一个DLL。二级是导入地址表IAT(即 IMAGE_THUNK_DATA 数组,一个指针数组),每个元素指向一个 IMAGE_IMPORT_BY_NAME结构(该结构含有一个函数序号和一个函数名称字符串)。

        总结一下,我们的定位过程:

        (1)通过 NtHeaders.OptionalHeader.DataDirectory[1].VirtualBase --> 定位到导入表(IID Table)

        (2)遍历每个 IID,直到遇到全0为止。

            通过 IID.Name -> 定位到 DLL 名字。

            通过 IID.OriginalFirstThunk 或者 FirstThunk -> 定位到IAT ( image_thunk_data32[] )

              遍历指针目录,知道遇到NULL为止。

                通过 thunk_data.AddressOfData -> 定位到一个 IMAGE_IMPORT_BY_NAME 的地址,再根据它寻址到真正的函数序号和函数名称。

     

    代码:

    // C++Test.cpp : 定义控制台应用程序的入口点。
    //
     
    #include "stdafx.h"
    #include "stdafx.h"
    #include <stdio.h>
    #include <windows.h>
    #include <Dbghelp.h> //ImageRvaToVa
    #pragma comment(lib, "Dbghelp.lib")
     
    int main(int argc, char* argv[]){
    int i, j;
    HANDLE hFile = CreateFile(L"C:\A.exe", //PE文件名
    GENERIC_READ, 
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    NULL);
     
    if(hFile == INVALID_HANDLE_VALUE){
    printf("Create File Failed.
    ");
    return 0;
    }
     
    HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY,    0, 0, NULL);
     
    if (hFileMapping == NULL || hFileMapping == INVALID_HANDLE_VALUE) { 
    printf("Could not create file mapping object (%d).
    ", GetLastError());
    return 0;
    }
     
    LPBYTE lpBaseAddress = (LPBYTE)MapViewOfFile(hFileMapping,   // handle to map object
    FILE_MAP_READ, 0, 0, 0);
     
    if (lpBaseAddress == NULL) { 
    printf("Could not map view of file (%d).
    ", GetLastError()); 
    return 0;
    }
     
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBaseAddress;
    PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(lpBaseAddress + pDosHeader->e_lfanew);
     
    //导入表的rva:0x2a000;
    DWORD Rva_import_table = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
     
    if(Rva_import_table == 0){
    printf("no import table!");
    goto UNMAP_AND_EXIT;
    }
     
    //这个虽然是内存地址,但是减去文件开头的地址,就是文件地址了
    //这个地址可以直接从里面读取你想要的东西了
     	PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(
    pNtHeaders, 
    lpBaseAddress, 
    Rva_import_table,
    NULL
    );
     
    //减去内存映射的首地址,就是文件地址了。。(很简单吧)
    printf("FileAddress Of ImportTable: %p
    ", ((DWORD)pImportTable - (DWORD)lpBaseAddress));
     
    //现在来到了导入表的面前:IMAGE_IMPORT_DESCRIPTOR 数组(以0元素为终止)
    //定义表示数组结尾的null元素!
    IMAGE_IMPORT_DESCRIPTOR null_iid;
    IMAGE_THUNK_DATA null_thunk;
    memset(&null_iid, 0, sizeof(null_iid));
    memset(&null_thunk, 0, sizeof(null_thunk));
     
    //每个元素代表了一个引入的DLL。
    for(i=0; memcmp(pImportTable + i, &null_iid, sizeof(null_iid))!=0; i++){
    //LPCSTR: 就是 const char*
    LPCSTR szDllName = (LPCSTR)ImageRvaToVa(
    pNtHeaders, lpBaseAddress, 
    pImportTable[i].Name, //DLL名称的RVA
    NULL);
     
    //拿到了DLL的名字
    printf("-----------------------------------------
    ");
    printf("[%d]: %s
    ", i, szDllName);
    printf("-----------------------------------------
    ");
     
    //现在去看看从该DLL中引入了哪些函数
    //我们来到该DLL的 IMAGE_TRUNK_DATA 数组(IAT:导入地址表)前面
    PIMAGE_THUNK_DATA32 pThunk = (PIMAGE_THUNK_DATA32)ImageRvaToVa(
    pNtHeaders, lpBaseAddress,
    pImportTable[i].OriginalFirstThunk, //【注意】这里使用的是OriginalFirstThunk
    NULL);
     
    for(j=0; memcmp(pThunk+j, &null_thunk, sizeof(null_thunk))!=0; j++){
    //这里通过RVA的最高位判断函数的导入方式,
    //如果最高位为1,按序号导入,否则按名称导入
    if(pThunk[j].u1.AddressOfData & IMAGE_ORDINAL_FLAG32){
    printf("	 [%d] 	 %ld 	 按序号导入
    ", j, pThunk[j].u1.AddressOfData & 0xffff);
    }else{
    //按名称导入,我们再次定向到函数序号和名称
    //注意其地址不能直接用,因为仍然是RVA!
    PIMAGE_IMPORT_BY_NAME pFuncName = (PIMAGE_IMPORT_BY_NAME)ImageRvaToVa(
    pNtHeaders,    lpBaseAddress,
    pThunk[j].u1.AddressOfData,
    NULL);
     
    printf("	 [%d] 	 %ld 	 %s
    ", j, pFuncName->Hint, pFuncName->Name);
    }
    }
    }
     
     
    UNMAP_AND_EXIT:
    //关闭文件,句柄。。
    UnmapViewOfFile(lpBaseAddress);
    CloseHandle(hFileMapping);
    CloseHandle(hFile);
    getchar();
    return 0;
    }


    【注意】在上面的代码中使用的是 OriginalFirstThunk ,对于没有事先绑定的PE文件来说, OriginalFirstThunk 和 FirstThunk 是并行的内容相同的数组。但是对于已经经过绑定的PE文件来说, FirstThunk 数组中的元素会被设置成真正的函数地址(VA)!因此如果这时用 FirstThunk 数组尝试获取函数名称是得不到的。所以上面的代码应该使用 OriginalFirstThunk ,这样无论对于绑定还是未绑定的PE文件,都能够定向到相应的函数名称。

     

    运行结果:


    对应源文件信息:

     


     

     

  • 相关阅读:
    grep命令、sed 命令 、awk命令、uniq命令
    正则表达式学习(python之re模块)
    linux目录结构
    shell之find命令详解
    shell之 ps、kill、killall命令详解
    linux用户及权限
    C#中数据类型char*,const char*和string的三者转换
    C#中通过SendARP读取MAC地址
    C#对字典Dictionary 的添加,遍历,移除系列操作
    CMD命令行实现复制一张图片1000份:
  • 原文地址:https://www.cnblogs.com/csnd/p/12062214.html
Copyright © 2011-2022 走看看