遍历导出表(上课代码)

// 01 遍历导出表.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"

//************************************
// Method:    IsPeFile
// FullName:  IsPeFile
// Access:    public
// Returns:   bool   成功失败
// Qualifier:
// Parameter: TCHAR * szPath  路径
//************************************
bool  IsPeFile(TCHAR* szPath)
{
    BOOL bSuccess = TRUE;
    //1 将PE文件读取到内存
    HANDLE hFile = CreateFile(
        szPath,
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL
        , NULL
        );
    DWORD dwSize = GetFileSize(hFile, NULL);
    DWORD dwRubbish = 0;
    unsigned char * pBuf = new unsigned char[dwSize];
    ReadFile(hFile, pBuf, dwSize, &dwRubbish, NULL);
    //2 判断是否是PE文件
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
    if (pDos->e_magic != IMAGE_DOS_SIGNATURE)
    {
        bSuccess = FALSE;
        goto Error;

    }
    PIMAGE_NT_HEADERS  pNt = (PIMAGE_NT_HEADERS)(pBuf + pDos->e_lfanew);
    if (pNt->Signature != IMAGE_NT_SIGNATURE)
    {
        bSuccess = FALSE;
        goto Error;
    }

Error:

    if (pBuf != NULL)
    {
        delete[]pBuf;
    }
    if (hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFile);
    }
    return bSuccess;
}

//************************************
// Method:    RvaToOffect
// FullName:  RvaToOffect
// Access:    public
// Returns:   DWORD
// Qualifier: 将RVA转换为Offect
// Parameter: DWORD rva    要转换的RVA
// Parameter: unsigned char * pFile   存储pe文件内容的缓冲区
//************************************
DWORD RvaToOffect(DWORD rva, unsigned char* pFile)
{
    //1 找到NT头
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFile;
    PIMAGE_NT_HEADERS  pNt = (PIMAGE_NT_HEADERS)(pFile + pDos->e_lfanew);
    //2 找到数据目录表
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
    //3 判断要转换的位置是不是PE头部
    if (rva < pSection->VirtualAddress)
    {
        return rva;
    }
    //4 在数据目录表中遍历,进行计算
    for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
        if (
            (rva >= pSection->VirtualAddress) &&
            (rva <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
            )
        {
            return rva - pSection->VirtualAddress + pSection->PointerToRawData;
        }
        pSection++;
    }
    return -1;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //1 将PE文件读取到内存
    HANDLE hFile = CreateFile(
        L"D:\\user32.dll",
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL
        , NULL
        );
    DWORD dwSize = GetFileSize(hFile, NULL);
    DWORD dwRubbish = 0;
    unsigned char * pBuf = new unsigned char[dwSize];
    ReadFile(hFile, pBuf, dwSize, &dwRubbish, NULL);
    //2 找到dos头
    PIMAGE_DOS_HEADER  pDos = PIMAGE_DOS_HEADER(pBuf);
    //3 找到nt头
    PIMAGE_NT_HEADERS pNt = PIMAGE_NT_HEADERS(pBuf + pDos->e_lfanew);
    //4 找到扩展头
    PIMAGE_OPTIONAL_HEADER pOption = &(pNt->OptionalHeader);
    //5 找到数据目录表
    PIMAGE_DATA_DIRECTORY  pDataDirectory = pOption->DataDirectory;
    //6 找到导出表的数据目录
    PIMAGE_DATA_DIRECTORY pExportDirectory = (pDataDirectory +0);

    //7 解析导出表的数据目录
    //7.1 得到导出表的文件偏移
    DWORD dwExOffect  = RvaToOffect(pExportDirectory->VirtualAddress, pBuf);
    //7.2 得到导出表结构体
    PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(pBuf + dwExOffect);
    //7.3打印dll的名字,注意:并不能直接打印,它提供的只是一个名字的RVA偏移
    char* pName = (char*)(RvaToOffect(pExport->Name, pBuf) + pBuf);
    printf("%s\n", pName);
    //8 为解析导出表做准备
    //8.1 函数的个数
    DWORD dwNumOfFun =  pExport->NumberOfFunctions;
    //8.2 名称的个数
    DWORD dwNumOfName =  pExport->NumberOfNames;
    //8.3 函数地址表的位置
    PDWORD pOffectOfFun = (PDWORD)
        (RvaToOffect(pExport->AddressOfFunctions, pBuf) + pBuf);
    //8.4序号表的位置
    PWORD  pOrder = (PWORD)
        (RvaToOffect(pExport->AddressOfNameOrdinals, pBuf) + pBuf);
    //8.5 名称表的位置
    PDWORD pOffectOfName = (PDWORD)
    (RvaToOffect(pExport->AddressOfNames,pBuf) + pBuf);
    //8.6 序号基数
    WORD wBase = pExport->Base;
    //9 开始解析导出表
    for (int i = 0; i < dwNumOfFun;i++){
        //9.1 假如这是一个无效地址
        if (pOffectOfFun[i] == 0)
            continue;
        //9.2 不是无效地址,就去序号表中找到这个序号
        int j = 0;
        for (; j < dwNumOfName; j++){
            if (pOrder[j] == i){
                //9.2.1找到了这个序号,说明这个函数有名字,属于名称导出
                char* pNameOfFun = (char*)(RvaToOffect(pOffectOfName[j], pBuf) + pBuf);
                printf(" 函数序号为:%hx 函数地址为:%X  函数名为:%s\n",
                    wBase+i,
                    pOffectOfFun[i], pNameOfFun);
                break;
            }
        }
        if (j == dwNumOfName){
            //9.2.2假如没有找到这个序号,说明这个函数没有名字,只有序号,
            //属于序号导出,这个序号叫做虚序号
            printf(" 函数序号为:%hx 函数地址为:%X 函数名为:NULL\n",
                wBase+i,
                pOffectOfFun[i]);
        }
    }
    return 0;
}
时间: 2024-08-11 09:47:53

遍历导出表(上课代码)的相关文章

编程算法 - 中序遍历 递归/迭代 代码(C)

中序遍历 递归/迭代 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 中序遍历(InOrder)作为二叉搜索树的排序方式, 有着重要的作用. 递归和迭代的方法都需要掌握, 迭代主要使用了栈(stack)进行输入输出. 代码: /* * main.cpp * * Created on: 2014.9.18 * Author: Spike */ /*eclipse cdt, gcc 4.8.1*/ #include <iostream> #inclu

二叉树的前中后序递归和非递归遍历操作【代码】

“遍历”是二叉树各种操作的基础,可以在遍历过程中对节点进行各种操作,如:求节点的双亲,求节点的孩子,判断节点的层次,当然,还有一些更重要的操作,例如,依据遍历序列建立二叉树,,再如,对建立的二叉树进行线索化,等等. 二叉树的各种遍历操作必须了然于心,无论是递归的,还是非递归的.递归算法的优点是形式简单,当然,正如一句话所说“迭代是人,递归是神.”递归算法的整个详细过程还是很烧脑的,每一步都把未知当作已知,每一步又在把未知变为已知.想不到描述起来又用上了递归的概念,生活中其实有很多递归的现象,我印

java实现线索化二叉树的前序、中序、后续的遍历(完整代码)

java实现线索化二叉树的前序.中序.后续的遍历 比如创建一个二叉树 1 / 3 6 / \ / 8 10 14 线索化二叉树几个概念: n个节点的二叉链表中含有n+1 [公式2n-(n-1)=n+1]个空指针域.利用二叉链表中的空指针域,存放指向该节点在某种遍历次序下的前驱和后继节点的指针(这种附加指针成为线索).如下面的就是6+1=7个空指针域 (8,10,14各有连个指针没有指向 6有一个) 加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树.分为前序线索二叉树.中序线索二叉树.

压栈出栈遍历栈实例代码

#include<stdio.h> #include<stdlib.h> #include<malloc.h> typedef struct Node//定义一个链表结构体 { int data; struct Node* pNext; }NODE,*PNODE; typedef struct Stack//定义一个栈结构体 { PNODE pTop; PNODE pBottom; }STACK,*PSTACK; void initStack(PSTACK); void

树的层次遍历(Java代码实现)

与树的前中后序遍历的DFS思想不同,层次遍历用到的是BFS思想.一般DFS用递归去实现(也可以用栈实现),BFS需要用队列去实现. 层次遍历的步骤是: 1.对于不为空的结点,先把该结点加入到队列中 2.从队中拿出结点,如果该结点的左右结点不为空,就分别把左右结点加入到队列中 3.重复以上操作直到队列为空 1 public class Solution{ 2 class TreeNode { 3 int val; 4 TreeNode left; 5 TreeNode right; 6 TreeN

java类List及List遍历器的代码

从某个程序中截取的一个示例代码: List<User> users = userDao.selectAll(); //mybatis java orm Iterator<User> iter = users.iterator(); while(iter.hasNext()){ User u = iter.next(); System.out.println("用户名:"+u.getUserName()+"密码:"+u.getPassword(

第三讲.继承,完整初始化方法,遍历构造器,多态(代码) 另附植物大战僵尸练习

//初始化和遍历构造器使用 //person.h文件 1 #import <Foundation/Foundation.h> 2 3 @interface Person : NSObject 4 5 { 6 7 NSString *_name; 8 int _age; 9 NSString *_sex; 10 11 12 } 13 14 +(id)PersonWithName:(NSString*)name age:(int)age; 15 -(id)initWithName:(NSStrin

C#使用foreach语句遍历数组的代码

下面的内容内容是关于C#使用foreach语句遍历数组的内容,希望对大家有所好处. using System; public class w3demo { public static void Main() { int sum = 0; int[] nums = new int[10]; for(int i = 0; i < 10; i++) nums[i] = i; foreach(int x in nums) { Console.WriteLine("Value is: "

Java递归方法遍历二叉树的代码

将内容过程中经常用的内容做个记录,如下内容内容是关于Java递归方法遍历二叉树的内容. package com.wzs; public class TestBinaryTree { public static void main(String[] args) { Node<String> g = new Node<String>("G", null, null); Node<String> e = new Node<String>(&qu