//This code work fine when compiled with `cl.exe /TP`,
//but the address of __invoke__start is wrong under debug mode,
//because at the address,it is actually a JMP instruction,
//not the code of the function
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <assert.h>
//*************
#define RAW_SECTION_ALIGNMENT 0x200
//*************
//RAW_BASE_CODE and RAW_SIZE_CODE *must* be
//a multiple of RAW_SECTION_ALIGNMENT which was defined above
//注意,RAW_BASE_CODE不能太小了,以免PE文件头的内容被覆盖
#define RAW_BASE_CODE 0x200
#define RAW_SIZE_CODE 0x200
//RAW_BASE_RDATA and RAW_SIZE_RDATA *must* be
//a multiple of RAW_SECTION_ALIGNMENT which was defined above
#define RAW_BASE_RDATA (RAW_BASE_CODE + RAW_SIZE_CODE)
#define RAW_SIZE_RDATA 0x200
//RVA_BASE_CODE 和 RVA_BASE_RDATA都必须按页边界对齐。
//同时我也把节对齐的值设为了一个页面大小(4096k即0x1000)
//这里我没有把内存中的节对齐大小放在头部的宏定义里,
//因为它只能被设置成页面大小的整数倍,而如果设置多了也不会有什么性能提升
#define RVA_BASE_CODE 0x1000
#define RVA_BASE_RDATA 0x2000
/*
* 每一个 IMPORT_ENTRY 对应于一个dll文件。
* 一个 IMPORT_ENTRY 指向一个 IAT 表,
* 每一个 IAT 表在遇到一个全零的 IAT 项时结束
*/
#define IAT_SIZE (2 * sizeof(DWORD))
#define OFFSET_IAT 0
#define RVA_IAT_ENTRY (RVA_BASE_RDATA + OFFSET_IAT)
/*
* 每一个 IMPORT_ENTRY 项有0x14个字节,
* (也即5个DWORD数据)
* 以一个全零的 IMPORT_ENTRY 项结尾
*/
#define IMPORT_SIZE (2 * sizeof(IMAGE_IMPORT_DESCRIPTOR)) //这里有2个IMPORT_ENTRY项
#define OFFSET_IMPORT 0x08
#define RVA_IMPORT_ENTRY (OFFSET_IMPORT + RVA_BASE_RDATA)
#define CODE_ENTRY_POINT_OFFSET 0
#define CODE_ENTRY_POINT (RVA_BASE_CODE + CODE_ENTRY_POINT_OFFSET)
#define EXEC_IMAGE_BASE 0x00400000
void __invoke__start()
{
void (__stdcall *MsgBox)(DWORD,DWORD,DWORD,DWORD);
MsgBox = (void (__stdcall*)(DWORD,DWORD,DWORD,DWORD))(
(//这里取IAT偏移为0的那个函数指针
(void**)(EXEC_IMAGE_BASE + RVA_IAT_ENTRY)
)[0]
);
MsgBox(0,0,0,0);
}
void __invoke__end(){}
void construct_file(FILE *fpExec)
{
{
IMAGE_DOS_HEADER *pDos;
pDos = (IMAGE_DOS_HEADER*)malloc(sizeof(IMAGE_DOS_HEADER));
memset(pDos,0,sizeof *pDos);
memcpy(&pDos ->e_magic,"MZ",2);
pDos ->e_lfanew = sizeof *pDos;
fwrite(pDos,1,sizeof *pDos,fpExec);
free(pDos);
}
{
IMAGE_NT_HEADERS *pNtHeader;
pNtHeader = (IMAGE_NT_HEADERS*)malloc(sizeof(IMAGE_NT_HEADERS));
memset(pNtHeader,0,sizeof *pNtHeader);
memcpy(pNtHeader,"PE\0\0",4);
IMAGE_FILE_HEADER *pFileHeader = &pNtHeader ->FileHeader;
pFileHeader ->Machine = IMAGE_FILE_MACHINE_I386;
pFileHeader ->NumberOfSections = 2;
pFileHeader ->SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER);
pFileHeader ->Characteristics = 0x010F;
IMAGE_OPTIONAL_HEADER *pOptionalHeader;
pOptionalHeader = &pNtHeader ->OptionalHeader;
pOptionalHeader ->Magic = 0x010B;
pOptionalHeader ->MajorLinkerVersion = 5;
pOptionalHeader ->MinorLinkerVersion = 12;
pOptionalHeader ->SizeOfCode = RAW_SIZE_CODE;
pOptionalHeader ->SizeOfInitializedData = RAW_SIZE_RDATA;
//基地址被设置成0x00400000,于是RVA 0x1000被映射到物理地址0x4001000
pOptionalHeader ->AddressOfEntryPoint = CODE_ENTRY_POINT;
pOptionalHeader ->BaseOfCode = RVA_BASE_CODE;
pOptionalHeader ->BaseOfData = RVA_BASE_RDATA;
pOptionalHeader ->ImageBase = EXEC_IMAGE_BASE;
pOptionalHeader ->SectionAlignment = 0x1000;
pOptionalHeader ->FileAlignment = RAW_SECTION_ALIGNMENT;
pOptionalHeader ->MajorOperatingSystemVersion = 4;
pOptionalHeader ->MajorSubsystemVersion = 4;
//头部可以被认为是一个节,所以3个对齐到SectionAlignment的0x1000加起来就是0x3000
//如果SectionAlignment的值修改了,此处也需要被修改
pOptionalHeader ->SizeOfImage = 0x3000;
//如果RAW_SECTION_ALIGNMENT被改成大于0x200的值,那么此处也需要被修改
pOptionalHeader ->SizeOfHeaders = 0x200; //向OptionalHeader ->FileAlignment对齐
pOptionalHeader ->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
pOptionalHeader ->SizeOfStackReserve = 0x100000;
pOptionalHeader ->SizeOfStackCommit = 0x1000;
pOptionalHeader ->SizeOfHeapReserve = 0x100000;
pOptionalHeader ->SizeOfHeapCommit = 0x1000;
pOptionalHeader ->NumberOfRvaAndSizes = 0x10;
IMAGE_DATA_DIRECTORY *pDataDirectory = pOptionalHeader ->DataDirectory;
//导入函数表位于RVA 0x2000,导入地址表位于RVA 0x2008
//它们都在第二个节中
//而代码位于第一个节
pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = RVA_IMPORT_ENTRY;
//0x2008实际上指向一个IMAGE_IMPORT_DESCRIPTOR
pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = IMPORT_SIZE;
pDataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = RVA_IAT_ENTRY;
pDataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = IAT_SIZE;
//.....
fwrite(pNtHeader,1,sizeof(IMAGE_NT_HEADERS),fpExec);
free(pNtHeader);
}
{
IMAGE_SECTION_HEADER *pSectionHeaders;
pSectionHeaders = (IMAGE_SECTION_HEADER*)malloc(2 * sizeof(IMAGE_SECTION_HEADER));
memset(pSectionHeaders,0,2 * sizeof(IMAGE_SECTION_HEADER));
strcpy((char*)pSectionHeaders[0].Name,".text");
pSectionHeaders[0].Misc.VirtualSize =
(char*)__invoke__end - (char*)__invoke__start;//紧缩的
pSectionHeaders[0].VirtualAddress = RVA_BASE_CODE;
pSectionHeaders[0].SizeOfRawData = RAW_SIZE_CODE;
pSectionHeaders[0].PointerToRawData = RAW_BASE_CODE;
pSectionHeaders[0].Characteristics =
IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ;
strcpy((char*)pSectionHeaders[1].Name,".rdata");
//没紧缩.rdata节,方便后面修改,例如增加IAT地址
pSectionHeaders[1] .Misc.VirtualSize = RAW_SIZE_RDATA;
pSectionHeaders[1] .VirtualAddress = RVA_BASE_RDATA;
pSectionHeaders[1] .SizeOfRawData = RAW_SIZE_RDATA;
pSectionHeaders[1] .PointerToRawData = RAW_BASE_RDATA;
pSectionHeaders[1] .Characteristics =
IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
fwrite(pSectionHeaders,1,2 * sizeof(IMAGE_SECTION_HEADER),fpExec);
free(pSectionHeaders);
while(ftell(fpExec) != RAW_BASE_CODE)fputc(0,fpExec);
}
{
char *code = (char*)malloc(RAW_SIZE_CODE);
memset(code,0,RAW_SIZE_CODE);
memcpy(code,__invoke__start,RAW_SIZE_CODE);
assert(RAW_SIZE_CODE >= (char*)__invoke__end - (char*)__invoke__start);
fwrite(code,1,RAW_SIZE_CODE,fpExec);
free(code);
}
{
char *pRData = (char*)malloc(RAW_SIZE_RDATA);
memset(pRData,0,RAW_SIZE_RDATA);
assert(OFFSET_IMPORT >= OFFSET_IAT + IAT_SIZE);
const DWORD offsetThunk = 0x30;
assert(offsetThunk >= OFFSET_IMPORT + IMPORT_SIZE);
const DWORD offsetFunctionName = 0x38;
assert(offsetFunctionName >= offsetThunk + IAT_SIZE);
const DWORD offsetDllName = 0x46;
IMAGE_IMPORT_DESCRIPTOR *desc;
desc = (IMAGE_IMPORT_DESCRIPTOR*)(pRData + OFFSET_IMPORT);
//有2份thunk表,
//一份实际上是IAT表我放在偏移OFFSET_IAT,
//另一份位于偏移offsetThunk
desc ->OriginalFirstThunk = RVA_BASE_RDATA + offsetThunk;
IMAGE_THUNK_DATA *thunk;
thunk = (IMAGE_THUNK_DATA*)(pRData + offsetThunk);
thunk ->u1.Function = RVA_BASE_RDATA + offsetFunctionName;
//描述符的FirstThunk指向另一份同样的thunk表
//实际上就是IAT,目标文件在被操作系统执行的时候,
//IAT表的那些指向IMAGE_IMPORT_BY_NAME的指针被替换成函数指针
assert(RVA_IAT_ENTRY == RVA_BASE_RDATA + OFFSET_IAT);
desc ->FirstThunk = (DWORD)RVA_IAT_ENTRY;
thunk = (IMAGE_THUNK_DATA*)(pRData + OFFSET_IAT);
thunk ->u1.Function = RVA_BASE_RDATA + offsetFunctionName;
IMAGE_IMPORT_BY_NAME *imgFunctionName;
imgFunctionName = (IMAGE_IMPORT_BY_NAME*)(pRData + offsetFunctionName);
strcpy((char*)imgFunctionName ->Name,"MessageBoxA");
//注意,下面的sizeof(imgFunctionName ->Hint),
//最好不要写成sizeof(IMAGE_IMPORT_BY_NAME),
//因为那个一字节的Name会因为对齐的需要,
//实际上使得整个sizeof(IMAGE_IMPORT_BY_NAME)为4字节
assert(offsetDllName >=
offsetFunctionName +
sizeof(imgFunctionName ->Hint) +
strlen((char*)imgFunctionName ->Name) + 1);
desc ->Name = RVA_BASE_RDATA + offsetDllName;
strcpy(pRData + offsetDllName,"user32.dll");
fwrite(pRData,1,RAW_SIZE_RDATA,fpExec);
free(pRData);
}
}
int main(int argc, char *argv[])
{
char file[MAX_PATH];
strncpy(file,argv[0],MAX_PATH);
if(!strchr(file,'.'))
{
strcat(file,".exe");
}
FILE *fp = fopen(file,"rb");
if(!fp)
{
perror("");
return -1;
}
fseek(fp,0,SEEK_END);
printf("file size %d \n",ftell(fp));
fclose(fp);
fp = NULL;
strcat(file,".mod.exe");
FILE *fpExec = fopen(file,"wb");
if(!fpExec)
{
perror("");
return -1;
}
construct_file(fpExec);
fclose(fpExec);
return 0;
}