这个就是把上篇文章作业,以及前几天的作业全部都写出来了,可能现在还差一个 PE 解释器了,这个比起下面这个就很简单了,之前也在 VC6 写过,过几天心情好给复制一下,先给大家看一下这个模拟可执行文件加载过程代码的效果
一开始我是写成上面这种,发现根本不行一直报错,这个好像只能都是 D 盘的东西才行(因为我的 vs 放在了 D 盘),其他盘的看反汇编确实一开始读文件路径下的程序都读不出来,所以改成了下面这个,然后就正常了,嗯嗯那个要强转一下,vs 编译器必须要强转,要不编译过不了
然后可能会碰到这个报错,下面是解决办法
基本上就没什么大问题了,下面是成功的效果
这两个都是能打开正常运行的,但确实为啥变大了,但用二进制编辑器打开,前面那些伸缩的情况应该都正常,问题就是这个 FakeNotePad.exe 就是在结束的时候多了好多 0x00,暂时还不懂原因
用 PE 解释器看了一下,说是有附加数据
OKK 废话不多说,上代码了
1.首先是 main()函数的代码
#include <iostream>
#include "PeFuction.h"
int main()
{
char* FilePath = (char*)"D:/PETool 1.0.0.5.exe"; //打开的PE文件绝对路径
char* SavePath = (char*)"D:/FakeNotePad.exe"; //保存的路径
char* FileBufferPoint = ReadPeFile(FilePath);
char* ImageBufferPoint = CopyFileBufferToImageBuffer(FileBufferPoint);
char* NewBufferPoint = CopyImageBufferToNewBuffer(ImageBufferPoint);
int flag = MemeryToFile(NewBufferPoint , SavePath);
if (flag) {
printf("全部成功,程序已在对应路径生成");
}
else {
printf("失败,再检查检查");
}
free(FileBufferPoint);
free(ImageBufferPoint);
free(NewBufferPoint);
return 0;
}
2.然后是 PeFuction.h 头文件
#pragma once
int PeFileSize(char* FilePath);
char* ReadPeFile(char* FilePath);
char* CopyFileBufferToImageBuffer(char* FileBufferPoint);
char* CopyImageBufferToNewBuffer(char* ImageBufferPoint);
int MemeryToFile(char* NewBufferPoint, char* SavePath);
3.最后就是主角—PeFuction.c 的代码
#include "PeFuction.h"
#include <cstdio>
#include <atomic>
//这个模块里面写的就是PE所要用到功能的集合,统一写在了这里
//给变量换个名字,写起来更方便一点
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;
//这个就是一些PE里面固定的值
#define MZ 0x5A4D
#define PE 0x4550
#define IMAGE_SIZEOF_SHORT_NAME 8
//DOS头
struct _IMAGE_DOS_HEADER {
WORD e_magic; //MZ标记
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
DWORD e_lfanew; //PE文件真正开始的偏移地址
};
//标准PE头
struct _IMAGE_FILE_HEADER {
WORD Machine; //文件运行平台
WORD NumberOfSections; //节数量
DWORD TimeDateStamp; //时间戳
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader; //可选PE头大小
WORD Characteristics; //特征值
};
//可选PE头
struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; //文件类型
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; //代码节文件对齐后的大小
DWORD SizeOfInitializedData; //初始化数据文件对齐后的大小
DWORD SizeOfUninitializedData; //未初始化数据文件对齐后大小
DWORD AddressOfEntryPoint; //程序入口点(偏移量)
DWORD BaseOfCode; //代码基址
DWORD BaseOfData; //数据基址
DWORD ImageBase; //内存镜像基址
DWORD SectionAlignment; //内存对齐粒度
DWORD FileAlignment; //文件对齐粒度
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; //文件装入虚拟内存后大小
DWORD SizeOfHeaders; //DOS、NT头和节表大小
DWORD CheckSum; //校验和
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve; //预留堆栈大小
DWORD SizeOfStackCommit; //实际分配堆栈大小
DWORD SizeOfHeapReserve; //预留堆大小
DWORD SizeOfHeapCommit; //实际分配堆大小
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes; //目录项数目
//_IMAGE_DATA_DIRECTORY DataDirectory[16]; //这个先不管
};
//NT头
struct _IMAGE_NT_HEADERS {
DWORD Signature; //PE签名,这个在宏定义里面已经说明
_IMAGE_FILE_HEADER FileHeader;
_IMAGE_OPTIONAL_HEADER OptionalHeader;
};
//节表
struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节表名
union {
DWORD PhysicalAddress;
DWORD VirtualSize; //内存中未对齐大小
}Misc;
DWORD VirtualAddress; //该节在内存中偏移地址
DWORD SizeOfRawData; //该节在硬盘上文件对齐后大小
DWORD PointerToRawData; //该节在硬盘上文件对齐后偏移地址
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //该节特征属性
};
//OK至此所有用到的结构体就都定义完了,这个海东老师的课件里面都有,气死但他不给代码
int PeFileSize(char* FilePath) {
//PeFileSize:计算文件在硬盘上的大小
//参数说明:
//FilePath:指向文件的绝对路径
//返回值说明:
//读取成功返回文件在硬盘上的大小,读取失败则返回0
FILE* pf = fopen(FilePath, "rb");
if (pf == NULL) {
perror("打开文件错误");
fclose(pf);
return 0;
}
fseek(pf, 0, 2);
int length = ftell(pf);
fseek(pf, 0, 0);
fclose(pf);
printf("已经成功读取该文件的大小\n");
return length;
}
char* ReadPeFile(char* FilePath) {
//ReadPeFile:将可执行文件从硬盘读取到FileBuffer
//参数说明:
//FilePath:指向文件的绝对路径
//返回值说明:
//读取成功返回FileBuffer的首地址,读取失败则返回0
FILE* pf = fopen(FilePath, "rb");
if (pf == NULL) {
perror("打开文件错误");
fclose(pf);
return 0;
}
int length = PeFileSize(FilePath);
char* ptr_1 = (char*)malloc(sizeof(char)*length);
if (ptr_1 == NULL) {
perror("File堆内存分配失败");
fclose(pf);
return 0;
}
memset(ptr_1 ,0 , sizeof(char) * length);
int flag = fread(ptr_1,length,1,pf);
if (flag == NULL) {
perror("读取数据失败,请检查文件路径");
fclose(pf);
free(ptr_1);
return 0;
}
fclose(pf);
//这里之所以没有free(ptr),原因是咱们下面还要用到这块堆的内存,所以可以在main函数结束之前释放掉就行
printf("已成功将可执行文件从硬盘读取到FileBuffer\n");
return ptr_1;
}
char* CopyFileBufferToImageBuffer(char* FileBufferPoint) {
//CopyFileBufferToImageBuffer:将可执行文件从FileBuffer读取到ImageBuffer
//参数说明:
//FileBufferPoint:指向可执行文件在FileBuffer的地址
//返回值说明:
//读取成功返回ImageBuffer的首地址,读取失败则返回0
_IMAGE_DOS_HEADER* _image_dos_header = NULL;
_IMAGE_FILE_HEADER* _image_file_header = NULL;
_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
_IMAGE_SECTION_HEADER* _image_section_header = NULL;
_image_dos_header = (_IMAGE_DOS_HEADER*)FileBufferPoint;
//下面这个别忘记了还有一个PE标记的大小,为4个字节
_image_file_header = (_IMAGE_FILE_HEADER*)(FileBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
int size = _image_optional_header->SizeOfImage;
char* ptr_2 = (char*)malloc(size);
if (ptr_2 == NULL) {
perror("Image堆内存分配失败");
return 0;
}
memset(ptr_2 ,0 ,size);
for (int i = 0; i < _image_optional_header->SizeOfHeaders;i++) {
*(ptr_2 + i) = *(FileBufferPoint + i);
}
for (int i = 0; i < _image_file_header->NumberOfSections;i++ ) {
char* temp_1 = FileBufferPoint + _image_section_header->PointerToRawData;
char* temp_2 = ptr_2 + _image_section_header->VirtualAddress;
for (int j = 0; j < _image_section_header->SizeOfRawData; j++) {
*(temp_2 + j) = *(temp_1 + j);
}
_image_section_header++;
}
printf("已成功将可执行文件从FileBuffer读取到ImageBuffer\n");
return ptr_2;
}
char* CopyImageBufferToNewBuffer(char* ImageBufferPoint) {
//CopyImageBufferToNewBuffer:将可执行文件从ImageBuffer读取到NewBuffer(其实也就是FileBuffer)
//参数说明:
//ImageBufferPoint:指向可执行文件在ImageBuffer的地址
//返回值说明:
//读取成功返回NewBuffer(其实也就是FileBuffer)的首地址,读取失败则返回0
_IMAGE_DOS_HEADER* _image_dos_header = NULL;
_IMAGE_FILE_HEADER* _image_file_header = NULL;
_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
_IMAGE_SECTION_HEADER* _image_section_header = NULL;
_image_dos_header = (_IMAGE_DOS_HEADER*)ImageBufferPoint;
//下面这个别忘记了还有一个PE标记的大小,为4个字节
_image_file_header = (_IMAGE_FILE_HEADER*)(ImageBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
int ImageSectionSize = 0;
_IMAGE_SECTION_HEADER* _image_section_header_temp = _image_section_header;
for (int i = 0; i < _image_file_header->NumberOfSections;i++) {
ImageSectionSize += _image_section_header_temp->PointerToRawData;
_image_section_header_temp++;
}
char* ptr_3 = (char*)malloc(_image_optional_header->SizeOfHeaders + ImageSectionSize);
if (ptr_3 == NULL) {
perror("NewBuffer堆内存分配失败");
return 0;
}
memset(ptr_3, 0, _image_optional_header->SizeOfHeaders + ImageSectionSize);
for (int i = 0; i < _image_optional_header->SizeOfHeaders; i++) {
*(ptr_3 + i) = *(ImageBufferPoint + i);
}
for (int i = 0; i < _image_file_header->NumberOfSections;i++) {
char* temp_1 = ImageBufferPoint + _image_section_header->VirtualAddress;
char* temp_2 = ptr_3 + _image_section_header->PointerToRawData;
for (int j = 0; j < _image_section_header->SizeOfRawData; j++) {
*(temp_2 + j) = *(temp_1 + j);
}
_image_section_header++;
}
printf("已成功将可执行文件从ImageBuffer读取到NewBuffer\n");
return ptr_3;
}
int MemeryToFile(char* NewBufferPoint ,char*SavePath) {
//MemeryTOFile:将可执行文件从NewBuffer读取到硬盘
//参数说明:
//NewBufferPoint:指向NewBuffer的首地址
//SavePath:指向另存为文件的绝对路径
//返回值说明:
//读取成功返回1,读取失败则返回0
_IMAGE_DOS_HEADER* _image_dos_header = NULL;
_IMAGE_FILE_HEADER* _image_file_header = NULL;
_IMAGE_OPTIONAL_HEADER* _image_optional_header = NULL;
_IMAGE_SECTION_HEADER* _image_section_header = NULL;
_image_dos_header = (_IMAGE_DOS_HEADER*)NewBufferPoint;
//下面这个别忘记了还有一个PE标记的大小,为4个字节
_image_file_header = (_IMAGE_FILE_HEADER*)(NewBufferPoint + _image_dos_header->e_lfanew + sizeof(PE));
_image_optional_header = (_IMAGE_OPTIONAL_HEADER*)((char*)_image_file_header + 20);
_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);
int ImageSectionSize = 0;
_IMAGE_SECTION_HEADER* _image_section_header_temp = _image_section_header;
for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
ImageSectionSize += _image_section_header_temp->PointerToRawData;
_image_section_header_temp++;
}
int size = _image_optional_header->SizeOfHeaders + ImageSectionSize;
FILE* pf = fopen(SavePath ,"wb");
if (pf == NULL) {
perror("打开文件错误");
fclose(pf);
return 0;
}
int flag = fwrite(NewBufferPoint, size, 1, pf);
if (flag == NULL) {
perror("存文件出现错误,请检查文件路径是否有效");
free(NewBufferPoint);
fclose(pf);
return 0;
}
fclose(pf);
return 1;
}
建议大家也手写一遍,不会的可以看 me 的,加油加油加油!!!