PE 空白区注入shellcode
实现流程:
1、在具有可执行属性的文件中进行写入相对应的shellcode,写入shellcode的时候需要先查看是否文件对齐和内存对齐一致,如果不一致那需要先转换,如果一致就可以直接在空白的地址上进行填写!
2、填写完shellcode之后,最后的跳转要跳回原程序的入口地址,所以还需要更改跳转到原入口点 !
3、因为需要一打开就先执行shellcode,所以需要将原入口点改为当前shellcode的开始地址!
注入过程:
当前要注入的EXE的信息:
MESSAGEBOXW 0x77D66534 基址 0x1000000 AddressOfEntryPoint 入口地址为 0x739D 内存对齐0x1000 文件对齐0x200
1、先计算实际位置text节 实际文件大小 0x7748 文件对齐大小 0x7800 文件偏移0x400 内存偏移0x1000 ------> 节数据 7B48 ~ 7C00
2、当前shellode的开始地址需要进行转换为内存地址,当前地址为7B60,所以转换为 7B60-400 + 1000 + 1000000 = 1008760,这个就是内存中的地址
3、然后再进行CALL指令调用,根据公式:X = 真正要跳转的地址 - E8这条指令的下一行地址
那么就是 77D66534(要跳转的地址) - (1008760 + 8 + 5) = 76D5DDC7,则填写的就是E8 C7 DD D5 76
,E8为CALL的硬编码
4、最后需要JMP到原来AddressOfEntryPoint
的地方,那么继续根据公式:X = 真正要跳转的地址(AddressOfEntryPoint) - E9这条指令的下一行地址,100739D - (1008760 + D + 5) = FFFFEC2B,则填写的就是E9 2B EC FF FF
,E9为JMP的硬编码
5、因为我们还需要当程序一加载就运行shellcode,所以我们的程序入口点需要被修改,也就是AddressOfEntryPoint要被修改,修改为当前shellcode的入口地址,也就是修改为RVA,上面已经算过了1008760
为VA,8760为RVA
!
6、运行测试,测试成功!
缺点:
1、这也只能算是单机版,因为没有进行动态加载获取MessageBoxA的地址,因为每个机器中的内存中user32.dll中MessageBoxA地址都不是唯一的
注意点:
1、这里的测试程序内存对齐和文件对齐都相同,所以好测试,但是如果使文件和内存对齐大小不同的话,那么就需要特别注意,此时我们在计算跳转地址的时候,需要先将FOA转换为RVA然后再进行计算填写对应的数据才行,比如入口点的地址这些填写的时候就需要先进行计算,先将FOA转换为RVA,然后再去改,原因就是对齐大小不同
2、这里的添加shellcode,我们只是在代码节中进行修改,大家都知道代码节的的属性是可执行的,如果想要在其他节中使用的话,那我们还需要注意该节的属性,也就是我们需要修改该节的属性为可执行!
代码实现:
DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID pImageBuffer); DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer); DWORD FOA_TO_RVA(PVOID FileAddress, DWORD FOA,PDWORD pRVA); DWORD RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA); void MyReadFile(PVOID* pFileBuffer,PDWORD BufferLenth); void MyWriteFile(PVOID pMemBuffer,size_t size); #define FILENAME "C:\\Documents and Settings\\Administrator\\桌面\\NOTEPAD.EXE" #define NEWFILENAME "C:\\Documents and Settings\\Administrator\\桌面\\NEW_NOTEPAD.EXE" //ReadPEFile:将文件读取到缓冲区 //参数说明: //lpszFile 文件路径 //pFileBuffer 缓冲区指针 //返回值说明: //读取失败返回0 否则返回实际读取的大小 void MyReadFile(PVOID* pFileBuffer,PDWORD BufferLenth){ FILE* File; File = fopen(FILENAME,"rb"); if(File == NULL){ printf("文件句柄打开失败"); return; } //读取文件 fseek(File,0,SEEK_END); *BufferLenth = ftell(File); fseek(File,0,SEEK_SET); //开辟新空间 *pFileBuffer = (PVOID)malloc(*BufferLenth); //内存清零 memset(*pFileBuffer,0,*BufferLenth); //读取到内存缓冲区 fread(*pFileBuffer,*BufferLenth,1,File); //关闭文件句柄 fclose(File); } //************************************************************************** //CopyFileBufferToImageBuffer:将文件从FileBuffer复制到ImageBuffer //参数说明: //pFileBuffer FileBuffer指针 //pImageBuffer ImageBuffer指针 //返回值说明: //读取失败返回0 否则返回复制的大小 DWORD CopyFileBufferToImageBuffer(PVOID pFileBuffer,PVOID* pImageBuffer){ PIMAGE_DOS_HEADER pImageDosHeader = NULL; PIMAGE_NT_HEADERS pImageNtHeader = NULL; PIMAGE_FILE_HEADER pImageFileHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL; PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL; DWORD ImageBufferSize = 0; int i=0; // DOS头 pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 标准PE pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4); // 可选PE pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER); //节表组 pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader); //获取ImageBufffer的内存大小 ImageBufferSize = pImageOptionalHeader->SizeOfImage; //为pImageBuffer分配内存空间 *pImageBuffer = (PVOID)malloc(ImageBufferSize); if (*pImageBuffer == NULL) { printf("malloc failed"); return -1; } //清零 memset(*pImageBuffer, 0, ImageBufferSize); // 拷贝头+节表 memcpy(*pImageBuffer, pFileBuffer, pImageOptionalHeader->SizeOfHeaders); //循环拷贝节表 for(i=0;i<pImageFileHeader->NumberOfSections;i++){ memcpy( (PVOID)((DWORD)*pImageBuffer + pImageSectionHeaderGroup[i].VirtualAddress), // 要拷贝的位置 ImageBuffer中的每个节数据的偏移位置 (PVOID)((DWORD)pFileBuffer + pImageSectionHeaderGroup[i].PointerToRawData), // 被拷贝的位置是 Filebuffer中的每个节数据的偏移位置 pImageSectionHeaderGroup[i].SizeOfRawData // 被拷贝的大小为 每个节数据的文件对齐大小 ); } return 0; } //************************************************************************** //CopyImageBufferToNewBuffer:将ImageBuffer中的数据复制到新的缓冲区 //参数说明: //pImageBuffer ImageBuffer指针 //pNewBuffer NewBuffer指针 //返回值说明: //读取失败返回0 否则返回复制的大小 DWORD CopyImageBufferToNewBuffer(PVOID pImageBuffer,PVOID* pNewBuffer){ PIMAGE_DOS_HEADER pImageDosHeader = NULL; PIMAGE_NT_HEADERS pImageNtHeader = NULL; PIMAGE_FILE_HEADER pImageFileHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL; PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL; DWORD NewBufferSize = 0; int i; int j; // DOS头 pImageDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer; //pImageNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew); // 标准PE pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4); // 可选PE pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER); //节表组 pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader); //获取NewBufferSize的内存大小 NewBufferSize = pImageOptionalHeader->SizeOfHeaders; //再循环加上节数据的大小 for(j=0;j<pImageFileHeader->NumberOfSections;j++){ NewBufferSize += pImageSectionHeaderGroup[j].SizeOfRawData; } //为NewBufferSize分配内存空间 *pNewBuffer = (PVOID)malloc(NewBufferSize); if (*pNewBuffer == NULL) { printf("malloc failed"); return -1; } //清零 memset(*pNewBuffer, 0, NewBufferSize); // 拷贝头+节表 memcpy(*pNewBuffer, pImageBuffer, pImageOptionalHeader->SizeOfHeaders); //循环拷贝节表 for(i=0;i<pImageFileHeader->NumberOfSections;i++){ memcpy( (PVOID)((DWORD)*pNewBuffer + pImageSectionHeaderGroup[j].PointerToRawData), (PVOID)((DWORD)pImageBuffer + pImageSectionHeaderGroup[j].VirtualAddress), pImageSectionHeaderGroup[j].SizeOfRawData ); } return NewBufferSize; } //功能:FOA 转换 RVA DWORD FOA_TO_RVA(PVOID FileAddress, DWORD FOA,PDWORD pRVA) { int ret = 0; int i; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress); PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader); //RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA if (FOA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment) { *pRVA = FOA; return ret; } //循环判断FOA在节区中 for (i=0;i < pFileHeader->NumberOfSections; i++) { if (FOA >= pSectionGroup[i].PointerToRawData && FOA < pSectionGroup[i].PointerToRawData + pSectionGroup[i].SizeOfRawData) { *pRVA = FOA - pSectionGroup[i].PointerToRawData + pSectionGroup[i].VirtualAddress; return *pRVA; } } //没有找到地址 ret = -4; printf("func FOA_TO_RVA() Error: %d 地址转换失败!\n", ret); return ret; } //功能:RVA 转换 FOA DWORD RVA_TO_FOA(PVOID FileAddress, DWORD RVA, PDWORD pFOA) { int ret = 0; int i=0; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress); PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4); PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER)); PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader); //RVA在文件头中 或 SectionAlignment 等于 FileAlignment 时RVA等于FOA if (RVA < pOptionalHeader->SizeOfHeaders || pOptionalHeader->SectionAlignment == pOptionalHeader->FileAlignment) { *pFOA = RVA; return ret; } //循环判断RVA在节区中 for (i = 0; i < pFileHeader->NumberOfSections; i++) { if (RVA >= pSectionGroup[i].VirtualAddress && RVA < pSectionGroup[i].VirtualAddress + pSectionGroup[i].Misc.VirtualSize) { *pFOA = pSectionGroup[i].PointerToRawData + RVA - pSectionGroup[i].VirtualAddress; return ret; } } //没有找到地址 ret = -4; printf("func RAV_TO_FOA() Error: %d 地址转换失败!\n", ret); return ret; } //功能:保存文件 void MyWriteFile(PVOID pMemBuffer,size_t size){ FILE* File; File = fopen(NEWFILENAME,"wb"); if(File == NULL){ printf("文件句柄打开失败"); return; } fwrite(pMemBuffer,size,1,File); printf("文件保存成功!"); fclose(File); } //功能:添加shellcode void FileBufferToAddShellcode(PVOID pFileBuffer){ PIMAGE_DOS_HEADER pImageDosHeader = NULL; PIMAGE_FILE_HEADER pImageFileHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pImageOptionalHeader = NULL; PIMAGE_SECTION_HEADER pImageSectionHeaderGroup = NULL; DWORD CodeAddress = 0; //要添加shellcode的地址 DWORD FuncAddress; //MESSAGEBOX地址 HMODULE hModule; //加载User32 DWORD FOA = 0; DWORD RVA = 0; BYTE SHELLCODE[] = { 0X6A,0X00,0X6A,0X00,0X6A,0X00,0X6A,0X00, 0XE8,0X00,0X00,0X00,0X00, 0XE9,0X00,0X00,0X00,0X00 }; DWORD E8_Next_Address; DWORD E9_Next_Address; DWORD EntryOfAddress; DWORD CodeAddress_bak; // DOS头 pImageDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 标准PE pImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pImageDosHeader + pImageDosHeader->e_lfanew + 4); // 可选PE pImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pImageFileHeader + IMAGE_SIZEOF_FILE_HEADER); //节表组 pImageSectionHeaderGroup = (PIMAGE_SECTION_HEADER)((DWORD)pImageOptionalHeader + pImageFileHeader->SizeOfOptionalHeader); //获取Messagebox的地址 hModule = LoadLibrary("User32.dll"); FuncAddress = (DWORD)GetProcAddress(hModule, "MessageBoxA"); // CodeAddress为SHELLCODE在文件中的起始地址 CodeAddress = (DWORD)pImageSectionHeaderGroup[0].PointerToRawData + (DWORD)pImageSectionHeaderGroup[0].Misc.VirtualSize; CodeAddress_bak = CodeAddress; // 计算E8这条指令的下一行地址的RVA E8_Next_Address = CodeAddress + 13; FOA_TO_RVA(pFileBuffer,E8_Next_Address,&RVA); // X = 真正要跳转的地址 - E8这条指令的下一行地址 E8_Next_Address = FuncAddress - (RVA + pImageOptionalHeader->ImageBase); //填充E8空白的空白部分 memcpy(&SHELLCODE[9], &E8_Next_Address, 4); // 计算E9这条指令的下一行地址的RVA E9_Next_Address = CodeAddress + 18; FOA_TO_RVA(pFileBuffer,E9_Next_Address,&RVA); //再获取原来入口地址的VA EntryOfAddress = pImageOptionalHeader->AddressOfEntryPoint; // X = 真正要跳转的地址 - E9这条指令的下一行地址 E9_Next_Address = EntryOfAddress - RVA; memcpy(&SHELLCODE[14],&E9_Next_Address,4); //填充完了shellcode,最后再把shellcode放进去 memcpy((PVOID)((DWORD)pFileBuffer+CodeAddress_bak),SHELLCODE,0x20); //最后替换OEP的位置,替换为shellcode的地址 FOA_TO_RVA(pFileBuffer,CodeAddress_bak,&RVA); pImageOptionalHeader->AddressOfEntryPoint = RVA; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY