遍历导出表(上课代码)

// 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;
}

 

转载于:https://www.cnblogs.com/Alyoyojie/p/5329481.html

用C++代码写出一个教务系统并满足以下内容【题目2】教务信息管理系统 学校拟开发一套管理系统,其中包含如下信息: 教师:工号、姓名、性别、出生年月、电话…… 课程:课程号、名称、学时、学分、上课时间地点…… 学生:学号、姓名、性别、出生年月、电话…… 选课:课程号、教师工号、学生学号、课程成绩…… 教室:教室号、名称、容量…… 上课时间包括从第几周上到第几周,每周的周几上课。是第几节课上课。假定这所学校从周一到周日第1至第12节课都可以排课。 为简化起见,本教学系统采用自然班统一进行排课。例如计科2301班的全部必修课程由管理员统一排课。我们只考虑必修课程,选修课程不作考虑。 基本要求包括: 1、提供用户操作的菜单和界面,用户至少可分为管理员、学生和教师三种类型。 2、设计相应的信息,用于记录信息,如学生信息、教师信息、课程信息、教室信息等,要求以文件的形式存储,格式可以自行设计。 3、管理员可进行如下操作: (1)学生、教师、课程、教室信息的新增、查看、修改、删除等。 (2)每学期开学前2周,要进行排课。要求设计实现一种算法进行排课。 (3)管理员对算法排好的课有权限进行修改。 (4)检验课是否存在教师、学生、教室的冲突。如果存在冲突,则系统显示出有冲突的相关信息以便于管理员进行调整。 4、学生可进行如下操作: (1)个人信息的新增、查看、修改、删除等。 (2)对教师的教学进行反馈,给出建议和意见。 (3)查询本班级的课。 (4)将本班级课导出为一个文本文件。 5、教师进行如下操作: (1)输入学生的成绩。 (2)查看学生的反馈意见。 (3)对所教班级某门课程的成绩进行排序。排序规则是<成绩(高优先),学号(小优先)>。也就是说,成绩高的学生排在前面,相同成绩的学生学号小的排在前面。 (4)查询本人的课。 (5)将本班级课导出为一个文本文件。 6、系统退出时,更新相应文件中的信息;当下次运行程序时,从文件读取所有信息。测试用例中的输入数据不少于20门课程、20位教师、160位同学。假定每个班级30-35人。
最新发布
03-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值