《Windows PE》5.2 遍历导出表

发布于:2024-10-18 ⋅ 阅读:(42) ⋅ 点赞:(0)

为了将程序读到内存指定位置,本节我们将讨论如何使用两种不同的方法遍历导出表。此外,我们还将给出一个打印进程调用kernel32中的API信息的示例程序。

本节必须掌握的知识点:

        遍历导出表

        打印kernel32

5.2.1 遍历导出表

       ■方法一

实验三十四:遍历导出表方法一

以下代码实现打印进程导出表的函数信息(导出表一般只存在于dll中,OD除外)。

使用常规方法实现,将程序读到内存指定位置,进行磁盘与内存的转换。

/*------------------------------------------------------------------------

 FileName: GetExportInfo.c

 实验34:方法一,使用常规方法实现,将程序读到内存指定位置,进行磁盘与内存的转换。

 (c) bcdaren, 2024

-----------------------------------------------------------------------*/

#include <windows.h>

#include <stdio.h>

#include <dbghelp.h>

#pragma comment (lib,"dbghelp")

#define WIN64

DWORD RvaToFoa(PIMAGE_NT_HEADERS ntHeaders, DWORD rva) {

//ntHeaders+4+sizeof(IMAGE_FILE_HEADER)+FileHeader.SizeOfOptionalHeader(32或64位PE)

    PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(ntHeaders);

    WORD numberOfSections = ntHeaders->FileHeader.NumberOfSections;

    for (WORD i = 0; i < numberOfSections; i++) {

        DWORD sectionStartRva = sectionHeader->VirtualAddress;

        DWORD sectionEndRva = sectionStartRva + sectionHeader->SizeOfRawData;

        if (rva >= sectionStartRva && rva < sectionEndRva) {

            DWORD foa = sectionHeader->PointerToRawData +

(rva - sectionStartRva);

            return foa;

        }

        sectionHeader++;

    }

    return 0;  // RVA not found

}

void main()

{

#ifdef WIN64

    //LPSTR szFileName;

    HANDLE hFile;

    LPVOID lpvResult;

    DWORD dwPageSize;

    DWORD dwBytesRead = 0;

    BOOL bReadFile;

    PIMAGE_DOS_HEADER psImageDOSHeader;

    PIMAGE_NT_HEADERS64 psImageNTHeader;

    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory;

    DWORD* pdwAddrOfFunc;

    DWORD* pdwAddrOfName;

    WORD* pdwAddrOfNameOrd;

    DWORD* pdwTemp;

    TCHAR szFileName[] = TEXT("c:\\kernel32_64.dll");

    hFile = CreateFile(szFileName,     //打开文件    

        GENERIC_READ,

        FILE_SHARE_READ,

        NULL,

        OPEN_EXISTING,

        FILE_ATTRIBUTE_NORMAL,

        NULL);

    dwPageSize = GetFileSize(hFile, 0);

    lpvResult = VirtualAlloc(//给文件分配虚拟内存

        NULL,

        dwPageSize,

        MEM_COMMIT,

        PAGE_READWRITE);

//读入内存

    bReadFile = ReadFile(hFile, lpvResult, dwPageSize, &dwBytesRead, 0);

    psImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    psImageNTHeader = (PIMAGE_NT_HEADERS64)\

        ((BYTE*)lpvResult + psImageDOSHeader->e_lfanew);

    DWORD dwTemp = psImageNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    dwTemp = RvaToFoa(psImageNTHeader, (DWORD)dwTemp);

    pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(dwTemp + (ULONGLONG)lpvResult);

    pdwTemp = (DWORD*)(RvaToFoa(psImageNTHeader, pImageExportDirectory->AddressOfNames) + (ULONGLONG)lpvResult);

    pdwAddrOfFunc = (DWORD*)(RvaToFoa(psImageNTHeader, pImageExportDirectory->AddressOfFunctions) + (ULONGLONG)psImageDOSHeader);

    pdwAddrOfNameOrd = (WORD*)(RvaToFoa(psImageNTHeader,pImageExportDirectory->AddressOfNameOrdinals) + (ULONGLONG)psImageDOSHeader);

    printf("序号\t函数地址\t函数名\n");

    for (DWORD i = 0; i < pImageExportDirectory->NumberOfFunctions; i++)

    {

        pdwAddrOfName = (DWORD*)((DWORD)RvaToFoa(psImageNTHeader, *pdwTemp) +

(ULONGLONG)psImageDOSHeader);

        printf("0x%04x\t", *pdwAddrOfNameOrd);

        printf("0x%08x\t", *pdwAddrOfFunc);

        printf("%s\n", (char *)pdwAddrOfName);

        pdwTemp++;

        pdwAddrOfNameOrd++;

        pdwAddrOfFunc++;

    }

#else

    //LPSTR szFileName;

    HANDLE hFile;

    LPVOID lpvResult;

    DWORD dwPageSize;

    DWORD dwBytesRead = 0;

    BOOL bReadFile;

    PIMAGE_DOS_HEADER psImageDOSHeader;

    PIMAGE_NT_HEADERS32 psImageNTHeader;

    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory;

    DWORD* pdwAddrOfFunc;

    DWORD* pdwAddrOfName;

    WORD* pdwAddrOfNameOrd;

    DWORD* pdwTemp;

    TCHAR szFileName[] = TEXT("c:\\kernel32_32.dll");

    hFile = CreateFile(szFileName,     //打开文件    

        GENERIC_READ,

        FILE_SHARE_READ,

        NULL,

        OPEN_EXISTING,

        FILE_ATTRIBUTE_NORMAL,

        NULL);

    dwPageSize = GetFileSize(hFile, 0);

    lpvResult = VirtualAlloc(//给文件分配虚拟内存

        NULL,

        dwPageSize,

        MEM_COMMIT,

        PAGE_READWRITE);

//读入内存

    bReadFile = ReadFile(hFile, lpvResult, dwPageSize, &dwBytesRead, 0);

    psImageDOSHeader = (PIMAGE_DOS_HEADER)lpvResult;

    psImageNTHeader = (PIMAGE_NT_HEADERS32)\

        ((BYTE*)lpvResult + psImageDOSHeader->e_lfanew);

    DWORD dwTemp = psImageNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;

    dwTemp = RvaToFoa(psImageNTHeader, (DWORD)dwTemp);

    pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(dwTemp +

(DWORD)lpvResult);

    pdwTemp = (DWORD*)(RvaToFoa(psImageNTHeader, pImageExportDirectory->AddressOfNames) + (DWORD)lpvResult);

    pdwAddrOfFunc = (DWORD*)(RvaToFoa(psImageNTHeader, pImageExportDirectory->AddressOfFunctions) + (DWORD)psImageDOSHeader);

    pdwAddrOfNameOrd = (WORD*)(RvaToFoa(psImageNTHeader, pImageExportDirectory->AddressOfNameOrdinals) + (DWORD)psImageDOSHeader);

    printf("序号\t函数地址\t函数名\n");

    for (DWORD i = 0; i < pImageExportDirectory->NumberOfFunctions; i++)

    {

        pdwAddrOfName = (DWORD*)((DWORD)RvaToFoa(psImageNTHeader, *pdwTemp) +

(DWORD)psImageDOSHeader);

        printf("0x%04x\t", *pdwAddrOfNameOrd);

        printf("0x%08x\t", *pdwAddrOfFunc);

        printf("%s\n", (char *)pdwAddrOfName);

        pdwTemp++;

        pdwAddrOfNameOrd++;

        pdwAddrOfFunc++;

    }

#endif

    system("pause");

    return;

}

    运行:

序号      函数地址        函数名

0x0000  0x00001160  AnimateClose

0x0001  0x00001010  AnimateOpen

0x0002  0x00001280  FadeInOpen

0x0003  0x000013c0  FadeOutClose

请按任意键继续. . .

 

总结

       32位和64位PE文件导出表描述符是相同的,遍历导出表的区别只有两点,32位基址和64位基址,以及NT32位NT头和64位NT头的区别。

方法二

实验三十五:遍历导出表方法二

使用LoadLibrary将DLL程序读到内存指定位置,进行磁盘与内存的转换。

/*------------------------------------------------------------------------

 FileName: GetExportInfo.c

 实验35:方法二,使用常规方法实现,将程序读到内存指定位置,进行磁盘与内存的转换。

 (c) bcdaren, 2024

-----------------------------------------------------------------------*/

#include <windows.h>

#include <stdio.h>

//#define WIN64

void main()

{

#ifdef WIN64

    HANDLE hMoudle;

    DWORD dwBytesRead = 0;

    PIMAGE_DOS_HEADER psImageDOSHeader;

    PIMAGE_NT_HEADERS64 psImageNTHeader;

    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory;

    DWORD* pdwAddrOfFunc;

    DWORD* pdwAddrOfName;

    WORD* pdwAddrOfNameOrd;

    DWORD** ppdwTemp;

    //有依赖项,加载失败

    hMoudle = LoadLibrary(TEXT("C:\\winResult_64.dll"));

    psImageDOSHeader = (PIMAGE_DOS_HEADER)hMoudle;

    psImageNTHeader = (PIMAGE_NT_HEADERS64)((ULONGLONG)hMoudle + psImageDOSHeader->e_lfanew);

    pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)psImageNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (ULONGLONG)hMoudle);

    ppdwTemp = (DWORD**)(pImageExportDirectory->AddressOfNames + (ULONGLONG)psImageDOSHeader);

    pdwAddrOfFunc = (DWORD*)(pImageExportDirectory->AddressOfFunctions + (ULONGLONG)psImageDOSHeader);

    pdwAddrOfNameOrd = (WORD*)(pImageExportDirectory->AddressOfNameOrdinals + (ULONGLONG)psImageDOSHeader);

    printf("序号\t函数地址\t函数名\n");

    for (DWORD i = 0; i < pImageExportDirectory->NumberOfFunctions; i++)

    {

        pdwAddrOfName = (DWORD*)((DWORD)(*ppdwTemp) + (ULONGLONG)psImageDOSHeader);

        printf("0x%04x  ", *pdwAddrOfNameOrd);

        printf("0x%08x  ", *pdwAddrOfFunc);

        printf("%s\n", (char *)pdwAddrOfName);

        ((DWORD*)ppdwTemp)++;//改为32位地址

        pdwAddrOfNameOrd++;

        pdwAddrOfFunc++;

    }

#else

    HANDLE hMoudle;

    DWORD dwBytesRead = 0;

    PIMAGE_DOS_HEADER psImageDOSHeader;

    PIMAGE_NT_HEADERS32 psImageNTHeader;

    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory;

    DWORD* pdwAddrOfFunc;

    DWORD* pdwAddrOfName;

    WORD* pdwAddrOfNameOrd;

    DWORD** ppdwTemp;

    hMoudle = LoadLibrary(TEXT("C:\\Windows\\System32\\kernel32.dll"));//不可以在"C:\"

    psImageDOSHeader = (PIMAGE_DOS_HEADER)hMoudle;

    psImageNTHeader = (PIMAGE_NT_HEADERS32)((DWORD)hMoudle + psImageDOSHeader->e_lfanew);

    pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)psImageNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (DWORD)hMoudle);

    ppdwTemp = (DWORD**)(pImageExportDirectory->AddressOfNames + (DWORD)psImageDOSHeader);

    pdwAddrOfFunc = (DWORD*)(pImageExportDirectory->AddressOfFunctions + (DWORD)psImageDOSHeader);

    pdwAddrOfNameOrd = (WORD*)(pImageExportDirectory->AddressOfNameOrdinals + (DWORD)psImageDOSHeader);

   

    printf("序号\t函数地址\t函数名\n");

    for (DWORD i = 0; i < pImageExportDirectory->NumberOfFunctions; i++)

    {

        pdwAddrOfName = (DWORD*)((DWORD)(*ppdwTemp) + (DWORD)psImageDOSHeader);

        printf("0x%04x  ", *pdwAddrOfNameOrd);

        printf("0x%08x  ", *pdwAddrOfFunc);

        printf("%s\n", (char *)pdwAddrOfName);

        ppdwTemp++;

        pdwAddrOfNameOrd++;

        pdwAddrOfFunc++;

    }

#endif

    system("pause");

    return;

}

 

注意

关于IMAGE_DATA_DIRECTORY的成员Size指定了VirtualAddress指向结构体的的大小,没有实际含义,因为目标结构体内同样包含结构体指针变量,该指针可以指向任何位置(包括入口点或自身)。所以将Size修改为0,不会影响pe文件的执行。

5.2.2 打印进程模块列表

实验三十六:打印进程模块列表

以下示例打开指定进程,枚举并打印进程中的所有模块信息,包括模块名称、基址和大小。

/*------------------------------------------------------------------------

 FileName:EnumerateProcessModules.c

 实验36:打印指定进程模块列表。

 (c) bcdaren, 2024

-----------------------------------------------------------------------*/

#include <Windows.h>

#include <stdio.h>

#include <psapi.h>

#include <tchar.h>

void EnumerateProcessModules(HANDLE hProcess) {

    HMODULE hModules[1024];

    DWORD cbNeeded;

    unsigned int i;

    if (EnumProcessModulesEx(hProcess, hModules, sizeof(hModules), &cbNeeded, LIST_MODULES_ALL)) {

        for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {

            TCHAR szModuleName[MAX_PATH];

            FARPROC pEntryPoint;

            // 获取模块名称

            if (GetModuleFileNameEx(hProcess, hModules[i], szModuleName, sizeof(szModuleName) / sizeof(TCHAR))) {

                _tprintf(_T("Module Name: %s\n"), szModuleName);

            }

            // 获取模块基址和大小

            MODULEINFO moduleInfo;

            if (GetModuleInformation(hProcess, hModules[i], &moduleInfo, sizeof(MODULEINFO))) {

                _tprintf(_T("Base Address: 0x%p\n"), moduleInfo.lpBaseOfDll);

                _tprintf(_T("Module Size: 0x%x\n"), moduleInfo.SizeOfImage);

            }

            _tprintf(_T("\n"));

        }

    }

}

int main() {

    //HANDLE hProcess = GetCurrentProcess();

    DWORD processId;

    HANDLE hProcess;

    // 输入需要打开的进程 ID

    printf("Enter the process ID to open: ");

    scanf_s("%u", &processId);

    // 打开进程

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

    if (hProcess == NULL) {

        printf("Failed to open the process. Error code: %u\n", GetLastError());

        return 1;

    }

    EnumerateProcessModules(hProcess);

    CloseHandle(hProcess);

    return 0;

}

运行:

Enter the process ID to open: 11864

Module Name: D:\code\winpe\notepad32.exe

Base Address: 0x01000000

Module Size: 0x13000

Module Name: C:\Windows\SYSTEM32\ntdll.dll

Base Address: 0x773C0000

Module Size: 0x18c000

Module Name: C:\Windows\System32\KERNEL32.DLL

Base Address: 0x75520000

Module Size: 0xd0000

Module Name: C:\Windows\System32\KERNELBASE.dll

Base Address: 0x749E0000

Module Size: 0x1d8000

Module Name: C:\Windows\System32\comdlg32.dll

 

总结

这段代码使用了 EnumProcessModules 函数遍历进程中的模块,并使用 GetModuleInformation 函数获取模块的基址。然后,它打印出每个模块的名称和基址。

请确保在编译时链接 psapi.lib 库,以便使用 EnumProcessModules 和 GetModuleInformation 函数。   

PE文件在内存中格式如下图:

图5-6 PE文件中内存中的格式


今日签到

点亮在社区的每一天
去签到