目录
如果需要在PE文件中构建一端SHELLCODE(默认节区剩余空间不足情况下),可以通过新增节来解决此问题.通常大部分加壳软件都会新增节来移动或者备份各种目录项数据.
新增节对PE文件中哪些值会有影响?
- IMAGE_FILE_HEADER→NumberOfSections(当前PE文件节的数量,如果新增节需要修正此值).
- IMAGE_OPTIONAL_HEADER → SizeOfImage(新增节后在内存中的大小需要修正).
- 在默认最后一个IMAGE_SECTION_HEADER后追加一个节表结构体并修正其数据.
新增节步骤:
- 通过判断IMAGE_OPTIONAL_HEADER → SizeOfHeaders(所有头+节表文件对齐后的大小)来检查是否有足够空间新增一个节表数据,如果不够抹除DOS_STUB数据并整体移动NT,SECTION的数据到DOS_STUB位置.并修正IMAGE_DOS_HEADER → e_lfanew.
- 在当前最后节后面新增一个IMAGE_SECTION_HEADER结构,并依据新增节大小修正其属性.
- 修复IMAGE_FILE_HEADER→NumberOfSections.
- 修复IMAGE_OPTIONAL_HEADER → SizeOfImage.
- 重新分配新增后对应大小的文件缓存.
- 拷贝先前数据.
手动新增节
通过WinHex工具查看PE文件默认属性
1).判断空间是否足够新增节
当前最后一个节后存在SIZEOF(IMAGE_SECTION_HEADER) * 2(80BYTE) 大小的空闲数据.
2).最后节处追加一个节表结构
内存与文件中的大小设置为0x1000方便一点(文件中大小为文件对齐后的大小).
内存与文件中的偏移设置为上一个节中pointertorawdata + sizeofrawdata.
节属性根据实际要求设置即可标志位对应数据点此查看.
3).修复IMAGE_FILE_HEADER→NumberOfSections.
4.修复IMAGE_OPTIONAL_HEADER → SizeOfImage.
5.在文件中新增对应节数据
移至文件尾部选中最后一个字节,WinHex选中菜单栏中编辑,选中菜单项粘贴0数据
新增节大小为1000h,对应十进制为4096.
新增数据后另存为文件,通过PE工具查看其数据
程序是否可以正常运行就知道新增节成功与否.
代码新增节
读取文件代码
PVOID FileToMem(IN PCHAR szFilePath, OUT LPDWORD dwFileSize)
{
//打开文件
FILE* pFile = fopen(szFilePath, "rb");
if (!pFile)
{
printf("FileToMem fopen Fail \r\n");
return NULL;
}
//获取文件长度
fseek(pFile, 0, SEEK_END); //SEEK_END文件结尾
DWORD Size = ftell(pFile);
fseek(pFile, 0, SEEK_SET); //SEEK_SET文件开头
//申请存储文件数据缓冲区
PCHAR pFileBuffer = (PCHAR)malloc(Size);
if (!pFileBuffer)
{
printf("FileToMem malloc Fail \r\n");
fclose(pFile);
return NULL;
}
//读取文件数据
fread(pFileBuffer, Size, 1, pFile);
//判断是否为可执行文件
if (*(PSHORT)pFileBuffer != IMAGE_DOS_SIGNATURE)
{
printf("Error: MZ \r\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
if (*(PDWORD)(pFileBuffer + *(PDWORD)(pFileBuffer + 0x3C)) != IMAGE_NT_SIGNATURE)
{
printf("Error: PE \r\n");
fclose(pFile);
free(pFileBuffer);
return NULL;
}
if (dwFileSize)
{
*dwFileSize = Size;
}
fclose(pFile);
return pFileBuffer;
}
输出文件代码
VOID MemToFile(IN PCHAR szFilePath, IN PVOID pFileBuffer, IN DWORD dwFileSize)
{
//打开文件
FILE* pFile = fopen(szFilePath, "wb");
if (!pFile)
{
printf("MemToFile fopen Fail \r\n");
return;
}
//输出文件
fwrite(pFileBuffer, dwFileSize, 1, pFile);
fclose(pFile);
}
移动NT+节表代码
BOOL MoveNtAndSectionToDosStub(IN PCHAR pBuffer)
{
//定位结构
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuffer;
PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
//清空DOS_STUB数据
memset(pBuffer + sizeof(IMAGE_DOS_HEADER), 0, pDos->e_lfanew - sizeof(IMAGE_DOS_HEADER));
//移动数据大小
DWORD dwMoveSize = sizeof(IMAGE_NT_HEADERS) + pFil->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER;
//备份数据
PUCHAR pTemp = (PUCHAR)malloc(dwMoveSize);
if (!pTemp)
{
return FALSE;
}
memset(pTemp,0, dwMoveSize);
memcpy(pTemp, pBuffer + pDos->e_lfanew, dwMoveSize);
//清空默认数据
memset(pBuffer + pDos->e_lfanew, 0, dwMoveSize);
//移动数据
memcpy(pBuffer + sizeof(IMAGE_DOS_HEADER), pTemp, dwMoveSize);
//修正e_lfanew指向
pDos->e_lfanew = sizeof(IMAGE_DOS_HEADER);
free(pTemp);
return TRUE;
}
新增节代码
PVOID AddNewSection(PCHAR pBuffer, DWORD dwSectionSize, LPDWORD pNewFileSize)
{
//定位结构
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuffer;
PIMAGE_NT_HEADERS pNth = (PIMAGE_NT_HEADERS)(pBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
PIMAGE_OPTIONAL_HEADER pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
//判断头部是否有空间新增节
if (pBuffer + pOpo->SizeOfHeaders - &pSec[pFil->NumberOfSections + 1] < IMAGE_SIZEOF_SECTION_HEADER )
{
//抹除DOS_STUB数据并将NT,SECTION整理向上移动
BOOL bRet = MoveNtAndSectionToDosStub(pBuffer);
if (!bRet)
{
printf("AddNewSection MoveNtAndSectionToDosStub Fail \r\n");
free(pBuffer);
return NULL;
}
pDos = (PIMAGE_DOS_HEADER)pBuffer;
pNth = (PIMAGE_NT_HEADERS)(pBuffer + pDos->e_lfanew);
pFil = (PIMAGE_FILE_HEADER)((PUCHAR)pNth + 4);
pOpo = (PIMAGE_OPTIONAL_HEADER)((PUCHAR)pFil + IMAGE_SIZEOF_FILE_HEADER);
pSec = (PIMAGE_SECTION_HEADER)((PUCHAR)pOpo + pFil->SizeOfOptionalHeader);
}
//填充新增节数据
CHAR szName[] = ".Kernel";
memcpy(pSec[pFil->NumberOfSections].Name, szName,8);
pSec[pFil->NumberOfSections].Misc.VirtualSize = dwSectionSize;//内存中对齐前的大小
pSec[pFil->NumberOfSections].VirtualAddress = Align(pOpo->SectionAlignment, pSec[pFil->NumberOfSections - 1].Misc.VirtualSize + pSec[pFil->NumberOfSections - 1].VirtualAddress);//内存中的偏移
pSec[pFil->NumberOfSections].SizeOfRawData = Align(pOpo->FileAlignment, dwSectionSize);//文件中对齐后的大小
pSec[pFil->NumberOfSections].PointerToRawData = Align(pOpo->FileAlignment, pSec[pFil->NumberOfSections - 1].PointerToRawData + pSec[pFil->NumberOfSections - 1].SizeOfRawData);//文件中的偏移
pSec[pFil->NumberOfSections].PointerToRelocations = 0;
pSec[pFil->NumberOfSections].PointerToLinenumbers = 0;
pSec[pFil->NumberOfSections].NumberOfRelocations = 0;
pSec[pFil->NumberOfSections].NumberOfLinenumbers = 0;
pSec[pFil->NumberOfSections].Characteristics |= pSec->Characteristics;//默认代码节
pSec[pFil->NumberOfSections].Characteristics |= 0xC0000040;
//新增节后补充大小为IMAGE_SECTION_HEADER结构的0数据
memset(&pSec[pFil->NumberOfSections + 1], 0, IMAGE_SIZEOF_SECTION_HEADER);
//修复默认节数量
pFil->NumberOfSections++;
//修复内存镜像大小
pOpo->SizeOfImage += Align(pOpo->SectionAlignment, dwSectionSize);
//默认文件大小
DWORD dwOldSize = pSec[pFil->NumberOfSections - 2].SizeOfRawData + pSec[pFil->NumberOfSections - 2].PointerToRawData;
//当前文件大小
DWORD dwNewSize = pSec[pFil->NumberOfSections - 1].SizeOfRawData + pSec[pFil->NumberOfSections - 1].PointerToRawData;
if (pNewFileSize)
{
*pNewFileSize = dwNewSize;
}
//重新分配缓冲区
PUCHAR pTemp = (PUCHAR)malloc(dwNewSize);
if (!pTemp)
{
printf("AddNewSection malloc Fail \r\n");
free(pBuffer);
return NULL;
}
memset(pTemp,0, dwNewSize);
memcpy(pTemp, pBuffer, dwOldSize);
free(pBuffer);
return pTemp;
}
测试代码
int main()
{
//读取文件二进制数据
DWORD dwFileSize = 0;
PCHAR pFileBuffer = FileToMem(FILE_PATH_IN, &dwFileSize);
if (!pFileBuffer)
{
return;
}
//新增节
pFileBuffer = AddNewSection(pFileBuffer, 0x2222, &dwFileSize);
//将二进制数据输出到文件
MemToFile(FILE_PATH_OUT, pFileBuffer, dwFileSize);
return 0;
}