12558网页游戏私服论坛

 找回密码
 立即注册
游戏开服表 申请开服
游戏名称 游戏描述 开服状态 游戏福利 运营商 游戏链接
攻城掠地-仿官 全新玩法,觉醒武将,觉醒技能 每周新区 经典复古版本,长久稳定 进入游戏
巅峰新版攻 攻城掠地公益服 攻城掠地SF 新兵种、新武将(兵种) 进入游戏
攻城掠地公 散人玩家的天堂 新开 进入游戏
改版攻城掠 上线即可国战PK 稳定新区 全新改版,功能强大 进入游戏
少年江山 高福利高爆率 刚开一秒 江湖水落潜蛟龙 进入游戏
太古封魔录 开服送10亿钻石 福利多多 不用充钱也可升级 进入游戏
神魔之道 签到送元宝 稳定开新区 送豪华签到奖励 进入游戏
神奇三国 统帅三军,招揽名将 免费玩新区 激情国战,征战四方 进入游戏
龙符 三日豪礼领到爽 天天开新区 助你征战无双 进入游戏
王者之师 免费领豪华奖励 免费玩新区 6元送6888元宝 进入游戏
查看: 573|回复: 0

PE加载器的简单实现

[复制链接]

49

主题

49

帖子

108

积分

实习版主

Rank: 7Rank: 7Rank: 7

积分
108
发表于 2020-2-9 04:57:13 | 显示全部楼层 |阅读模式
模拟PE加载器加载PE文件,对导入表以及重定位表的操作过程。
PE加载器的步骤:
1.将PE文件用ReadFile读取数据
char szFileName[] = "1.exe";//打开文件,设置属性可读可写HANDLE hFile = CreateFileA(szFileName,        GENERIC_READ | GENERIC_WRITE,        FILE_SHARE_READ | FILE_SHARE_WRITE,        NULL,        OPEN_EXISTING,        FILE_ATTRIBUTE_ARCHIVE,        NULL);if (INVALID_HANDLE_VALUE == hFile){        printf("文件打开失败\n");        return 1;}//获取文件大小DWORD dwFileSize = GetFileSize(hFile, NULL);//申请空间将exe读取到内存中char *pData = new char[dwFileSize];if (NULL == pData){        printf("空间申请失败\n");        return 2;}DWORD dwRet = 0;ReadFile(hFile, pData, dwFileSize, &dwRet, NULL);CloseHandle(hFile);2.根据PE结构获取镜像大小,在自己的程序中申请可读可写可执行的内存。
char* chBaseAddress = NULL;//获取镜像大小DWORD dwSizeOfImage = GetSizeOfImage(pFileBuff);//在进程中开辟一块内存空间chBaseAddress = (char*)VirtualAlloc(NULL,        dwSizeOfImage,        MEM_COMMIT | MEM_RESERVE,        PAGE_EXECUTE_READWRITE);if (NULL == chBaseAddress){        printf("申请进程空间失败\n");        return NULL;}3.将申请的空间全部填为0
RtlZeroMemory(chBaseAddress, dwSizeOfImage);4.将用ReadFile读取的数据映射到内存中

由上图可知,PE文件在加载到内存中对齐粒度不同,因此在写代码时要注意这个问题
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuff;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pFileBuff + pDos->e_lfanew);PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);//所有头 + 结表头的大小DWORD dwSizeOfHeaders = pNt->OptionalHeader.SizeOfHeaders;//获取区段数量int nNumerOfSections = pNt->FileHeader.NumberOfSections;// 将前一部分都拷贝过去RtlCopyMemory(chBaseAddress, pFileBuff, dwSizeOfHeaders);char* chSrcMem = NULL;char* chDestMem = NULL;DWORD dwSizeOfRawData = 0;for (int i = 0; i < nNumerOfSections; i++){        if ((0 == pSection->VirtualAddress) ||                (0 == pSection->SizeOfRawData))        {                pSection++;                continue;        }        chSrcMem = (char*)((DWORD)pFileBuff + pSection->PointerToRawData);        chDestMem = (char*)((DWORD)chBaseAddress + pSection->VirtualAddress);        dwSizeOfRawData = pSection->SizeOfRawData;        RtlCopyMemory(chDestMem, chSrcMem, dwSizeOfRawData);        pSection++;}return TRUE;5.修复重定位
重定位后的地址 = 需要重定位的地址  -  默认加载基址  +  当前加载基址
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)(chBaseAddress + pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);//判断是否有重定位表if ((char*)pLoc == (char*)pDos){        return TRUE;}while ((pLoc->VirtualAddress + pLoc->SizeOfBlock) != 0) //开始扫描重定位表{        WORD *pLocData = (WORD *)((PBYTE)pLoc + sizeof(IMAGE_BASE_RELOCATION));        //计算需要修正的重定位项(地址)的数目        int nNumberOfReloc = (pLoc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);        for (int i = 0; i < nNumberOfReloc; i++)        {                // 每个WORD由两部分组成。高4位指出了重定位的类型,WINNT.H中的一系列IMAGE_REL_BASED_xxx定义了重定位类型的取值。                // 低12位是相对于VirtualAddress域的偏移,指出了必须进行重定位的位置。                if ((DWORD)(pLocData & 0x0000F000) == 0x00003000) //这是一个需要修正的地址                {                        DWORD* pAddress = (DWORD *)((PBYTE)pDos + pLoc->VirtualAddress +                                                                                                                                (pLocData & 0x0FFF));                                DWORD dwDelta = (DWORD)pDos - pNt->OptionalHeader.ImageBase;                        *pAddress += dwDelta;                }        }        //转移到下一个节进行处理        pLoc = (PIMAGE_BASE_RELOCATION)((PBYTE)pLoc + pLoc->SizeOfBlock);}return TRUE;6.根据PE结构的导入表,加载所需的dll,并获取导入函数的地址并写入导入表中
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDos +        pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);// 循环遍历DLL导入表中的DLL及获取导入表中的函数地址char *lpDllName = NULL;HMODULE hDll = NULL;PIMAGE_THUNK_DATA lpImportNameArray = NULL;PIMAGE_IMPORT_BY_NAME lpImportByName = NULL;PIMAGE_THUNK_DATA lpImportFuncAddrArray = NULL;FARPROC lpFuncAddress = NULL;DWORD i = 0;while (TRUE){        if (0 == pImportTable->OriginalFirstThunk)        {                break;        }        // 获取导入表中DLL的名称并加载DLL        lpDllName = (char *)((DWORD)pDos + pImportTable->Name);        hDll = GetModuleHandleA(lpDllName);        if (NULL == hDll)        {                hDll = LoadLibraryA(lpDllName);                if (NULL == hDll)                {                        pImportTable++;                        continue;                }        }        i = 0;        // 获取OriginalFirstThunk以及对应的导入函数名称表首地址        lpImportNameArray = (PIMAGE_THUNK_DATA)((DWORD)pDos + pImportTable->OriginalFirstThunk);        // 获取FirstThunk以及对应的导入函数地址表首地址        lpImportFuncAddrArray = (PIMAGE_THUNK_DATA)((DWORD)pDos + pImportTable->FirstThunk);        while (TRUE)        {                if (0 == lpImportNameArray.u1.AddressOfData)                {                        break;                }                // 获取IMAGE_IMPORT_BY_NAME结构                lpImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pDos + lpImportNameArray.u1.AddressOfData);                // 判断导出函数是序号导出还是函数名称导出                if (0x80000000 & lpImportNameArray.u1.Ordinal)                {                        // 序号导出                        lpFuncAddress = GetProcAddress(hDll, (LPCSTR)                                                   (lpImportNameArray.u1.Ordinal & 0x0000FFFF));                }                else                {                        // 名称导出                        lpFuncAddress = GetProcAddress(hDll, (LPCSTR)lpImportByName->Name);                }                lpImportFuncAddrArray.u1.Function = (DWORD)lpFuncAddress;                i++;        }        pImportTable++;}7.修改PE文件的加载基址
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);pNt->OptionalHeader.ImageBase = (ULONG32)chBaseAddress;8.跳转到PE的入口点处执行
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)chBaseAddress;PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(chBaseAddress + pDos->e_lfanew);char* ExeEntry = (char*)(chBaseAddress + pNt->OptionalHeader.AddressOfEntryPoint);// 跳转到入口点处执行__asm{        mov eax, ExeEntry        jmp eax}通过上述代码,成功加载并运行程序:


来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
楼主热帖
回复

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|12558网页游戏私服论坛 |网站地图

GMT+8, 2024-3-28 21:50 , Processed in 0.156250 second(s), 31 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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