空白区添加代码
在 PE 中插入一段调用 MessageBox 的代码。
获取MessageBox地址,构造ShellCode代码
利 OD 定位出 MessageBoxA 函数的地址为 0x77D507EA 。
构造 shellcode :
unsigned char shellcode[] = {
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBox
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP
};
其中 shellcode 中的地址需要根据 shellcode 在 PE 中插入的位置来确定。
找到合适的空白区域
节表中的 SizeOfRawData 为 该节在 PE 文件中关于文件对齐后的大小,Misc.VirtualSize 为该节加载到内存后的真实大小。因此可以添加内容的空白区域大小为 SizeOfRawData - Misc.VirtualSize 。
遍历 PE 文件的节表,找到可以添加 shellcode 的节。
PIMAGE_SECTION_HEADER findFree(int size) {
for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
if (pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize >= size) {
return pSectionHeader;
}
}
return (PIMAGE_SECTION_HEADER) NULL;
}
修复 shellcode 中的地址
shellcode 中的 JMP 和 CALL 后面跟的地址 = 下一条指令的地址 - 目标地址 。
DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
DWORD addr = ImageBase + rva;
*(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
*(DWORD *) (shellcode + 14) = OEP - (addr + 18);
修改节的属性以及 OEP
将 OEP 指向 shellcode,并将 shellcode 所在节的属性修改为可执行。
pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize;
将修正后的 shellcode 写入 PE 文件并保存
memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
运行结果
运行后生成了一个新的 PE 文件
运行 notepad1.exe,发现调用 MessageBox 的 shellcode 成功运行。
之后 PE 本身的代码也正常运行。
完整代码
// PE.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include <string>
#include <sstream>
#include<vector>
struct PE {
char *buf;
int size;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS32 pNTHeader;
std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
bool error;
explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
if (!fsRead.is_open()) {
std::cerr << "[-] failed to open the PE file." << std::endl;
error = true;
return;
}
fsRead.seekg(0, std::ios::end);
size = (int) fsRead.tellg();
buf = new char[size];
fsRead.seekg(0);
fsRead.read(buf, size);
pDosHeader = (PIMAGE_DOS_HEADER) buf;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
}
}
bool saveToFile(const std::string &path) {
std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
if (!fsWrite.is_open()) {
std::cerr << "[-] failed to open the dump file." << std::endl;
return false;
}
fsWrite.write(buf, size);
fsWrite.close();
return true;
}
bool isError() const {
return error;
}
~PE() {
delete[]buf;
}
};
unsigned char shellcode[] = {
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBox
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP
};
PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");
PIMAGE_SECTION_HEADER findFree(int size) {
for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
if (pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize >= size) {
return pSectionHeader;
}
}
return (PIMAGE_SECTION_HEADER) NULL;
}
int main() {
if (pe.isError()) {
std::cerr << "[-] Failed to load PE." << std::endl;
return 0;
}
PIMAGE_SECTION_HEADER pSectionHeader = findFree(sizeof(shellcode));
if (pSectionHeader == NULL) {
std::cerr << "[-] Filed to find free area." << std::endl;
return 0;
}
DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
DWORD addr = ImageBase + rva;
*(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
*(DWORD *) (shellcode + 14) = OEP - (addr + 18);
pSectionHeader->Characteristics |= 0x60000020;
pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize;
memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
return 0;
}
新增节
在 PE 中新增一个 .new
节,长度为 0x1000 。
将 IMAGE_NT_HEADER 和节表整体上移
如图所示,在节表结束到一个节之间的空白区域可能存放有有用的数据。
因此为了尽量保证添加节表后不会破坏 PE 文件,需要将 IMAGE_NT_HEADER 和节表整体上移,将 DOS stub 覆盖。
DWORD headSize = sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) * pe.pNTHeader->FileHeader.NumberOfSections;
if (pe.pNTHeader->OptionalHeader.SizeOfHeaders - sizeof(IMAGE_DOS_HEADER) - headSize < sizeof(IMAGE_SECTION_HEADER) * 2) {
std::cerr << "[-] Head space is not enough." << std::endl;
return 0;
}
memcpy(pe.buf + sizeof(IMAGE_DOS_HEADER), pe.pNTHeader, headSize);
DWORD offset = pe.pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);
pe.pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
pe.pNTHeader = (PIMAGE_NT_HEADERS) ((char *) pe.pNTHeader - offset);
for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
pSectionHeader = (PIMAGE_SECTION_HEADER) ((char *) pSectionHeader - offset);
}
在节表末尾添加一项,并将后一项填充 0
PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
memset(pNewSection, 0, sizeof(IMAGE_SECTION_HEADER));
memcpy(pNewSection->Name, sectionName, sizeof(sectionName));
PIMAGE_SECTION_HEADER pLastSection = pe.pSectionHeaders.back();
DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
pNewSection->Misc.VirtualSize = sectionSize;
pNewSection->VirtualAddress = pLastSection->VirtualAddress + (pLastSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
pNewSection->SizeOfRawData = (sectionSize + fileAlignment - 1) / fileAlignment * fileAlignment;
pNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;
pNewSection->Characteristics = sectionCharacteristics;
memset(pNewSection + 1, 0, sizeof(IMAGE_SECTION_HEADER));
更新 SizeOfImage 和 NumberOfSections 字段
注意:SizeOfHeaders 不需要更新,因为如果 SizeOfHeaders 需要更新那么节表没有足够的空间来新增节。
assert(pe.pNTHeader->OptionalHeader.SizeOfImage == pNewSection->VirtualAddress);
pe.pNTHeader->OptionalHeader.SizeOfImage += (pNewSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
pe.pNTHeader->FileHeader.NumberOfSections++;
为新增的节分配空间并在其中添加代码
assert(pe.size == pNewSection->PointerToRawData);
pe.resize(pe.size + pNewSection->SizeOfRawData);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
DWORD rva = pSectionHeader->VirtualAddress;
DWORD addr = ImageBase + rva;
*(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
*(DWORD *) (shellcode + 14) = OEP - (addr + 18);
pSectionHeader->Characteristics |= 0x60000020;
pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
DWORD foa = pSectionHeader->PointerToRawData;
memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
运行结果
新增了一个节,并且 OEP 也在改节。
运行生成的 PE 文件,添加的 shellcode 成功执行且原有的功能未受影响。
完整代码
// PE.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include<vector>
#include<assert.h>
#include<algorithm>
struct PE {
char *buf;
DWORD size;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS32 pNTHeader;
std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
bool error;
explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
if (!fsRead.is_open()) {
std::cerr << "[-] failed to open the PE file." << std::endl;
error = true;
return;
}
fsRead.seekg(0, std::ios::end);
size = (int) fsRead.tellg();
buf = new char[size];
fsRead.seekg(0);
fsRead.read(buf, size);
init();
}
void init() {
pDosHeader = (PIMAGE_DOS_HEADER) buf;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
}
}
bool saveToFile(const std::string &path) {
std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
if (!fsWrite.is_open()) {
std::cerr << "[-] failed to open the dump file." << std::endl;
return false;
}
fsWrite.write(buf, size);
fsWrite.close();
return true;
}
void resize(DWORD _size) {
char *tbuf = new char[_size];
memset(tbuf, 0, _size);
memcpy(tbuf, this->buf, min(_size, this->size));
delete[]this->buf;
this->buf = tbuf;
this->size = _size;
init();
}
bool isError() const {
return error;
}
~PE() {
delete[]buf;
}
};
const char sectionName[] = ".new";
const DWORD sectionSize = 0x1000;
const DWORD sectionCharacteristics = 0x60000020;
unsigned char shellcode[] = {
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBox
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP
};
int main() {
PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");
if (pe.isError()) {
std::cerr << "[-] Failed to load PE." << std::endl;
return 0;
}
DWORD headSize = sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) * pe.pNTHeader->FileHeader.NumberOfSections;
if (pe.pNTHeader->OptionalHeader.SizeOfHeaders - sizeof(IMAGE_DOS_HEADER) - headSize < sizeof(IMAGE_SECTION_HEADER) * 2) {
std::cerr << "[-] Head space is not enough." << std::endl;
return 0;
}
memcpy(pe.buf + sizeof(IMAGE_DOS_HEADER), pe.pNTHeader, headSize);
DWORD offset = pe.pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER);
pe.pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER);
pe.pNTHeader = (PIMAGE_NT_HEADERS) ((char *) pe.pNTHeader - offset);
for (int i = 0; i < pe.pSectionHeaders.size(); i++) {
PIMAGE_SECTION_HEADER &pSectionHeader = pe.pSectionHeaders[i];
pSectionHeader = (PIMAGE_SECTION_HEADER) ((char *) pSectionHeader - offset);
}
PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
memset(pNewSection, 0, sizeof(IMAGE_SECTION_HEADER));
memcpy(pNewSection->Name, sectionName, sizeof(sectionName));
PIMAGE_SECTION_HEADER pLastSection = pe.pSectionHeaders.back();
DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
pNewSection->Misc.VirtualSize = sectionSize;
pNewSection->VirtualAddress = pLastSection->VirtualAddress + (pLastSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
pNewSection->SizeOfRawData = (sectionSize + fileAlignment - 1) / fileAlignment * fileAlignment;
pNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;
pNewSection->Characteristics = sectionCharacteristics;
memset(pNewSection + 1, 0, sizeof(IMAGE_SECTION_HEADER));
assert(pe.pNTHeader->OptionalHeader.SizeOfImage == pNewSection->VirtualAddress);
pe.pNTHeader->OptionalHeader.SizeOfImage += (pNewSection->Misc.VirtualSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
pe.pNTHeader->FileHeader.NumberOfSections++;
assert(pe.size == pNewSection->PointerToRawData);
pe.resize(pe.size + pNewSection->SizeOfRawData);
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER) (pe.buf + sizeof(IMAGE_DOS_HEADER) + headSize);
DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
DWORD rva = pSectionHeader->VirtualAddress;
DWORD addr = ImageBase + rva;
*(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
*(DWORD *) (shellcode + 14) = OEP - (addr + 18);
pSectionHeader->Characteristics |= 0x60000020;
pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
DWORD foa = pSectionHeader->PointerToRawData;
memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
return 0;
}
扩大节
修改节表最后一项的数据并为扩大的节分配空间
PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders.back();
DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
pSectionHeader->Misc.VirtualSize += extendSize;
pSectionHeader->SizeOfRawData = (pSectionHeader->Misc.VirtualSize + fileAlignment - 1) / fileAlignment * fileAlignment;
pe.pNTHeader->OptionalHeader.SizeOfImage = (pe.pNTHeader->OptionalHeader.SizeOfImage + extendSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
pe.resize(pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData);
在扩大的节中写入 shellcode
assert(extendSize >= sizeof(shellcode));
DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);
DWORD addr = ImageBase + rva;
*(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
*(DWORD *) (shellcode + 14) = OEP - (addr + 18);
pSectionHeader->Characteristics |= 0x60000020;
pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);
memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
运行结果
如下图所示,最后一个节被扩大了。
运行生成的 PE 文件,shellcode 被执行且原有功能未受影响。
完整代码
// PE.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include<vector>
#include<assert.h>
#include<algorithm>
struct PE {
char *buf;
DWORD size;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS32 pNTHeader;
std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
bool error;
explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
if (!fsRead.is_open()) {
std::cerr << "[-] failed to open the PE file." << std::endl;
error = true;
return;
}
fsRead.seekg(0, std::ios::end);
size = (int) fsRead.tellg();
buf = new char[size];
fsRead.seekg(0);
fsRead.read(buf, size);
init();
}
void init() {
pDosHeader = (PIMAGE_DOS_HEADER) buf;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
}
}
bool saveToFile(const std::string &path) {
std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
if (!fsWrite.is_open()) {
std::cerr << "[-] failed to open the dump file." << std::endl;
return false;
}
fsWrite.write(buf, size);
fsWrite.close();
return true;
}
void resize(DWORD _size) {
char *tbuf = new char[_size];
memset(tbuf, 0, _size);
memcpy(tbuf, this->buf, min(_size, this->size));
delete[]this->buf;
this->buf = tbuf;
this->size = _size;
init();
}
bool isError() const {
return error;
}
~PE() {
delete[]buf;
}
};
const DWORD extendSize = 0x3000;
unsigned char shellcode[] = {
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0x6A, 0x00, // push 0
0xE8, 0x00, 0x00, 0x00, 0x00, // call MessageBox
0xE9, 0x00, 0x00, 0x00, 0x00 // jmp OEP
};
int main() {
PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");
if (pe.isError()) {
std::cerr << "[-] Failed to load PE." << std::endl;
return 0;
}
PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders.back();
DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
DWORD sectionAlignment = pe.pNTHeader->OptionalHeader.SectionAlignment;
pSectionHeader->Misc.VirtualSize += extendSize;
pSectionHeader->SizeOfRawData = (pSectionHeader->Misc.VirtualSize + fileAlignment - 1) / fileAlignment * fileAlignment;
pe.pNTHeader->OptionalHeader.SizeOfImage = (pe.pNTHeader->OptionalHeader.SizeOfImage + extendSize + sectionAlignment - 1) / sectionAlignment * sectionAlignment;
pe.resize(pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData);
assert(extendSize >= sizeof(shellcode));
pSectionHeader = pe.pSectionHeaders.back();
DWORD ImageBase = pe.pNTHeader->OptionalHeader.ImageBase;
DWORD OEP = ImageBase + pe.pNTHeader->OptionalHeader.AddressOfEntryPoint;
DWORD rva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);
DWORD addr = ImageBase + rva;
*(DWORD *) (shellcode + 9) = 0x77D507EA - (addr + 13);
*(DWORD *) (shellcode + 14) = OEP - (addr + 18);
pSectionHeader->Characteristics |= 0x60000020;
pe.pNTHeader->OptionalHeader.AddressOfEntryPoint = rva;
DWORD foa = pSectionHeader->PointerToRawData + pSectionHeader->Misc.VirtualSize - sizeof(shellcode);
memcpy(pe.buf + foa, shellcode, sizeof(shellcode));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
return 0;
}
合并节
合并节的时候有一个特别坑的点:虽然节表数量减少造成 SizeOfHeaders 改变,但不能修改 SizeOfHeaders ,因为 PE 文件加载时有些结构的定位依赖 SizeOfHeaders 。
修改节表第一项的属性
由于所有节合并成一个,因此需要将节表第一项的大小属性修改成所有节合并后对应的属性,并且 Characteristics 需要是包含所有节对应属性。
DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
pe.pSectionHeaders[0]->Misc.VirtualSize = pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->Misc.VirtualSize - pe.pSectionHeaders[0]->VirtualAddress;
pe.pSectionHeaders[0]->SizeOfRawData = (pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->SizeOfRawData - pe.pSectionHeaders[0]->VirtualAddress + fileAlignment - 1) / fileAlignment * fileAlignment;
for (int i = 1; i < pe.pSectionHeaders.size(); i++) {
pe.pSectionHeaders[0]->Characteristics |= pe.pSectionHeaders[i]->Characteristics;
}
扩大 PE 文件大小并将所有节拉伸
pe.resize(pe.pSectionHeaders[0]->PointerToRawData + pe.pSectionHeaders[0]->SizeOfRawData);
//pe.pNTHeader->OptionalHeader.SizeOfHeaders = (pe.pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) + fileAlignment - 1) / fileAlignment * fileAlignment;
pe.pNTHeader->FileHeader.NumberOfSections = 1;
for (int j = (int) pe.pSectionHeaders.size() - 1; j >= 1; j--) {
PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders[j];
char *buf = new char[pSectionHeader->SizeOfRawData];
memcpy(buf, pe.buf + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData);
memset(pe.buf + pSectionHeader->PointerToRawData, 0, pSectionHeader->SizeOfRawData);
memcpy(pe.buf + pe.pSectionHeaders[0]->PointerToRawData + pSectionHeader->VirtualAddress - pe.pSectionHeaders[0]->VirtualAddress, buf, pSectionHeader->SizeOfRawData);
delete[]buf;
}
运行结果
PE 文件所有节合并在一起且 PE 文件可以正常运行。
完整代码
// PE.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<Windows.h>
#include<fstream>
#include<vector>
#include<assert.h>
#include<algorithm>
struct PE {
char *buf;
DWORD size;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS32 pNTHeader;
std::vector<PIMAGE_SECTION_HEADER> pSectionHeaders;
bool error;
explicit PE(const std::string &path) : error(false), pDosHeader(NULL), pNTHeader(NULL) {
std::ifstream fsRead(path.c_str(), std::ios::in | std::ios::binary);
if (!fsRead.is_open()) {
std::cerr << "[-] failed to open the PE file." << std::endl;
error = true;
return;
}
fsRead.seekg(0, std::ios::end);
size = (int) fsRead.tellg();
buf = new char[size];
fsRead.seekg(0);
fsRead.read(buf, size);
init();
}
void init() {
pDosHeader = (PIMAGE_DOS_HEADER) buf;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pNTHeader = (PIMAGE_NT_HEADERS32) (buf + pDosHeader->e_lfanew);
if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
std::cerr << "[-] File is not a PE file." << std::endl;
error = true;
return;
}
pSectionHeaders.resize(pNTHeader->FileHeader.NumberOfSections);
for (int i = 0, p = pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32); i < pSectionHeaders.size(); i++, p += sizeof(IMAGE_SECTION_HEADER)) {
pSectionHeaders[i] = (PIMAGE_SECTION_HEADER) (buf + p);
}
}
bool saveToFile(const std::string &path) {
std::ofstream fsWrite(path.c_str(), std::ios::out | std::ios::binary);
if (!fsWrite.is_open()) {
std::cerr << "[-] failed to open the dump file." << std::endl;
return false;
}
fsWrite.write(buf, size);
fsWrite.close();
return true;
}
void resize(DWORD _size) {
char *tbuf = new char[_size];
memset(tbuf, 0, _size);
memcpy(tbuf, this->buf, min(_size, this->size));
delete[]this->buf;
this->buf = tbuf;
this->size = _size;
init();
}
bool isError() const {
return error;
}
~PE() {
delete[]buf;
}
};
int main() {
PE pe("C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe");
if (pe.isError()) {
std::cerr << "[-] Failed to load PE." << std::endl;
return 0;
}
assert(pe.pNTHeader->FileHeader.NumberOfSections > 1);
DWORD fileAlignment = pe.pNTHeader->OptionalHeader.FileAlignment;
pe.pSectionHeaders[0]->Misc.VirtualSize = pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->Misc.VirtualSize - pe.pSectionHeaders[0]->VirtualAddress;
pe.pSectionHeaders[0]->SizeOfRawData = (pe.pSectionHeaders.back()->VirtualAddress + pe.pSectionHeaders.back()->SizeOfRawData - pe.pSectionHeaders[0]->VirtualAddress + fileAlignment - 1) / fileAlignment * fileAlignment;
for (int i = 1; i < pe.pSectionHeaders.size(); i++) {
pe.pSectionHeaders[0]->Characteristics |= pe.pSectionHeaders[i]->Characteristics;
}
pe.resize(pe.pSectionHeaders[0]->PointerToRawData + pe.pSectionHeaders[0]->SizeOfRawData);
//pe.pNTHeader->OptionalHeader.SizeOfHeaders = (pe.pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER) + fileAlignment - 1) / fileAlignment * fileAlignment;
pe.pNTHeader->FileHeader.NumberOfSections = 1;
for (int j = (int) pe.pSectionHeaders.size() - 1; j >= 1; j--) {
PIMAGE_SECTION_HEADER pSectionHeader = pe.pSectionHeaders[j];
char *buf = new char[pSectionHeader->SizeOfRawData];
memcpy(buf, pe.buf + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData);
memset(pe.buf + pSectionHeader->PointerToRawData, 0, pSectionHeader->SizeOfRawData);
memcpy(pe.buf + pe.pSectionHeaders[0]->PointerToRawData + pSectionHeader->VirtualAddress - pe.pSectionHeaders[0]->VirtualAddress, buf, pSectionHeader->SizeOfRawData);
delete[]buf;
}
memset(pe.pSectionHeaders[1], 0, sizeof(IMAGE_SECTION_HEADER));
pe.saveToFile("C:\\Documents and Settings\\Administrator\\桌面\\notepad1.exe");
return 0;
}