• 逆向笔记——PE输出导出表


    1、如何定位导出表:

    数据目录项的第一个结构,就是导出表.							
    							
    typedef struct _IMAGE_DATA_DIRECTORY {							
        DWORD   VirtualAddress;							
        DWORD   Size;							
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;							
    							
    VirtualAddress  导出表的RVA							
    							
    Size 导出表大小  							
    
    DWORD pExportTbOffset = RVA2Offset(pNtHeaders, pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress); //注意这个VirtualAddress是VA
    pExportTable = (PIMAGE_EXPORT_DIRECTORY)(pExportTbOffset+fileBuffer);
    

    2、导出表结构

    上面的结构,只是说明导出表在哪里,有多大,并不是真正的导出表.							
    							
    如何在FileBuffer中找到这个结构呢?在VirtualAddress中存储的是RVA,如果想在FileBuffer中定位							
    							
    必须要先将该RVA转换成FOA.							
    							
    真正的导出表结构如下:							
    							
    							
    typedef struct _IMAGE_EXPORT_DIRECTORY {							
        DWORD   Characteristics;				// 未使用			
        DWORD   TimeDateStamp;				// 时间戳			
        WORD    MajorVersion;				// 未使用			
        WORD    MinorVersion;				// 未使用			
        DWORD   Name;				// 指向该导出表文件名字符串			
        DWORD   Base;				// 导出函数起始序号			
        DWORD   NumberOfFunctions;				// 所有导出函数的个数			
        DWORD   NumberOfNames;				// 以函数名字导出的函数个数			
        DWORD   AddressOfFunctions;     // 导出函数地址表RVA							
        DWORD   AddressOfNames;         // 导出函数名称表RVA							
        DWORD   AddressOfNameOrdinals;  // 导出函数序号表RVA							
    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;	
    
    	DWORD* pdwFunctionAddress = (DWORD*)(RVA2Offset(pNtHeaders, (DWORD)pExportTable->AddressOfNames)+(DWORD)fileBuffer);
    	WORD* pwOrdinals = (WORD*)(RVA2Offset(pNtHeaders, (DWORD)pExportTable->AddressOfNameOrdinals)+(DWORD)fileBuffer);
    	DWORD* pdwNamesAddress = (DWORD*)((DWORD)fileBuffer + RVA2Offset(pNtHeaders, pExportTable->AddressOfNames));
    
    	int i = 0;
    	for(i;i<(int)pExportTable->NumberOfNames;i++)
    	{
    
    		DWORD dwFunctionAddress = pdwFunctionAddress[pwOrdinals[i]];
            DWORD pdwFunNameOffset = (DWORD)fileBuffer + RVA2Offset(pNtHeaders, pdwNamesAddress[i]);
    
            printf("导出函数序号: %d , 函数名: %s  ,相对内存偏移: 0x%X
    ", pExportTable->Base + i, pdwFunNameOffset, dwFunctionAddress);
    	}
    

    代码

    
    #include <stdio.h>
    #include <windows.h>
    
    unsigned char* FileBuffer(const char* FileName);
    void ImportTable();
    unsigned char* RVAToFVA(unsigned char* fileBuffer,unsigned char* x);
    DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA);
    
    void main()
    {
    	ImportTable();
    }
    void ImportTable()
    {
    
    	PIMAGE_DOS_HEADER pDosHeader;
    	PIMAGE_NT_HEADERS pNtHeaders;
    	PIMAGE_EXPORT_DIRECTORY  pExportTable;
    
    	unsigned char* fileBuffer = FileBuffer("OllyDBG.EXE");
    	pDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
    	pNtHeaders = (PIMAGE_NT_HEADERS)(fileBuffer+pDosHeader->e_lfanew);
    
    	printf("导出表的相对虚拟地址:%x
    ",pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);
    	printf("导出表的大小:%x
    ",pNtHeaders->OptionalHeader.DataDirectory[0].Size);
    
    	DWORD pExportTbOffset = RVA2Offset(pNtHeaders, pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress); //注意这个VirtualAddress是VA
    	pExportTable = (PIMAGE_EXPORT_DIRECTORY)(pExportTbOffset+fileBuffer);
    	printf("函数地址:%x
    ",pExportTable->AddressOfFunctions);
    	printf("函数名序号地址:%x
    ",pExportTable->AddressOfNameOrdinals);
    	printf("函数名地址:%x
    ",pExportTable->AddressOfNames);
    	printf("函数序号基址:%x
    ",pExportTable->Base);
    	printf("特征值:%x
    ",pExportTable->Characteristics);
    	printf("函数名:%s
    ",RVA2Offset(pNtHeaders, pExportTable->Name)+fileBuffer); //指向导出表的文件字符串
    	printf("函数函数数量:%x
    ",pExportTable->NumberOfFunctions);
    	printf("函数名数量:%d
    ",pExportTable->NumberOfNames);
    
    	DWORD* pdwFunctionAddress = (DWORD*)(RVA2Offset(pNtHeaders, (DWORD)pExportTable->AddressOfNames)+(DWORD)fileBuffer);
    	WORD* pwOrdinals = (WORD*)(RVA2Offset(pNtHeaders, (DWORD)pExportTable->AddressOfNameOrdinals)+(DWORD)fileBuffer);
    	DWORD* pdwNamesAddress = (DWORD*)((DWORD)fileBuffer + RVA2Offset(pNtHeaders, pExportTable->AddressOfNames));
    
    	int i = 0;
    	for(i;i<(int)pExportTable->NumberOfNames;i++)
    	{
    
    		DWORD dwFunctionAddress = pdwFunctionAddress[pwOrdinals[i]];
            DWORD pdwFunNameOffset = (DWORD)fileBuffer + RVA2Offset(pNtHeaders, pdwNamesAddress[i]);
    
            printf("导出函数序号: %d , 函数名: %s  ,相对内存偏移: 0x%X
    ", pExportTable->Base + i, pdwFunNameOffset, dwFunctionAddress);
    	}
    
    }
    
    //将PE文件读到FileBuffer
    unsigned char* FileBuffer(const char* FileName)
    {
         unsigned char* Heap = NULL;
         FILE* Stream;
    
         //打开文件
         Stream = fopen(FileName,"rb");
         //计算文件大小
         fseek(Stream,0,SEEK_END);
         long FileSize = ftell(Stream);
         fseek(Stream,0,SEEK_SET);
    
         //分配堆空间
         Heap = (unsigned char*)malloc(sizeof(char)*FileSize);
         //将文件拷到堆
         fread(Heap,sizeof(char),FileSize,Stream);
         fclose(Stream);
    
    	 return Heap;
    }
    
    DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, DWORD dwRVA)
    {
        PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
    
        for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++)
        {
            if(dwRVA >= pSection[i].VirtualAddress && dwRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData))
            {
                return pSection[i].PointerToRawData + (dwRVA - pSection[i].VirtualAddress);
            }
        }
    
        return 0;
    }
    
    
  • 相关阅读:
    mysql去重
    java 实现一套流程管理、流转的思路(伪工作流)
    js模块加载框架 sea.js学习笔记
    使用js命名空间进行模块式开发
    二叉树的基本操作实现(数据结构实验)
    学生信息管理系统-顺序表&&链表(数据结构第一次作业)
    计算表达式的值--顺序栈(数据结构第二次实验)
    使用seek()方法报错:“io.UnsupportedOperation: can't do nonzero cur-relative seeks”错误的原因
    seek()方法的使用
    python中如何打印某月日历
  • 原文地址:https://www.cnblogs.com/Erma/p/12601828.html
Copyright © 2020-2023  润新知