1、File-> FileBuffer
2、FileBuffer->ImageBuffer
3、判斷空閒區是否有足夠的空間儲存ShellCode程式碼
4、將NT頭到節表往上提
5、新增一個節表
6、新增屬性
7、將修改後的資料複製到一個新的緩衝區中去(pNewImageBuffer)
8、將ShellCode程式碼複製到空閒區
9、修正E8、E9
10、修正OEP(入口程式)
11、ImageBuffer->NewBuffer
12、NewBuffer->檔案
1、(看,讀取->拉伸->還原,篇)
2、(看,讀取->拉伸->還原,篇)
3、判斷空閒區是否有足夠的空間儲存ShellCode程式碼
//DOS頭地址
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
if((DWORD)pNTHeader - (DWORD)(pDosHeader->e_lfanew) < sizeof(IMAGE_SECTION_HEADER))
{
printf("沒有多餘空間");
free(pFileBuffer);
return;
}
4、將NT頭到節表往上提
memcpy((void*)((DWORD)pDosHeader + 0x40),
pNTHeader,
(DWORD)(pSectionHeader + pPEHeader->NumberOfSections) - (DWORD)pNTHeader);
5、新增一個節表
pDosHeader->e_lfanew = 0x40;
//DOS頭地址
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//新增一個新的節表
memcpy((void*)(DWORD)(pSectionHeader + pPEHeader->NumberOfSections),(const void *)(DWORD)pSectionHeader,sizeof(PIMAGE_SECTION_HEADER));
//修改PE頭中的節表數量
pPEHeader->NumberOfSections = pPEHeader->NumberOfSections + 1;
6、新增屬性
//修改新增節表的屬性
char* s = ".tttt";
memcpy((pSectionHeader + pPEHeader->NumberOfSections - 1)->Name,s,strlen(s) + 1);
(pSectionHeader + pPEHeader->NumberOfSections - 1)->Misc.VirtualSize = pOptionHeader->SectionAlignment;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->VirtualAddress = pOptionHeader->SizeOfImage;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->SizeOfRawData = pOptionHeader->SectionAlignment;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->PointerToRawData = Size;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->Characteristics = 0x60000020;
//修改SizeOfImage的大小
pOptionHeader->SizeOfImage = pOptionHeader->SizeOfImage + pOptionHeader->SectionAlignment;
7、將修改後的資料複製到一個新的緩衝區中去
//在原有資料的最後,新增一個節的資料(記憶體對齊的整數倍)
pNewImageBuffer = malloc(pOptionHeader->SizeOfImage);
memset(pNewImageBuffer,0,pOptionHeader->SizeOfImage);
memcpy(pNewImageBuffer,pImageBuffer,pOptionHeader->SizeOfImage - pOptionHeader->SectionAlignment);
8、將ShellCode程式碼複製到空閒區
//DOS頭地址
pDosHeader = (PIMAGE_DOS_HEADER)pNewImageBuffer;
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pNewImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//將程式碼複製到空閒區
CodeBegin = (PBYTE)((DWORD)pNewImageBuffer + (DWORD)(pSectionHeader + pPEHeader->NumberOfSections - 1)->VirtualAddress);
memcpy(CodeBegin,ShellCode,SHELLCODELENGTH);
9、修正E8、E9
//修正E8
DWORD CallAddr = (MESSAGEBOXADDR - (pOptionHeader->ImageBase + ((DWORD)(CodeBegin + 0xD) - (DWORD)pNewImageBuffer)));
*(PDWORD)(CodeBegin + 9) = CallAddr;
//修正E9
DWORD JmpAddr = (pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - (pOptionHeader->ImageBase + (pSectionHeader + pPEHeader->NumberOfSections - 1)->VirtualAddress + SHELLCODELENGTH);
*(PDWORD)(CodeBegin + 0xE) = JmpAddr;
10、修正OEP(入口程式)
//修改OEP
pOptionHeader->AddressOfEntryPoint = (DWORD)CodeBegin - (DWORD)pNewImageBuffer;
11、ImageBuffer->NewBuffer(看,讀取->拉伸->還原,篇)
12、NewBuffer->檔案(看,讀取->拉伸->還原,篇)
#include "stdafx.h"
#include <windows.h>
#include "stdlib.h"
#include "stdio.h"
#define FILEPATH_IN "C:/fg.exe"
#define FILEPATH_OUT "C:/copyxx.exe"
#define SHELLCODELENGTH 0x12
#define MESSAGEBOXADDR 0x77D5050B
BYTE ShellCode[] =
{
0x6A,00,0x6A,00,0x6A,00,0x6A,00,
0xE8,00,00,00,00,
0xE9,00,00,00,00
};
DWORD ReadPEFile(IN LPSTR lpszFile,OUT LPVOID* pFileBuffer )
{
FILE *pFile = NULL;
DWORD fileSize = 0; //檔案大小
LPVOID pTempFileBuffer = NULL; //緩衝區首地址
pFile = fopen(lpszFile,"rb"); //開啟檔案
if(!pFile)
{
printf("開啟檔案失敗");
return NULL;
}
//讀取檔案大小
fseek(pFile,0,SEEK_END); //將指標從開始的位置移動到末尾
fileSize = ftell(pFile); //獲取資料大小
//分配緩衝區(申請記憶體)
pTempFileBuffer = malloc(fileSize);
if(!pTempFileBuffer)
{
printf("分配空間失敗");
fclose(pFile);
return NULL;
}
//將檔案資料讀取到緩衝區
fseek(pFile,0,SEEK_SET); //將指標指向開始
size_t n = fread(pTempFileBuffer,fileSize,1,pFile); //將資料讀取到緩衝區中
if(!n)
{
printf("讀取資料失敗");
free(pTempFileBuffer); //釋放記憶體
fclose(pFile); //關閉檔案
return NULL;
}
//關閉檔案
*pFileBuffer = pTempFileBuffer;
pTempFileBuffer = NULL;
fclose(pFile); //關閉檔案
return fileSize;
}
DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer,OUT LPVOID* pImageBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL; //用來接收DOS頭資訊
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTemImageBuffer = NULL;
//判斷傳入的值是否有效
if(!pFileBuffer)
{
printf("緩衝區指標無效");
return 0;
}
//判斷是否為PE檔案
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ頭\n");
return 0;
}
//獲取頭資訊
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if(*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE標誌\n");
return 0;
}
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//申請緩衝區大小
pTemImageBuffer = malloc(pOptionHeader->SizeOfImage);
if(!pTemImageBuffer)
{
printf("分配空間失敗");
return 0;
}
//初始化緩衝區
memset(pTemImageBuffer,0,pOptionHeader->SizeOfImage);
//根據 SizeOfHeaders,先拷貝頭
memcpy(pTemImageBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
//根據節表,迴圈拷貝節
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for(int i=0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTemImageBuffer + pTempSectionHeader->VirtualAddress),(void*)((DWORD)pFileBuffer + pTempSectionHeader->PointerToRawData),pTempSectionHeader->SizeOfRawData);
}
//返回資料
*pImageBuffer = pTemImageBuffer;
pTemImageBuffer = NULL;
return 0;
}
DWORD CopyImageBufferToNewBuffer(IN LPVOID pImageBuffer,OUT LPVOID* pNewBuffer)
{
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
LPVOID pTempNewBuffer = NULL;
if(!pImageBuffer)
{
printf("緩衝區指標無效");
return 0;
}
if(*((PWORD)pImageBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ頭\n");
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
if(*((PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE標誌\n");
return 0;
}
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//資料大小(最後一個節的檔案偏移 + 最後一個節的真實大小 + 檔案對齊)
DWORD FileSize = (
(pSectionHeader + pPEHeader->NumberOfSections-1)->PointerToRawData
+ (pSectionHeader + pPEHeader->NumberOfSections-1)->Misc.VirtualSize
+ pOptionHeader->FileAlignment
)&(0 - pOptionHeader->FileAlignment);
pTempNewBuffer = malloc(FileSize);
if(!pTempNewBuffer)
{
printf("分配空間失敗");
return 0;
}
//初始化緩衝區
memset(pTempNewBuffer,0,FileSize);
memcpy(pTempNewBuffer,pDosHeader,pOptionHeader->SizeOfHeaders);
//根據節表,迴圈拷貝節
PIMAGE_SECTION_HEADER pTempSectionHeader = pSectionHeader;
for(int i=0;i<pPEHeader->NumberOfSections;i++,pTempSectionHeader++)
{
memcpy((void*)((DWORD)pTempNewBuffer + pTempSectionHeader->PointerToRawData),
(void*)((DWORD)pImageBuffer + pTempSectionHeader->VirtualAddress),
pTempSectionHeader->Misc.VirtualSize);
}
*pNewBuffer = pTempNewBuffer;
pTempNewBuffer = NULL;
return FileSize;
}
BOOL MemeryTOFile(LPVOID pMemBuffer,size_t size,LPSTR lpszFile)
{
FILE *fp = NULL;
fp = fopen(lpszFile,"wb");
if(!fp)
{
return FALSE;
}
fwrite(pMemBuffer,size,1,fp); //向磁碟寫入資料
fclose(fp); //關閉檔案
fp = NULL;
return TRUE;
}
VOID Test()
{
DWORD Size = 0; //用來接收資料大小
BOOL isok = FALSE; //用來接收寫入磁碟是否成功
PBYTE CodeBegin = NULL;
LPVOID pFileBuffer = NULL; //用來接收緩衝區的首地址
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
LPVOID pNewImageBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
//File-> FileBuffer
Size = ReadPEFile(FILEPATH_IN,&pFileBuffer); //呼叫函數讀取檔案資料
if(!pFileBuffer || !Size)
{
printf("File-> FileBuffer失敗");
return;
}
//FileBuffer->ImageBuffer
CopyFileBufferToImageBuffer(pFileBuffer,&pImageBuffer);
if(!pImageBuffer)
{
printf("FileBuffer->ImageBuffer失敗");
free(pFileBuffer);
return;
}
//DOS頭地址
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
if((DWORD)pNTHeader - (DWORD)(pDosHeader->e_lfanew) < sizeof(IMAGE_SECTION_HEADER))
{
printf("沒有多餘空間");
free(pFileBuffer);
return;
}
memcpy((void*)((DWORD)pDosHeader + 0x40),pNTHeader,(DWORD)(pSectionHeader + pPEHeader->NumberOfSections) - (DWORD)pNTHeader);
pDosHeader->e_lfanew = 0x40;
//DOS頭地址
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//新增一個新的節表
memcpy((void*)(DWORD)(pSectionHeader + pPEHeader->NumberOfSections),(const void *)(DWORD)pSectionHeader,sizeof(PIMAGE_SECTION_HEADER));
//修改PE頭中的節表數量
pPEHeader->NumberOfSections = pPEHeader->NumberOfSections + 1;
//修改新增節表的屬性
char* s = ".tttt";
memcpy((pSectionHeader + pPEHeader->NumberOfSections - 1)->Name,s,strlen(s) + 1);
(pSectionHeader + pPEHeader->NumberOfSections - 1)->Misc.VirtualSize = pOptionHeader->SectionAlignment;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->VirtualAddress = pOptionHeader->SizeOfImage;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->SizeOfRawData = pOptionHeader->SectionAlignment;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->PointerToRawData = Size;
(pSectionHeader + pPEHeader->NumberOfSections - 1)->Characteristics = 0x60000020;
//修改SizeOfImage的大小
pOptionHeader->SizeOfImage = pOptionHeader->SizeOfImage + pOptionHeader->SectionAlignment;
//在原有資料的最後,新增一個節的資料(記憶體對齊的整數倍)
pNewImageBuffer = malloc(pOptionHeader->SizeOfImage);
memset(pNewImageBuffer,0,pOptionHeader->SizeOfImage);
memcpy(pNewImageBuffer,pImageBuffer,pOptionHeader->SizeOfImage - pOptionHeader->SectionAlignment);
//DOS頭地址
pDosHeader = (PIMAGE_DOS_HEADER)pNewImageBuffer;
//NT頭地址
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pNewImageBuffer + pDosHeader->e_lfanew);
//標準PE頭地址
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x4);
//可選PE頭地址
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
//第一個節表地址
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
//將程式碼複製到空閒區
CodeBegin = (PBYTE)((DWORD)pNewImageBuffer + (DWORD)(pSectionHeader + pPEHeader->NumberOfSections - 1)->VirtualAddress);
memcpy(CodeBegin,ShellCode,SHELLCODELENGTH);
//修正E8
DWORD CallAddr = (MESSAGEBOXADDR - (pOptionHeader->ImageBase + ((DWORD)(CodeBegin + 0xD) - (DWORD)pNewImageBuffer)));
*(PDWORD)(CodeBegin + 9) = CallAddr;
//修正E9
DWORD JmpAddr = (pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - (pOptionHeader->ImageBase + (pSectionHeader + pPEHeader->NumberOfSections - 1)->VirtualAddress + SHELLCODELENGTH);
*(PDWORD)(CodeBegin + 0xE) = JmpAddr;
//修改OEP
pOptionHeader->AddressOfEntryPoint = (DWORD)CodeBegin - (DWORD)pNewImageBuffer;
//ImageBuffer->NewBuffer
Size = CopyImageBufferToNewBuffer(pNewImageBuffer,&pNewBuffer);
if(!Size || !pNewBuffer)
{
printf("ImageBuffer->NewBuffer失敗");
free(pFileBuffer);
free(pImageBuffer);
return;
}
//NewBuffer->檔案
isok = MemeryTOFile(pNewBuffer,Size,FILEPATH_OUT);
if(isok)
{
printf("存檔成功");
return;
}
//釋放記憶體
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
}
int main(int argc, char* argv[])
{
Test();
getchar();
return 0;
}