PE结构详细教程
文章目录
c语言基础
一级指针
#include<stdio.h>
int main() {
char n[] = "abcdefghijklmn";
char* N = &n;
*(N+1) = 'p';
printf("%x\n",&n);
printf("%p\n", N);
printf("%p\n", &N);
printf("%p\n", &n[1]);
printf("%p\n", ( & n[0] + sizeof(char)));
printf("%c\n", *( & n[0] + sizeof(char)));
printf("%c\n", n[0]);
printf("%c\n", *N);
return 0;
}
d8fdf8
00D8FDF8
00D8FDEC
00D8FDF9
00D8FDF9
p
a
a
d8fdf8:n的地址
00D8FDF8:N指向的地址
00D8FDEC:N的地址
00D8FDF9:n[1]的地址(char类型一个字节)
00D8FDF9:n[0]的地址+一个char大小的地址,即n[1]
p:*( & n[0] + sizeof(char)),取出(n[0]地址+一个char大小的地址)地址的值,这里如果不用&n[0]而是用&n则不能得到n[1],这里&n+1的+1默认加的是sizeof(n)
验证如下
#include<stdio.h>
int main() {
char n[] = "abcde";
printf("%p\n", &n);
printf("%p\n", &n[0]);
printf("%p\n", &n[1]);
printf("%p\n", (&n[0] + sizeof(char)));
printf("%p\n", ((& n) + sizeof(char)));
printf("%p\n", ((& n) + 1));
printf("%d\n",sizeof(n));
printf("%p\n", (&n + sizeof(n)));
printf("%c\n", *(&n[0] + sizeof(char)));
printf("%c\n", *(&n + sizeof(char)));
printf("%c\n", n[1]);
return 0;
}
010FFC70
010FFC70
010FFC71
010FFC71
010FFC76
010FFC76
6
010FFC94(010FFC70+024),即+6*6
b
v
b
二(多)级指针
#include<stdio.h>
int main() {
int a = 100;
int* p1 = &a;
int** p2 = &p1;
return 0;
}
#include <stdio.h>
int main() {
int a = 100;
int* p1 = &a;
int** p2 = &p1;
int*** p3 = &p2;
printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3);
printf("&p2 = %#X, p3 = %#X\n", &p2, p3);
printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);
printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);
return 0;
}
100, 100, 100, 100
&p2 = 0XB8F70C, p3 = 0XB8F70C
&p1 = 0XB8F718, p2 = 0XB8F718, *p3 = 0XB8F718
&a = 0XB8F724, p1 = 0XB8F724, *p2 = 0XB8F724, **p3 = 0XB8F724
函数指针
#include <stdio.h>
int maxValue(int a, int b) {
return a > b ? a : b;
}
int main() {
int (*p)(int, int); //定义一个与maxValue兼容的指针
p = maxValue;
printf("%d", p(20, 45)); //通过指针调用
}
函数指针,即指向函数的指针
内联汇编
#include<stdio.h>
int add(int a, int b) {
int c = a;
int d = b;
int ret = c + d;
return ret;
}
int main()
{
char* p;
int k = 0;
p = &add;
__asm {
push 4
push 7
call p
add esp,8
mov k,eax
}
printf("%d\n", k);
__asm {
sub esp, 4
mov dword ptr[esp], 8
push 7
call p
add esp, 8
mov k, eax
}
printf("%d", k);
system("pause");
return 0;
}
11
15
数组指针
指向数组的指针
#include <stdio.h>
int main()
{
char(*p)[6]; //定义了指向含有4个元素的一维数组的指针
char a[3][4] = { 'a','b','c','d','e','f','g','h','i','j','k','l'};
p = a; //将二维数组的首地址赋值给p,也可是a[0]或&a[0][0]
//p++; //表示p跨过行a[0][],指向了行a[1][]
printf("%c",(*p)[4]);
return 0;
}
e
指针数组
存放指针的数组
#include <stdio.h>
const int MAX = 4;
int main()
{
const char* names[4] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};
int a = 10, b = 20, c = 22, d = 99;
int* age[4] = {&a,&b,&c,&d};
int i = 0;
for (i = 0; i < MAX; i++)
{
printf("Value of names[%d] = %s,age=%d\n", i, names[i],*age[i]);
}
return 0;
}
Value of names[0] = Zara Ali,age=10
Value of names[1] = Hina Ali,age=20
Value of names[2] = Nuha Ali,age=22
Value of names[3] = Sara Ali,age=99
字符转数字
#include <stdio.h>
int main()
{
char a = '7';
int b = a - '0';
printf("%d",b);
return 0;
}
保留位数
#include<stdio.h>
int main()
{
float b = 9.9;
printf("%3f\n", b);
printf("%30f\n", b);
printf("%3.f\n", b);
printf("%.3f\n",b);
return 0;
}
#
#include<stdio.h>
int main()
{
int a = 10;
int b = 3;
printf("%30.7f", float (a) / b);
}
9.900000
9.900000
10
9.900? 3.3333333
句柄类型
HWND是线程相关的,你可以通过HWND找到该窗口所属进程和线程
Handle 是代表系统的内核对象,如文件句柄,线程句柄,进程句柄。
系统对内核对象以链表的形式进行管理,载入到内存中的每一个内核对象都有一个线性地址,同时相对系统来说,在串列中有一个索引位置,这个索引位置就是内核对象的handle。
HINSTANCE的本质是模块基地址,他仅仅在同一进程中才有意义,跨进程的HINSTANCE是没有意义
HMODULE 是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址。
HINSTANCE 在win32下与HMODULE是相同的东西(只有在16位windows上,二者有所不同).
DLL寻找之LoadLibrary
Process Monitor
- Include
Operation is CreateFile
Operation is LoadImage
Operation is QueryOpen
Path contains .dll
processname is kk.exe
Path begins with C:\test\KKcapture
- Exclude
Result is SUCCESS
我们通过运?xxx.exe白文件对比,寻找是否存在LoadLibrary函数,如果存在,我们可以直接构造一个恶意黑DLL。反之,我们就需要劫持不存在的DLL。
如果文件允许LoadLibrary函数动态解析库的路径,那么该文件也会在当前目录中查找库DLL,我们通过将"白加黑"复制到具有写入权限的目录即可
劫持DllMain中的控制流时,没有必要枚举和满足所有需要的导出,即可能存在 DLL 没有任何导出并且只能通过 DllMain 入口点被劫持的情况
玩转PE结构
常用工具
cff explorer,x32/64dbg
基础知识
结构图
解析PE
基础知识
LPVOID 指向任意类型的指针
fseek(pFile, 0, SEEK_END);文件指针到尾部
FileSize = ftell(pFile);获取指针到开头的大小
fseek(pFile, 0, SEEK_SET);文件指针到头部
malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
ptr -- 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。
size -- 这是要读取的每个元素的大小,以字节为单位。
nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
#define IMAGE_SIZEOF_FILE_HEADER 20 :这里指的是文件头为20字节大小
VA = Image Base + RVA
RAW - PointerToRawData = RVA - ImageBase
RAW = RVA - ImageBase + PointerToRawData
SizeOfOptionalHeader:32位则E0H,64位则F0H
Magic :32位则10B,64位则20B
更多细节都在图上
完整代码
// addsection.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<Windows.h>
void myreadfile(char path[], int* filelen, char** exebuf)
{
FILE* file = NULL;
fopen_s(&file, path, "rb");
fseek(file, 0, SEEK_END);
*filelen = ftell(file);
fseek(file, 0, SEEK_SET);
char* buf = (char*)VirtualAlloc(NULL, *filelen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
fread(buf, 1, *filelen, file);
*exebuf = buf;
}
int main()
{
char exepath[] = "D:\\c_project\\c2\\Debug\\Project1.exe";
char* exebuf = NULL;
int exefilelen = 0;
myreadfile(exepath, &exefilelen, &exebuf);
PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER)exebuf;
printf("PIMAGE_DOS_HEADER->e_magic:%x\n", pIMAGE_DOS_HEADER->e_magic);
printf("PIMAGE_DOS_HEADER->e_lfanew:%x\n", pIMAGE_DOS_HEADER->e_lfanew);
PIMAGE_NT_HEADERS pIMAGE_NT_HEADERS = (PIMAGE_NT_HEADERS)(exebuf + pIMAGE_DOS_HEADER->e_lfanew);
printf("PIMAGE_NT_HEADERS->Signature:%x\n", pIMAGE_NT_HEADERS->Signature);
PIMAGE_FILE_HEADER pIMAGE_FILE_HEADER = (PIMAGE_FILE_HEADER)&pIMAGE_NT_HEADERS->FileHeader;
PIMAGE_SECTION_HEADER pPIMAGE_SECTION_HEADER = (PIMAGE_SECTION_HEADER)(pIMAGE_NT_HEADERS + 1);
pPIMAGE_SECTION_HEADER = pPIMAGE_SECTION_HEADER + pIMAGE_FILE_HEADER->NumberOfSections - 1;
printf("pPIMAGE_SECTION_HEADER->Name:%s\n", pPIMAGE_SECTION_HEADER->Name);
printf("pPIMAGE_SECTION_HEADER->Misc.VirtualSize:%x\n", pPIMAGE_SECTION_HEADER->Misc.VirtualSize);
printf("pPIMAGE_SECTION_HEADER->VirtualAddress:%x\n", pPIMAGE_SECTION_HEADER->VirtualAddress);
printf("pPIMAGE_SECTION_HEADER->SizeOfRawData:%x\n", pPIMAGE_SECTION_HEADER->SizeOfRawData);
printf("pPIMAGE_SECTION_HEADER->PointerToRawData:%x\n", pPIMAGE_SECTION_HEADER->PointerToRawData);
printf("pPIMAGE_SECTION_HEADER->Characteristics:%x\n", pPIMAGE_SECTION_HEADER->Characteristics);
}
添加节
// 关键代码
#include <iostream>
#include<Windows.h>
DWORD ArithmeticFileAlignment(DWORD FileSize)
{
DWORD myFileSize = 0;
if (FileSize % FileAlignment != 0)
{
myFileSize = ((FileSize / FileAlignment) + 1) * FileAlignment;
}
else
{
myFileSize = FileSize;
}
return myFileSize;
}
DWORD ArithmeticSectionAlignment(DWORD VirtualSize)
{
DWORD myVirtualSize = 0;
if (VirtualSize % SectionAlignment != 0)
{
myVirtualSize = ((VirtualSize / SectionAlignment) + 1) * SectionAlignment;
}
else
{
myVirtualSize = VirtualSize;
}
return myVirtualSize;
}
int main()
{
DWORD exeAlignmentVirtualSize = ArithmeticSectionAlignment(pPIMAGE_SECTION_HEADER->Misc.VirtualSize);
DWORD FileAlignmentVirtualSize = ArithmeticSectionAlignment(addfilelen);
DWORD FileAlignmentFileSize = ArithmeticFileAlignment(addfilelen);
PIMAGE_SECTION_HEADER pmyPIMAGE_SECTION_HEADER = pPIMAGE_SECTION_HEADER + 1;
memcpy(pmyPIMAGE_SECTION_HEADER->Name, "mysec", strlen("mysec"));
pmyPIMAGE_SECTION_HEADER->Misc.VirtualSize = FileAlignmentVirtualSize;
pmyPIMAGE_SECTION_HEADER->VirtualAddress = exeAlignmentVirtualSize + pPIMAGE_SECTION_HEADER->VirtualAddress;
pmyPIMAGE_SECTION_HEADER->SizeOfRawData = FileAlignmentFileSize;
pmyPIMAGE_SECTION_HEADER->PointerToRawData = pPIMAGE_SECTION_HEADER->PointerToRawData + pPIMAGE_SECTION_HEADER->SizeOfRawData;
pmyPIMAGE_SECTION_HEADER->PointerToLinenumbers = 0;
pmyPIMAGE_SECTION_HEADER->NumberOfRelocations = 0;
pmyPIMAGE_SECTION_HEADER->PointerToRelocations = 0;
pmyPIMAGE_SECTION_HEADER->NumberOfLinenumbers = 0;
pmyPIMAGE_SECTION_HEADER->Characteristics = 0x60000020;
pIMAGE_NT_HEADERS->OptionalHeader.SizeOfImage += FileAlignmentVirtualSize;
char* execopydata = (char*)VirtualAlloc(NULL, exefilelen + FileAlignmentFileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(execopydata, exebuf, exefilelen);
memcpy(execopydata + exefilelen, addfilebuf, addfilelen);
FILE* savefile = NULL;
fopen_s(&savefile, "11.exe", "wb");
fwrite(execopydata, 1, exefilelen + FileAlignmentFileSize, savefile);
fclose(savefile);
}
修改address of entry point指向新增节也可以执行shellcode,但是原文件功能失效,可以固定程序入口地址(非随机化),然后写死执行完shellcode后jmp到正常功能即可使原功能生效
IMAGE_DATA_DIRECTORY
IMAGE_DATA_DIRECTORY数据目录列表,它由16个相同的IMAGE_DATA_DIRECTORY结构组成,这16个数据目录结构定义很简单仅仅指出了某种数据的位置和长度,定义如下:
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 数据起始RVA
DWORD Size; // 数据块的长度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
地址转换
内存加载映像图
VA = Image Base + RVA
FOA=RVA-VirtualAddress+PointerToRawData 数据的文件偏移=数据RVA - 节RVA + 节的文件偏移
由于内存中第一个节无法映射到文件,因此需要对RVA进行判断是否在.textbss节之后,可以通过获取到的节的SizeOfRawData进行判断,解析第一个节的信息如下,即SizeOfRawData:0
pPIMAGE_SECTION_HEADER->Name:.textbss
pPIMAGE_SECTION_HEADER->Misc.VirtualSize:10000
pPIMAGE_SECTION_HEADER->VirtualAddress:1000
pPIMAGE_SECTION_HEADER->SizeOfRawData:0
pPIMAGE_SECTION_HEADER->PointerToRawData:0
pPIMAGE_SECTION_HEADER->Characteristics:e00000a0
同时若在0填充的块间隙中,也无法映射到文件,即RVA-VirtualAddress>SizeOfRawData
RVA->FOA
BOOL RVAtoFAfun(DWORD RVA, DWORD imagebase, PIMAGE_SECTION_HEADER pSection, int nNumofSection, DWORD* pFA)
{
int FA = 0;
for (int i = 0; i < nNumofSection; i++)
{
DWORD VirtualSizeAlignment = ArithmeticSectionAlignment(pSection[i].Misc.VirtualSize);
if (RVA >= pSection[i].VirtualAddress && RVA < pSection[i].VirtualAddress + VirtualSizeAlignment)
{
if (pSection[i].SizeOfRawData == 0)
{
FA = 0;
break;
}
if (RVA - pSection[i].VirtualAddress <= pSection[i].SizeOfRawData)
FA = RVA - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
else
FA = 0;//数据在节块的空隙中
}
}
if (FA == 0)
{
return FALSE;
}
*pFA = FA;
return TRUE;
};
导出表
导出函数
#dllmain.cpp
#include "pch.h"
#include<stdio.h>
int fun(int a) {
return a + 10;
}
#Source.def
EXPORTS
fun @8
main.cpp
#include <iostream>
#include<Windows.h>
typedef int (*p)(int);
int main() {
HMODULE dl = LoadLibraryA("D:\\c_project\\c2\\Debug\\Dll1.dll");
//p p1=(p) GetProcAddress(dl, (LPCSTR)8);
p p1 = (p)GetProcAddress(dl, "fun");
printf("%d", p1(2));
return 0;
}
导出表结构
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
GetProcAddress自实现
原生地址获取
#include<stdio.h>
#include<windows.h>
typedef int(*msg)(int a);
int main()
{
HMODULE hDll = LoadLibrary(L"Dll1.dll");
void *p=GetProcAddress(hDll,"fun");
msg func = (msg)GetProcAddress(hDll, "fun");
printf("%x\n", func);
printf("%x", p);
//运行msg函数
printf("%d",func(3));
return 0;
}
70de1177
70de117713
自定义获取
dbg调试发现将fun名字和ffff进行cmp,大于ffff则是str查找,否则为序号查找
#include<stdio.h>
#include<windows.h>
#include<string.h>
FARPROC
WINAPI
myGetProcAddress(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
) {
PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pIMAGE_NT_HEADER = (PIMAGE_NT_HEADERS)((DWORD)hModule + pIMAGE_DOS_HEADER->e_lfanew);
PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORYRVA =(PIMAGE_EXPORT_DIRECTORY) (pIMAGE_NT_HEADER->OptionalHeader.DataDirectory[0].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORY =(PIMAGE_EXPORT_DIRECTORY) ((DWORD)hModule + (DWORD)pIMAGE_EXPORT_DIRECTORYRVA);
printf("%s\n", (DWORD)(pIMAGE_EXPORT_DIRECTORY->Name)+ (DWORD)hModule);
DWORD ordRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals;
WORD* ordVA = (WORD*)(ordRVA + (DWORD)hModule);
DWORD nameRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfNames;
DWORD* nameVA =(DWORD*) (nameRVA + (DWORD)hModule);
DWORD funRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfFunctions;
DWORD* funVA = (DWORD*)(funRVA + (DWORD)hModule);
//获取到导出的函数名字,并遍历名字进行查找
for (int i = 0; i < pIMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
{
char* funname = (char*)((nameVA[i]) + (DWORD)hModule);
if (strcmp(funname, lpProcName) == 0) {
printf("find it!,ord:%d\n", pIMAGE_EXPORT_DIRECTORY->Base + ordVA[i]);
printf("%x\n", (DWORD)hModule+funVA[ordVA[i]]);
return NULL;
}
}
printf("no this function");
return NULL;
}
FARPROC
WINAPI
MyOrdinalGetProcAddress(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
)
{
if ((DWORD)lpProcName >= 0xffff)
{
myGetProcAddress(hModule, lpProcName);
}
else {
PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pIMAGE_NT_HEADER = (PIMAGE_NT_HEADERS)((DWORD)hModule + pIMAGE_DOS_HEADER->e_lfanew);
PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORYRVA = (PIMAGE_EXPORT_DIRECTORY)(pIMAGE_NT_HEADER->OptionalHeader.DataDirectory[0].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORY = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModule + (DWORD)pIMAGE_EXPORT_DIRECTORYRVA);
printf("%s\n", (DWORD)(pIMAGE_EXPORT_DIRECTORY->Name) + (DWORD)hModule);
DWORD ordRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals;
WORD* ordVA = (WORD*)(ordRVA + (DWORD)hModule);
DWORD nameRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfNames;
DWORD* nameVA = (DWORD*)(nameRVA + (DWORD)hModule);
DWORD funRVA = pIMAGE_EXPORT_DIRECTORY->AddressOfFunctions;
DWORD* funVA = (DWORD*)(funRVA + (DWORD)hModule);
DWORD base = pIMAGE_EXPORT_DIRECTORY->Base;
int ord = (DWORD)lpProcName - base;
printf("%x\n", (funVA[ord])+(DWORD)hModule);
}
}
int main()
{
HMODULE hDll = LoadLibrary(L"Dll1.dll");
void *p= MyOrdinalGetProcAddress(hDll,"fun");
void* p2 = GetProcAddress(hDll, "fun");
printf("%x\n", p2);
printf("fun2:\n");
printf("\n");
void* p3 = GetProcAddress(hDll, "fun2");
void* p4 = MyOrdinalGetProcAddress(hDll, (LPCSTR)12);
printf("%x\n", p3);
return 0;
}
Dll1.dll
find it!,ord:9
7ba31159
7ba31159
fun2:Dll1.dll
7ba312d0
7ba312d0
导入表
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32
{
union {
DWORD ForwarderString; // 转发字符串的RAV
DWORD Function; // 被导入函数的地址
DWORD Ordinal; // 被导入函数的序号
DWORD AddressOfData; // 指向输入名称表 PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
名词
OriginalFirstThunk指向INT
FirstThunk指向IAT
其中将OriginalFirstThunk全部置零不影响程序的运行
载入前结构
加载后的结构
解析导入表
PIMAGE_IMPORT_DESCRIPTOR pIMAGE_IMPORT_DESCRIPTOR = (PIMAGE_IMPORT_DESCRIPTOR)
(pIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[1].VirtualAddress + (DWORD)hModule);
for (size_t i = 0; pIMAGE_IMPORT_DESCRIPTOR[i].Name != 0; i++)
{
char* dllName = (char*)(pIMAGE_IMPORT_DESCRIPTOR[i].Name + (DWORD)hModule);
printf("%s\n", dllName);
PIMAGE_THUNK_DATA pIMAGE_THUNK_DATA = (PIMAGE_THUNK_DATA)(pIMAGE_IMPORT_DESCRIPTOR[i].OriginalFirstThunk + (DWORD)hModule);
DWORD* pfunaddress = (DWORD*)(pIMAGE_IMPORT_DESCRIPTOR[i].FirstThunk + (DWORD)hModule);
for (size_t j = 0; pIMAGE_THUNK_DATA[j].u1.ForwarderString != 0; j++)
{
PIMAGE_IMPORT_BY_NAME FunName = (PIMAGE_IMPORT_BY_NAME)(
(pIMAGE_THUNK_DATA[j].u1.ForwarderString) + (DWORD)hModule);
printf("%s:%x\n", FunName->Name, pfunaddress[j]);
}
printf("\n\n");
}
重定位表
PE文件重定位表中保存的仅仅只是一大堆需要修正的代码的地址,修正算法可以描述为,将直接寻址指令中的地址加上模块实际装入地址与模块建议装入地址之差。为了进行运算需要3个数据,首先是需要修正机器码地址,其次是模块建议装入地址,最后是模块的实际装入地址。在这3个数据中,模块的建议装入地址已经在PE文件头中定义了,而模块的实际装入地址时Windows装载器在装载文件时确定的,事实上PE文件重定位表中保存的仅仅只是,一大堆需要修正的代码的地址
IMAGE_BASE_RELOCATION
重定位表会被单独存放在.reloc
命名的节中,重定位表的位置和大小可以从数据目录中的第6个IMAGE_DATA_DIRECTORY
结构中获取到,该表的组织方式时以0x1000
页为一块,每一块负责一页,从PE文件头获取到重定位表地址后,就可以顺序读取到所有表结构,每个重定位块以一个IMAGE_BASE_RELOCATION
结构开头,后面跟着在本页中使用的所有重定位项,每个重定位项占用16字节,最后一个节点是一个使用0填充的_IMAGE_BASE_RELOCATION
标志表的结束
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
items=(SizeOfBlock-8)/2
枚举重定位块
#include<Windows.h>
#include<iostream>
using namespace std;
DWORD RvaToFoa(_In_ DWORD rva, _In_ PIMAGE_SECTION_HEADER p, _In_ PIMAGE_FILE_HEADER f)
{
for (int i = 0; i < f->NumberOfSections; i++)
{
// FOA = 数据的RVA + 区段的RVA - 区段的FOA
if (rva >= p->VirtualAddress && rva < (p->VirtualAddress + p->Misc.VirtualSize))
{
return rva - p->VirtualAddress + p->PointerToRawData;
}
p++;
}
return 0;
}
void ImageNtHeader(_In_z_ const char* path)
{
// 获取文件对象
HANDLE hfile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// 获取文件大小
DWORD fSize = GetFileSize(hfile, NULL);
char* pBuff = new char[fSize];
DWORD dwReadSize = 0;
// 读文件
BOOL bSuccess = ReadFile(hfile, pBuff, fSize, &dwReadSize, NULL);
if (bSuccess)
{
typedef struct _TYPE {
WORD Offset : 12; //大小 2bit 重定位的偏移
WORD Tyoe : 4;
}TYPE, * PTYPE;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
PIMAGE_NT_HEADERS32 pNtHeader{ 0 };
pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + pBuff);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&pNtHeader->OptionalHeader;
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
// PIMAGE_DATA_DIRECTORY dataDirectory = (PIMAGE_DATA_DIRECTORY)&pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
PIMAGE_BASE_RELOCATION pRel = (PIMAGE_BASE_RELOCATION)(pBuff + RvaToFoa(pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, pSectionHeader, pFileHeader));
DWORD dwCount = 0;
while (*(PLONGLONG)pRel)
{
printf("[%d] VirtualAddress-> [0x%08x] SizeOfBlock-> [0x%08x] \r\n", dwCount++, pRel->VirtualAddress, pRel->SizeOfBlock);
//需要修复重定位项个数
DWORD dwRelEntry = (pRel->SizeOfBlock - 8) / 2;
//指向重定位项
//PWORD p1 = (PWORD)pRel;
//PWORD pRelData = p1 + 8;
PWORD pRelData = (PWORD)pRel + 4;
for (size_t i = 0; i < dwRelEntry; i++)
{
//判断高4位
//32位高4位0011
//64位高4位1010
if ((pRelData[i] & 0x3000) == 0x3000)
{
//低12位 + VirtualAddress为真正需要修复数据的RVA
DWORD dwData = pRelData[i] & 0x0FFF;
DWORD dwDataRVA = (DWORD)dwData + (DWORD)pRel->VirtualAddress;
printf("[%d] DATA[0x%04x] RVA[0x%08x]\r\n", i, dwData|0x3000, dwDataRVA); }
}
//指向下一个重定位结构
pRel = (PIMAGE_BASE_RELOCATION)((PUCHAR)pRel + pRel->SizeOfBlock);
}
}
else (cout.write("打开文件失败", 20));
CloseHandle(hfile);
delete[] pBuff;
}
void main()
{
ImageNtHeader(R"(D:\c_project\dlltest\Debug\Dll1.dll)");
}
需要重定位的文件偏移量计算方法
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!