思路过程
1、要判断空间是否足够
要判断节目录下面是否有足够的空间添加一个节目录项,一个节目录项需要40个字节的空间,而且整个节目录必须以40个存储 0的字节结尾,也就是所加一个节目录项只是需要 80个字节的空间。
2、复制一个相同属性的节,然后在复制过来的基础上进行修改必要的属性
3、修改数据
1、修改节的数量 _IMAGE_FILE_HEADER->NumberOfSections
2、修改PE文件内存拉伸后的大小,IMAGE_OPTIONAL_HEADER ->SizeOfImage 3、在文件尾添加空间,按照文件对齐来添加 4、修改节的属性 _IMAGE_SECTION_HEADER->Characteristics 5、修改节的名字 _IMAGE_SECTION_HEADER->Name 6、修改节在内存中的大小IMAGE_SECTION_HEADER->Misc.VirtualSize, 这个尺寸可以直接修改为 SizeOfRawData 7、修改在内存中偏移的地址VirtualAddress ,是上一个节的VirtualAddress值加上Misc.VirtualSize,然后按文件对齐后的尺寸
3503C+1D4000=20 903C(按对齐后的大小)=20A000
8.修改在文件中对齐的大小SizeOfRawData,这个值和内存中对齐前的一样 9.修改在文件中的偏移PointerToRawData,实际就是上一个节的在文件中的偏移PointerToRawData+在文件中对齐后的尺寸SizeOfRawData 10.修改最后一个节 Characteristics ,节的属性,60000020 表示代码节属性
当空间不足时
1、有些程序会在节表到节的空白区之间添加一些信息,导致节表下面没有空间
程序的DOS头到PE签名之间有一处区域DOS Stub,不影响程序的运行,数据也是程序的一些说明信息,对我们来说就是垃圾数据,所以我们可以将PE到节表末尾这一部分整体上移,把Dos Stub这块数据覆盖了,接着修改DOS头中的e_lfanew字段的值为上移后PE签名的地址即可,那么此时下面就会空出来一部分,我们就可以先将这部分全部修改成0x00,再往这片区域新增节表即可
2、如果第一条的空间也不足, 就扩大最后一个节的尺寸,扩大最后一个节不影响其它节。 扩大节的步骤: (Ex 新增节的大小)
代码方式实现添加节
添加节并实现插入shellcode
VOID TestAddSecInCode()
{
PVOID pFileBuffer = NULL;
PVOID pImageBuffer = NULL;
PVOID pNewBuffer = NULL;
PVOID pNewImageBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeaderUP = NULL;
PBYTE codeBegin = NULL;
BOOL isOk = FALSE;
DWORD size = 0;
/*File->fileBuffer*/
ReadPEFile(file_path, &pFileBuffer);
if (!pFileBuffer)
{
printf("文件->缓冲区失败");
return;
}
/*FileBuffer->ImageBuffer*/
CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
if (!pImageBuffer)
{
printf(" 复制文件到缓冲区失败");
free(pFileBuffer);
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)(((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)(((DWORD)pImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER + pPEHeader->SizeOfOptionalHeader);
pNewImageBuffer = malloc(pOptionHeader->SizeOfImage + 0x1000);
if (pNewImageBuffer == NULL)
{
printf("NewImage分配失败");
free(pNewImageBuffer);
return;
}
memset(pNewImageBuffer, 0, pOptionHeader->SizeOfImage + 0x1000);//空间初始化
printf("pNewImageBuffer的起始位置:%p\n", (char*)pNewImageBuffer);
memcpy(pNewImageBuffer, pImageBuffer, pOptionHeader->SizeOfImage);
//pNewImageBuffer重新定义头部信息
PIMAGE_DOS_HEADER pDosHeader_2 = (PIMAGE_DOS_HEADER)pNewImageBuffer;
PIMAGE_FILE_HEADER pPEHeader_2 = (PIMAGE_FILE_HEADER)((DWORD)((DWORD)pNewImageBuffer + pDosHeader->e_lfanew) + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader_2 = (PIMAGE_OPTIONAL_HEADER32)(((DWORD)pNewImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_SECTION_HEADER pSectionHeader_2 = (PIMAGE_SECTION_HEADER)(((DWORD)pNewImageBuffer + pDosHeader->e_lfanew) + 4 + IMAGE_SIZEOF_FILE_HEADER + pPEHeader->SizeOfOptionalHeader);
PIMAGE_SECTION_HEADER pSectionHeader_2_1 = pSectionHeader_2;
// 判断有没有80个字节的空间添加节目录信息
// 用SizeOfHeader头大小,减去DOS头到最后一个节目录的值 就可以判断空间是否足够
// 计算DOS头到最后一个节目录大小
//计算最后一个节目录位置
pSectionHeader_2_1 = pSectionHeader_2 + (pPEHeader_2->NumberOfSections);
DWORD addOfSecSize = pOptionHeader_2->SizeOfHeaders - ((DWORD)pSectionHeader_2 + 40 - (DWORD)pNewImageBuffer);
printf("节的空闲大小:%i\n", addOfSecSize);
if (addOfSecSize < 80)
{
printf("剩余空间不足");
return;
}
//复制节到新的节表
codeBegin =(PBYTE)pSectionHeader_2_1;
memcpy(codeBegin, pSectionHeader_2, 40);
//修改属性
pPEHeader_2->NumberOfSections += 1;//修改节的数量
pOptionHeader_2->SizeOfImage += 0x1000;//修改sizeofimage的大小
pSectionHeader_2 += pPEHeader_2->NumberOfSections - 1;//指向新加节表的位置
//修改节表属性
sprintf((char*)pSectionHeader_2->Name, "%s", ".tttt");
pSectionHeader_2->Misc.VirtualSize = 0x1000; //修改这个节在内存中大小,没有对齐的大小,这里写与添加的空间一样大
pSectionHeaderUP = pSectionHeader_2 - 1;
//修改在内存中RVA偏移地址,是上一个节的VirtualAddress值加上Misc.VirtualSize,然后按文件对齐后的尺寸
pSectionHeader_2->VirtualAddress = (pSectionHeaderUP->VirtualAddress + pSectionHeaderUP->Misc.VirtualSize+pOptionHeader_2->SectionAlignment)/pOptionHeader_2->SectionAlignment*pOptionHeader_2->SectionAlignment;
pSectionHeader_2->SizeOfRawData = 0x1000;//修改在文件对齐后的大小
pSectionHeader_2->PointerToRawData = pSectionHeaderUP->PointerToRawData + pSectionHeaderUP->SizeOfRawData;
//添加代码
//将代码复制到空闲区
codeBegin = (PBYTE)((DWORD)pNewImageBuffer + pSectionHeader_2->VirtualAddress);
memcpy(codeBegin, shellcode, SHELLCODELENGTH);
//修正E8
DWORD callAddr = (MESSAGEBOXADDR - (pOptionHeader_2->ImageBase + ((DWORD)(codeBegin + 0xD) - (DWORD)pNewImageBuffer)));
*(PDWORD)(codeBegin + 9) = callAddr;
//修正E9
DWORD jmpAddr = ((pOptionHeader_2->AddressOfEntryPoint + pOptionHeader_2->ImageBase) - (pOptionHeader_2->ImageBase + ((DWORD)(codeBegin + SHELLCODELENGTH) - (DWORD)pNewImageBuffer)));
*(PDWORD)(codeBegin + 0xE) = jmpAddr;
//修改OEP
pOptionHeader_2->AddressOfEntryPoint = (DWORD)codeBegin - (DWORD)pNewImageBuffer;
//ImageBuffer->NewBuffer
size = CopyImageBufferToNewFileBuffer(pNewImageBuffer, &pNewBuffer);
printf("ImageBufferToNewFileBuffer");
if (size == 0 || !pNewBuffer)
{
printf("存盘失败");
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
return;
}
printf("ImageBufferToNewFileBuffer");
isOk = MemoryToFile(pNewBuffer, size, write_file_path);
printf("MemoryToFile");
if (isOk)
{
printf("存盘成功");
return;
}
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
return;
}