Windows 逆向-重定位 C 代码

Oyst3r 于 2024-03-05 发布

就是写个小程序

1.项目的整体结构

2.各个部分的代码

main 函数

#include <iostream>
#include <windows.h>
#include "fuction.h"
int main()
{
    char* FilePath = (char*)"D:/my_c++project/Test_Dll/move_storehouse.dll";  //打开的PE文件绝对路径
    char* FileBufferPoint = ReadPeFile(FilePath);
    PrintReloctionTables(FileBufferPoint);
    free(FileBufferPoint);
    return 0;
}

头文件

#pragma once
int PeFileSize(char* FilePath);
char* ReadPeFile(char* FilePath);
int RvaToFoa(char* FileBufferPoint, int RVA);
void PrintReloctionTables(char* FileBufferPoint);

功能函数

#include <stdlib.h>
#include <cstdio>
#include <atomic>
#include "Fuction.h"
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned char BYTE;

#define MZ 0x5A4D
#define PE 0x4550
#define IMAGE_SIZEOF_SHORT_NAME 8

typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned char BYTE;

#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;  //特征值
};

//数据目录
struct _IMAGE_DATA_DIRECTORY {
	DWORD VirtualAddress;
	DWORD Size;
};

//可选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;  //该节特征属性
};

//重定位表
struct _IMAGE_BASE_RELOCATION {
	DWORD VirtualAddress;
	DWORD SizeOfBlock;
	//具体项
};

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;
}

int RvaToFoa(char* FileBufferPoint, int RVA) {
	//RvaToFoa:将可执行文件在内存中的地址转换为在FileBuffer中的地址
	//参数说明:
	//FileBufferPoint:指向可执行文件在FileBuffer的地址
	//返回值说明:
	//读取成功返回FOA,读取失败则返回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);

	RVA += _image_optional_header->ImageBase;
	int flag = 0;
	if (_image_section_header->VirtualAddress > RVA - _image_optional_header->ImageBase) {
		return RVA;
	}
	for (int i = 0; i < _image_file_header->NumberOfSections; i++) {
		if (RVA - _image_optional_header->ImageBase >= _image_section_header->VirtualAddress && RVA - _image_optional_header->ImageBase < _image_section_header->VirtualAddress + _image_section_header->Misc.VirtualSize) {
			flag = 1;
			break;
		}
		else {
			_image_section_header++;
		}
	}
	if (flag == 0) {
		return 0;
	}
	int TempAddress = RVA - _image_optional_header->ImageBase - _image_section_header->VirtualAddress;
	return _image_section_header->PointerToRawData + TempAddress;
}

void PrintReloctionTables(char* FileBufferPoint) {
	//PrintReloctionTables:打印出重定位表的内容
	//参数说明:
	//FileBufferPoint:指向可执行文件在FileBuffer的地址
	//返回值说明:
	//没有返回值,直接打印结果

	_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_DATA_DIRECTORY* _image_data_directory = NULL;
	_IMAGE_BASE_RELOCATION* _image_base_relocation = 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_data_directory = (_IMAGE_DATA_DIRECTORY*)(_image_optional_header->DataDirectory+5);
	_image_section_header = (_IMAGE_SECTION_HEADER*)((char*)_image_optional_header + _image_file_header->SizeOfOptionalHeader);

	if (_image_data_directory->VirtualAddress == 0) {
		printf("该二进制文件没有重定位表");
		getchar();
		exit(0);
	}

	_image_base_relocation = (_IMAGE_BASE_RELOCATION*)(RvaToFoa(FileBufferPoint, _image_data_directory->VirtualAddress) + (DWORD)FileBufferPoint);
	printf("***************ReloctionTables****************\n");
	for (int i = 0; _image_base_relocation->VirtualAddress != 0;i++) {
		printf("*********第%d块*********\n", i + 1);
		printf("VirtualAddress:%08X   SizeOfBlock:%08X\n", _image_base_relocation->VirtualAddress, _image_base_relocation->SizeOfBlock);

		for (int j = 0; j < (_image_base_relocation->SizeOfBlock - 8) / 2;j++) {
			if ((*((WORD*)((char*)_image_base_relocation + 8 + j * 2)) >> 12) != 0) {

				printf("%d\t%08X\n", *((WORD*)((char*)_image_base_relocation + 8 + j * 2)) >> 12, _image_base_relocation->VirtualAddress + (*((WORD*)((BYTE*)_image_base_relocation + 8 + j * 2)) & 0x0FFF));

			}
		}
		_image_base_relocation = (_IMAGE_BASE_RELOCATION*)((char*)_image_base_relocation + _image_base_relocation->SizeOfBlock);
	}

}

3.运行验证,与 Lord PE 保持一致,成功