吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3321|回复: 21
收起左侧

[原创工具] 本工具目的是学习性的 用VC++写的对PE结构的解析

  [复制链接]
tfrist 发表于 2022-9-2 12:08
本帖最后由 tfrist 于 2022-9-2 12:16 编辑

前几日在灌水去有朋友在问学习PE结构的问题, 勾起了我的回忆, 在2006年曾经用VC++写过类似的程序, 就在帖子里面感慨了一下, 后面有坛友留言让放出来看看.
原帖在: https://www.52pojie.cn/thread-1681357-1-1.html
回复在: https://www.52pojie.cn/forum.php ... &page=2#pid43873656

好吧, 本着技术交流和抛砖引玉的目的,现在整理放出来. 希望对初学者有所帮助, 也给我自己留下些记录. 工程很简单, 就是读取PE结构从中找到引入表(Import Table)和导出表(Export Table) 然后显示出来.   通过解析过程熟悉学习PE结构方面的知识.

附件中有压缩的源代码 (谨慎啊, 下载需要1个CB).   测试VC6 和VS2005, VS2010都可以工作.

后记:
好不容易找到了多年尘封的U盘, 在U盘中一个布满蜘蛛网的文件夹的角落里找到了它, 当时备份的这个工程文件夹.
拷贝出来解压放到VS2010里面, 提示需要转换了一下工程(因为当时是用VC6写的工程), 然后F5跑了一下,还不错居然跑了起来.
随手试验了几个exe和dll文件, exe还好, 有一个dll解析的时候崩了. 打开源代码仔细看了看当时稚嫩的代码, 找到了crash的问题所在, fixed 这个bug.
测试了几个文件还好没有crash.  当然我不排除还有其他bug.  那就希望兄弟们自己修复了 哈哈!



运行起来就是这样青涩的界面:  图片可以显示吗?  图片上传到这里了 https://imgse.com/i/vIQhn0
aa.png

ListImportFun.zip

50.19 KB, 下载次数: 24, 下载积分: 吾爱币 -2 CB

PE学习源代码

免费评分

参与人数 4吾爱币 +12 热心值 +4 收起 理由
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
朱朱你堕落了 + 3 + 1 看来你现在是神级大佬了!
pang7 + 1 + 1 用心讨论,共获提升!鼓励源码
sam喵喵 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| tfrist 发表于 2022-9-2 12:11
几个关键的方法 我拷贝到这里


//读取文件信息
void CListImportFunDlg::ReadPeInfo(char *fileName)
{
        int len = 0;
        if(fileName == NULL) //如果文件名错误
                return;
        if(m_buf != NULL) //如果buf不为空 则先行释放
        {
                delete [] m_buf;
                m_buf = NULL;
        }
        CStdioFile file;
        if(file.Open(fileName, CFile::modeRead|CFile::typeBinary))
        { //如果读取文件成功
                len = file.GetLength();
                m_filesize = len;
                m_buf = new BYTE[len];
                file.Read(m_buf, len); //读取文件信息
                file.Close();
        }
        else
        {
                DWORD error = GetLastError();
                LPVOID lpMsgBuf;
                //得到当前错误的 字符串
                FormatMessage(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM |
                        FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL,
                        error,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                        (LPTSTR) &lpMsgBuf,
                        0,
                        NULL
                        );
                // Display the string.
                MessageBox((LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
        }
}

IMAGE_IMPORT_DESCRIPTOR importDes;
//显示import表中的数据 指向的引入表项的首地址
void CListImportFunDlg::ShowImportTable()
{
        char dllname[40] = {0};
        BYTE *temp = m_pImportTable;
        int offset = 0;
        char outputBuf[0x100] = {0};
        for(int cnt=0; cnt<m_dllNum; cnt++)
        {
                importDes = *(IMAGE_IMPORT_DESCRIPTOR*)(m_pImportTable+cnt*0x14);
                if(0 == importDes.Name)
                        return; //完成了
                temp = m_buf+importDes.Name-m_v_import_Off; //转换成本地文件偏移
                sprintf(dllname, "%s", (char*)temp); //得到所在dll的名字
                sprintf(outputBuf, "-------------- %s Import Table --------------\n", dllname);
                AddMsg(outputBuf, 1);
                if(importDes.FirstThunk&0x80000000)
                {//表示为函数的序列号
                        if(0 == importDes.OriginalFirstThunk)
                                continue;
                        temp = (BYTE*)importDes.OriginalFirstThunk;
                        temp += (DWORD)m_buf;
                        temp -= m_v_import_Off;       
                }
                else
                {
                        temp = (BYTE*)importDes.FirstThunk;
                        temp += (DWORD)m_buf;
                        temp -= m_v_import_Off;                       
                }
                int cnt1 = 0;
                BYTE *addr = NULL;
                while(1)
                {
                        addr = (BYTE*)*(DWORD*)(temp+cnt1*4);                       
                        if(NULL == addr)
                                break;                       
                        memset(outputBuf, 0, sizeof(outputBuf));
                        if((DWORD)addr&0x80000000)
                        {
                                sprintf(outputBuf, "(%03d) [%04X]\n", cnt1+1, (DWORD)addr&0xFFFF);
                        }
                        else
                        {
                                addr -= m_v_import_Off;
                                if((DWORD)addr > (DWORD)m_filesize)
                                {
                                        sprintf(outputBuf, "import (%03d) [%04X] -- Address (%Xh)\n", cnt1+1, (addr));
                                }
                                else
                                {
                                        addr += (DWORD)m_buf;
                                        sprintf(outputBuf, "import (%03d) [%04X] -- %s\n", cnt1+1, *(WORD*)(addr), addr+2);
                                }
                        }
                        AddMsg(outputBuf);
                        cnt1++;
                }
        }
}

IMAGE_EXPORT_DIRECTORY exportDes;
//显示export表中的数据 指向的输出表项的首地址
void CListImportFunDlg::ShowExportTable()
{
        if(NULL == m_pExportTable) //如果输出表的地址为0则说明没有
        {
                AddMsg("-------------- No Export Table Found!!!--------------\n");
                AddMsg("______________________________________________\n");
                return;
        }
        char dllname[40] = {0};
        BYTE *temp = m_pExportTable;
        int offset = 0;
        char outputBuf[0x100] = {0};

        offset = *(DWORD*)(temp+0xc);
        offset -= m_v_export_Off;
        offset += (DWORD)m_buf;

        sprintf(dllname, "%s", offset);
        sprintf(outputBuf, "-------------- %s Export Table --------------\n", dllname);
        AddMsg(outputBuf, 1);
       
        offset = *(DWORD*)(temp+0x20); //得到输出函数名字数组的头
        offset -= m_v_export_Off;
        offset += (DWORD)m_buf;       
        int funNum = *(DWORD*)(temp+0x18); //通过名字输出的函数的个数
        temp = (BYTE*)offset; //
        BYTE *addr = NULL;
        for(int cnt=0; cnt<funNum; cnt++)
        {
                addr = (BYTE*)*(DWORD*)(temp+cnt*4);
                addr -= m_v_export_Off;
                addr += (DWORD)m_buf;
                sprintf(outputBuf, "export (%03d) -- %s\n", cnt+1, addr);
                AddMsg(outputBuf);
        }
}

//通过一个虚拟偏移地址 RVA 的到它所在节的偏移值
int CListImportFunDlg::GetSectionOffset(int addr, char* secName)
{
        BYTE *temp = m_pPEHead;
        if(m_buf == NULL)
                return 0;
        IMAGE_SECTION_HEADER sectionH;        //用于保存节的结构
        int sectionCount = *(WORD*)(m_pPEHead+0x6); //得到节的个数
        temp = m_pPEHead+0xf8; //得到块的首地址
        //同所有的节比较 找到所在的节信息
        int maxAddr = 0;
        for(int cnt=0; cnt<sectionCount; cnt++)
        {
                sectionH = *(IMAGE_SECTION_HEADER*)(temp+cnt*0x28);
                //本块的映射地址+本块的大小 得到本块的最大
                maxAddr = sectionH.VirtualAddress + sectionH.Misc.VirtualSize;
                //计算偏移值
                if(addr > maxAddr)
                        continue;
                if(secName != NULL)
                        sprintf(secName, "%s", sectionH.Name);
                return sectionH.VirtualAddress - sectionH.PointerToRawData;
        }
        return 0;
}

//进一步处理pe文件 返回如果为false 说明当前的文件不是pe文件
BOOL CListImportFunDlg::ParsePEStruct()
{
        BYTE *temp = m_buf;
        if(m_buf == NULL)
                return FALSE;
        if(memcmp(temp, "MZ", 2) != 0)
                return FALSE;
        int len = *(DWORD*)(temp+0x3c); //pe文件的首地址
        temp += len;
        if(memcmp(temp, "PE\0\0", 4) != 0)
        {
                delete [] m_buf;
                m_buf = NULL;
                return FALSE;
        }
        m_pPEHead = temp; //得到首地址
        temp += 0x80; //指向数据目录表中的第2项 “import项”
        int offset = *(DWORD*)temp; //得到 输入表项数组的首地址的 RVA 需要转换成文件地址
        len = *(DWORD*)(temp+4); //得到 输入表数组的总大小
        m_dllNum = len/0x14; //得到共有多少个dll引入表       
        m_v_import_Off = GetSectionOffset(offset, m_secName); //得到rva和真正物理地址的差 和 所在的节的名字
        char msg[0x100] = {0};
        sprintf(msg, "SectionName: %s  offset: %d\n\n", m_secName, m_v_import_Off);
        AddMsg(msg);
        //指到import table 表结构的数组的头部 每一个import结构占用14h个字节
        m_pImportTable = m_buf+(offset-m_v_import_Off); //经过运算后的引入表的物理地址       
        //指到export table 表结构的首地址
        temp = m_pPEHead;
        temp += 0x78;
        offset = *(DWORD*)temp;
        m_v_export_Off = GetSectionOffset(offset, m_secName); //得到rva和真正物理地址的差 和 所在的节的名字
        if(NULL != offset)
        {
                m_pExportTable = m_buf+(offset-m_v_export_Off); //经过计算后的输出表的物理地址       
                sprintf(msg, "SectionName: %s  offset: %d\n\n", m_secName, m_v_export_Off);
                AddMsg(msg);
        }
        else
                m_pExportTable = NULL; //没有输出表
        return TRUE;
}
 楼主| tfrist 发表于 2022-9-7 00:27
sam喵喵 发表于 2022-9-2 12:15
 楼主| tfrist 发表于 2022-9-2 12:17
sam喵喵 发表于 2022-9-2 12:15
感谢分享,拜读学习!

好快啊  谢谢捧场!
 楼主| tfrist 发表于 2022-9-2 12:18
sam喵喵 发表于 2022-9-2 12:15
感谢分享,拜读学习!

多亏你的鼓励我才找到这个许久的工程
twapj 发表于 2022-9-2 13:10
学习了!!
tp206555 发表于 2022-9-2 14:27
感谢分享
youcome 发表于 2022-9-2 14:28
谢谢楼主的分享,试试看
夏夜吉他 发表于 2022-9-2 18:58
支持原创!
qinren 发表于 2022-9-2 19:20
支持~~~!感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-26 09:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表