PE文件结构学习记录-1

内存读写复习

想当年也是做pwn题的,又看了看视频,发现有些东西还是忘记了。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
byte 8
WORD 16
DWORD 32

取值
MOV EAX,DWORD PTR DS:[0x123FC2]
取内存地址 编号
LEA EAX,DWORD PTR DS:[0x123FC2]
内存前的DS ES SS
DS 是个数
ESP EBP: SS
EDI :ES

ESP 栈顶
EBP 栈底
push 入栈
pop 出栈
栈高往低走

push 相当
lea esp,dword ptr ss:[esp-4]
mov dword ptr ss[esp],eax
栈顶拔高

ECX 值决定REP指令重复次数
stos指令,它的功能是将eax中的数据放入的edi所指的地址中,同时,edi会增加4个字节,rep使指令重复执行ecx中填写的次数。
方括弧表示存储器,这个地址实际上是edi的内容所指向的地址。这里的stos其实对应的是stosd,其它的还有
CALL : EIP改为函数地址,返回地址压栈
RET 返回时改EIP(弹栈,esp加)


pe头解析

内容

5b4b62c4c1faa

以下内容有参考x复制✓[原创]归纳PE结构基础知识,顺便手撕个PE-编程技术-看雪论坛-安全社区|安全招聘|bbs.pediy.com

内存对齐和硬盘对齐

就是一个pe文件在硬盘上的数据可能和硬盘中的不一样

因为老软件硬盘对齐是200h

内存对齐是1000h

后来就对齐一样了

使用分节结构原因:

节省内存,多开的时候只需要

DOS头

1
2
3
4
5
6
7
typedef struct _IMAE_DOS_HEADER
{
WORD e_magic; // 0x00, 'MZ'(0x5A4D)标识
............
LONG e_lfanew; // 0x3C, PE头的起始地址,默认0xB0处
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

重要:
1.WORD e_magic(0x00处)
● 对应PE文件的开头,是PE文件DOS头的标识符"MZ"→0x5A4D
○ 对应Winnt.h头文件中宏定义: #define IMAGE_DOS_SIGNATURE 0x4D5A // MZ
2.LONG e_lfanew(0x3C处)
● 对应PE文件0x3C处指向NT头在文件中的偏移(默认0xB0),即32位及以上系统文件头在文件中真正的偏移

2.NT头

(1)DWORD Signature:"PE\0\0"→0x00004550

(2)文件头结构体 IMAGE_FILE_HEADER FileHeader(标准PE头)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 文件头结构体: _IMAGE_FILE_HEADER
typedef struct _IMAGE_FILE_HEADER{
WORD Machine; // +0x00, 指定程序的运行平台,勿改
WORD NumberOfSections; // +0x02, PE中的节/块(section)数量,勿改
DWORD TimeDateStamp; // +0x04, 时间戳:链接器填写的文件生成时间
DWORD PointerToSymbolTable; // +0x08, 指向符号表的地址(主要用于调试)
DWORD NumberOfSymbols; // +0x0C, 符号表中符号个数(同上)
WORD SizeOfOptionalHeader; // +0x10, IMAGE_OPTIONAL_HEADER32选项头结构大小,勿改
WORD Characteristics; // +0x12, 文件属性,勿改
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

//* 字段1:Machine 表CPU的类型, 定义在windows.h中,常用类型
32位:#define IMAGE_FILE_MACHINE_I386, 0x014c // Intel 386, x86
64位:#define IMAGE_FILE_MACHINE_AMD64, 0x8664 // AMD64(KB), x64
//* 字段2:NumberOfSections 表PE中的节(section)数量:
节表紧跟在IMAGE_NT_HEADERS后面,此字段决定了节表中元素的个数,即节的个数
遍历节表经验:根据此处的个数拿对应的节表数据
//* 字段6:SizeOfOptionalHeader 表IMAGE_OPTIONAL_HEADER32 结构大小
定位节表位置=选项头地址+选项头大小
//* 字段7: Characteristics 表文件属性,EXE默认0100,DLL默认210Eh,或运算组合设置。
#define IMAGE_FILE_RELOCS_STRIPPED     0x0001 // 文件中不存在重定位信息
#define IMAGE_FILE_EXECUTABLE_IMAGE    0x0002 // 文件可执行
#define IMAGE_FILE_LINE_NUMS_STRIPPED   0x0004 // 文件中不存在行信息
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED   0x0008 // 文件中不存在符号信息
#define IMAGE_FILE_AGGRESIVE_WS_TRIM    0x0010 // 调整工作集
#define IMAGE_FILE_LARGE_ADDRESS_AWARE   0x0020 // 程序能处理大于2G的地址
#define IMAGE_FILE_BYTES_REVERSED_LO    0x0080 // 小尾方式
#define IMAGE_FILE_32BIT_MACHINE      0x0100 // 只在32位平台上运行
#define IMAGE_FILE_DEBUG_STRIPPED     0x0200 // 不包含调试信息
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // 不能从可移动盘运行
#define IMAGE_FILE_NET_RUN_FROM_SWAP    0x0800 // 不能从网络运行
#define IMAGE_FILE_SYSTEM          0x1000 // 系统文件(如驱动程序),不能直接运行
#define IMAGE_FILE_DLL            0x2000 // 是一个dll文件
#define IMAGE_FILE_UP_SYSTEM_ONLY      0x4000 // 文件不能在多处理器计算机上运行
#define IMAGE_FILE_BYTES_REVERSED_HI    0x8000 // 大尾方式

(3)选项头结构体IMAGE_OPTIONAL_HEADER(区分32位和64位,可选PE头)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
typedef struct _IMAGE_OPTIONAL_HEADER
{
WORD Magic; //* PE标志字:32位(0x10B),64位(0x20B)
BYTE MajorLinkerVersion; // 主链接器版本号
BYTE MinorLinkerVersion; // 副链接器版本号
DWORD SizeOfCode; // 代码所占空间大小(代码节大小)
DWORD SizeOfInitializedData; // 已初始化数据所占空间大小
DWORD SizeOfUninitializedData; // 未初始化数据所占空间大小
DWORD AddressOfEntryPoint; //* 程序执行入口RVA,(w)(Win)mainCRTStartup:即0D首次断下来的自进程地址
DWORD BaseOfCode; // 代码段基址
DWORD BaseOfData; // 数据段基址
DWORD ImageBase; //* 内存加载基址,exe默认0x400000,dll默认0x10000000
DWORD SectionAlignment; //* 节区数据在内存中的对齐值,一定是4的倍数,一般是0x1000(4096=4K)
DWORD FileAlignment; //* 节区数据在文件中的对齐值,一般是0x200(磁盘扇区大小512)
WORD MajorOperatingSystemVersion; // 要求操作系统最低版本号的主版本号
WORD MinorOperatingSystemVersion; // 要求操作系统最低版本号的副版本号
WORD MajorImageVersion; // 可运行于操作系统的主版本号
WORD MinorImageVersion; // 可运行于操作系统的次版本号
WORD MajorSubsystemVersion; // 主子系统版本号:不可修改
WORD MinorSubsystemVersion; // 副子系统版本号
DWORD Win32VersionValue; // 版本号:不被病毒利用的话一般为0,XP中不可修改
DWORD SizeOfImage; //* PE文件在进程内存中的总大小,与SectionAlignment对齐
DWORD SizeOfHeaders; //* PE文件头部在文件中的按照文件对齐后的总大小(所有头 + 节表)
DWORD CheckSum; // 对文件做校验,判断文件是否被修改:3环无用,MapFileAndCheckSum获取
WORD Subsystem; // 子系统,与连接选项/system相关:1=驱动程序,2=图形界面,3=控制台/Dll
WORD DllCharacteristics; // 文件特性
DWORD SizeOfStackReserve; // 初始化时保留的栈大小
DWORD SizeOfStackCommit; // 初始化时实际提交的栈大小
DWORD SizeOfHeapReserve; // 初始化时保留的堆大小
DWORD SizeOfHeapCommit; // 初始化时实际提交的堆大小
DWORD LoaderFlags; // 已废弃,与调试有关,默认为 0
DWORD NumberOfRvaAndSizes; // 下边数据目录的项数,此字段自Windows NT发布以来,一直是16
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];// 数据目录表
} IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32;

//* 字段6:AddressOfEntryPoint 表 程序入口RVA,即OEP:
EOP:程序入口点,壳相关概念
OEP:原本的程序入口点(实际为偏移,+模块基址=实际入口点)
EP: 被加工后的入口点
//* 字段9:ImageBase 表 模块加载基地址,exe默认0x400000,dll默认0x10000000
建议装载地址:exe映射加载到内存中的首地址= PE 0处,即实例句柄hInstance
一般而言,exe文件可遵从装载地址建议,但dll文件无法满足
//* 尾字段:DataDirectory 表 数据目录表,用来定义多种不通用处的数据块。
存储了PE中各个表的位置,详情参考IMAGE_DIRECTORY_ENTRY...系列宏

3.节表

(1)节表总概

  • 节表:描述PE文件与内存之间的映射关系,由一系列的IMAGE_SECTION_HEADER结构排列而成,每个结构用来描述一个节(每个节占用0x28h),说明PE文件的指定内容拷贝至内存的哪个位置、拷贝大小及内存属性的设置。结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束,所以节表中总的IMAGE_SECTION_HEADER结构数量等于节的数量加一。节表总是被存放在紧接在PE文件头的地方。

  • 节表大小 = FileHeader.NumberOfSections(节数量)* IMAGE_SECTION_HEADER 结构体。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // IMAGE_SECTION_HEADER 节表结构体,大小40B
    typedef struct _IMAGE_SECTION_HEADER {
    BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节表名称:描述性字段
    // 下方4个字段:从文件S1处开始,拷贝S2大小的数据,到内存S3处,有效数据占用内存S4大小
    union {
    DWORD PhysicalAddress;
    DWORD VirtualSize; // S4:内存大小
    } Misc;
    DWORD VirtualAddress; // S3:内存地址:基于模块基址
    DWORD SizeOfRawData; // (拷贝的到内存数据大小)S2:文件对齐大小
    DWORD PointerToRawData; // (节在文件中偏移)S1:文件偏移
    DWORD PointerToRelocations; // 无用
    DWORD PointerToLinenumbers; // 无用
    WORD NumberOfRelocations; // 无用
    WORD NumberOfLinenumbers; // 无用
    DWORD Characteristics; // 节属性,取值IMAGE_SCN_...系列宏
    } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

    img

节属性常用位含义

节表置于选项头之后,节表首地址 计算方法:

选项头的地址 + 选项头的大小;

(2) e_lfanew+4+0x14(文件头大小)+0xE0(32位选项头大小)64位是0xF0。

image-20220227003416563

FileBuffer(文件里的数据状态)与ImageBuffer(内存里的数据状态)

image-20220227205440595
1
SizeOfRawData不一定比Misc大,因为Misc在文件中,可能是在文件里未初始化未进行分配的值(记录的是在内存中的大小)
image-20220227212440476
1
2
3
4
5
写代码的坑
vs fopen_s 函数:
第一个参数是FILE *指针
第二个文件名字
第三个是方法

call jmp

1
2
3
4
jmp = E8
jmp后面的值x = jmp后一条指令地址(运行时内存中的地址)+要跳转的地址
call = E9
call 后面的值同上,均是运行时内存中的地址

指针

1
2
3
byte*)转byte型指针
dword)要用指针算地址的时候转的dword类型
都是强制类型转换

任意空白区添加代码

修改vs编译的程序,遇到的问题:

1.要是修改vs编译出来的程序要跳过.textbss这个节表,这个表的内存地址和文件地址都为0x000000

2.vs编译的程序已经开启ALSR(地址随机化),要想修改成功需要关闭这个

image-20220303143104764
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
LPVOID changeimagebuffer(LPVOID imagebuffer) {
PIMAGE_DOS_HEADER pDosHeader = NULL;
DWORD SizeOfImage = 0;
DWORD SizeOfHeaders = 0;
WORD NumberOfSections = 0;
PIMAGE_SECTION_HEADER pSectionHeaderBase = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
LPVOID pImageBuffer = NULL;
PBYTE codebegin = NULL;



pDosHeader = (PIMAGE_DOS_HEADER)imagebuffer;
pNtHeaders = FileToNtHeader(imagebuffer);

//判断空闲区是否能存储shellcode代码(文件对齐大小-misc里真实内存大小)
pSectionHeaderBase = LocateSectionBase(imagebuffer);
if (((pSectionHeaderBase->SizeOfRawData) - (pSectionHeaderBase->Misc.VirtualSize)) < sizeof(shellcode))
{
printf("代码空闲区不足");
free(imagebuffer);
return NULL;
}

//把代码复制到空闲区
codebegin = (PBYTE)((DWORD)imagebuffer + pSectionHeaderBase->VirtualAddress + pSectionHeaderBase->Misc.VirtualSize)+14;
memcpy(codebegin, shellcode, sizeof(shellcode));
//修正E8

DWORD e8addr = (DWORD)(MESSAGE_BOX_ADDRESS - (pNtHeaders->OptionalHeader.ImageBase + ((DWORD)codebegin +(DWORD)0x0D - (DWORD)imagebuffer)));
//e8addr = 0x7D8A55E1;
*(PDWORD)(codebegin + 9) = e8addr;
// 修正E9
DWORD e9addr = (DWORD)((pNtHeaders->OptionalHeader.ImageBase + pNtHeaders->OptionalHeader.AddressOfEntryPoint) - (pNtHeaders->OptionalHeader.ImageBase + ((DWORD)codebegin + (DWORD)0x12 - (DWORD)imagebuffer)));
//e9addr = 0xFFFFDC95;
*(PDWORD)(codebegin + 0xE) = e9addr;
//pSectionHeaderBase = (PIMAGE_SECTION_HEADER)(BYTE*)imagebuffer + pDosHeader->e_lfanew + sizeof(IMAGE_);

pNtHeaders->OptionalHeader.AddressOfEntryPoint = (DWORD)codebegin - (DWORD)imagebuffer;
//pNtHeaders->OptionalHeader.AddressOfEntryPoint = 0x01A730;
return imagebuffer;


}

新增加一个节

需要进行的操作

1)添加一个新的节(可以copy一份)
2)在新增节后面填充一个节大小的000
3)修改PE头中节的数量
4)修改sizeofimage的大小
5)再原有数据的最后,新增一个节的数据(内存对齐的整数倍).
6)修正新增节表的属性

注意:SizeOfHeader - (DOS +垃圾数据+ PE标记+标准PE头+可选PE头+己存在节表)= 2个节表的大小

空间不够可以把PE头往上移动(占DOS头的垃圾数据)

代码遇到的问题

1.imagebuffer开辟内存空间太小了,解决:重新申请一个足够大的空间然后拷贝内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
LPVOID NEWSetion(LPVOID pImageBuffer) {
/*
1)添加一个新的节(可以copy一份)
2)在新增节后面填充一个节大小的000
3)修改PE头中节的数量
4)修改sizeofimage的大小
5)再原有数据的最后,新增一个节的数据(内存对齐的整数倍).
6)修正新增节表的属性

注意:SizeOfHeader - (DOS +垃圾数据+ PE标记+标准PE头+可选PE头+己存在节表)= 2个节表的大小

空间不够可以把PE头往上移动(占DOS头的垃圾数据)

*/
PIMAGE_DOS_HEADER pDosHeader = NULL;
DWORD SizeOfImage = 0;
DWORD SizeOfHeaders = 0;
WORD NumberOfSections = 0;
DWORD pointtonew = 0;
PIMAGE_SECTION_HEADER pSectionHeaderBase = NULL;
PIMAGE_SECTION_HEADER pnewSectionHeaderBase = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
//LPVOID pImageBuffer = NULL;
PBYTE codebegin = NULL;

pNtHeaders = FileToNtHeader(pImageBuffer);//nt头
//内存对齐大小
const DWORD size = pNtHeaders->OptionalHeader.SectionAlignment;

LPVOID npFileBuffer = malloc(pNtHeaders->OptionalHeader.SizeOfImage + size);


memset(npFileBuffer,0x00 , pNtHeaders->OptionalHeader.SizeOfImage + size);
memcpy(npFileBuffer, pImageBuffer, pNtHeaders->OptionalHeader.SizeOfImage);

pImageBuffer = npFileBuffer;
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;//dos头
pNtHeaders = FileToNtHeader(pImageBuffer);//nt头

//验证剩余空间是否够2个节表大小
DWORD freesp = pNtHeaders->OptionalHeader.SizeOfHeaders - (pDosHeader->e_lfanew + 0x4 + 0x14 + pNtHeaders->FileHeader.SizeOfOptionalHeader + (pNtHeaders->FileHeader.NumberOfSections * 0x28));
if (freesp < 0x28 * 2)
{
printf ("空间不足够添加节表,添加失败");
return pImageBuffer;
};


//找最后一个节表结束的位置添加节
pSectionHeaderBase = LocateSectionBase(pImageBuffer);
LPVOID addsectionstart = (BYTE*)pSectionHeaderBase + (DWORD)(0x28 * (pNtHeaders->FileHeader.NumberOfSections));
memcpy(addsectionstart, pSectionHeaderBase, 0x28);

//在内存最后添加一个内存对齐整数倍的00
LPVOID endexe = (BYTE*)pImageBuffer + pNtHeaders->OptionalHeader.SizeOfImage ;


BYTE* tian1 = new BYTE[size];
memset(tian1, 0x00, size);

//tian1
//BYTE tian[4096] = { 0x00 };
memcpy(endexe, tian1 , size);

//修改sizeofimage大小
pNtHeaders->OptionalHeader.SizeOfImage = pNtHeaders->OptionalHeader.SizeOfImage + size;

//修改新添加节的属性
pnewSectionHeaderBase =(PIMAGE_SECTION_HEADER) addsectionstart;
pnewSectionHeaderBase->Misc.VirtualSize = size;
pnewSectionHeaderBase->VirtualAddress = (DWORD) endexe - (DWORD) pImageBuffer;
pnewSectionHeaderBase->SizeOfRawData = size;
//文件中偏移,先找上一个节的文件偏移+是一个节的大小(偷懒直接看是不是SizeOfRawData打过真实大小,直接加了)
PIMAGE_SECTION_HEADER presec = PIMAGE_SECTION_HEADER((BYTE*)pnewSectionHeaderBase - 0x28);
if (presec->SizeOfRawData > presec->Misc.VirtualSize)
{
pointtonew = presec->SizeOfRawData + presec->PointerToRawData;
}
else
{
return 0;
}
pnewSectionHeaderBase->PointerToRawData = pointtonew;



//+pNtHeaders->OptionalHeader.SectionAlignment
//修改节的数量+1
pNtHeaders->FileHeader.NumberOfSections = pNtHeaders->FileHeader.NumberOfSections + 1;



return pImageBuffer;
}

扩大节:

1、拉伸到内存
2.分配一块新的空间: Size0fImage + Ex
3、将最后一个节的Si zeOfRawData和VirtualSize改成N
SizeOfRawData = VirtualSize = N
N = (SizeOfRawData或者VirtualSize 内存对齐后的值) + Ex (这两个值取最大的)
4、修改SizeOfImage大小
Size0fImage = Size0fImage + Ex

合并节

1、拉伸到内存
2、将第一个节的内存大小、文件大小改成一样
Max = SizeOfRawData>VirtualSize?SizeOfRawData:VirtualSize
SizeOfRawData = VirtualSize = 最后一个节的VirtualAddress(23000h) + Max (4000h)- SizeOfHeaders内存对齐后的大小(1000h)
3、将第一个节的属性改为包含所有节的属性

DWORD Characteristics; 进行或运算即可
4、修改节的数量为1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
函数功能:合并节
参数:内存映像指针
返回值:内存映像指针
注意:无
1、拉伸到内存
2、将第一个节的内存大小、文件大小改成一样
Max = SizeOfRawData>VirtualSize?SizeOfRawData:VirtualSize
SizeOfRawData = VirtualSize = 最后一个节的VirtualAddress + Max - SizeOfHeaders内存对齐后的大小
3、将第一个节的属性改为包含所有节的属性
4、修改节的数量为1
*/
LPVOID Setiontoone(LPVOID pImageBuffer) {
PIMAGE_DOS_HEADER pDosHeader = NULL;
DWORD SizeOfImage = 0;
DWORD SizeOfHeaders = 0;
DWORD SizeOfRawData = 0;
DWORD VirtualSize = 0;
DWORD MAX = 0;
WORD NumberOfSections = 0;
DWORD pointtonew = 0;
PIMAGE_SECTION_HEADER pSectionHeaderBase = NULL;
PIMAGE_SECTION_HEADER pnewSectionHeaderBase = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;//dos头
pNtHeaders = FileToNtHeader(pImageBuffer);//nt头

//SizeOfHeaders内存对齐后的大小
pSectionHeaderBase = LocateSectionBase(pImageBuffer);
DWORD SizeOfHeadersmem = pSectionHeaderBase->VirtualAddress;
//找最后一个节
PIMAGE_SECTION_HEADER pLaswSectionHeaderBase = PIMAGE_SECTION_HEADER ((BYTE*)pSectionHeaderBase + (DWORD)(0x28 * (pNtHeaders->FileHeader.NumberOfSections-1)));
//Max = SizeOfRawData>VirtualSize?SizeOfRawData:VirtualSize
if (pLaswSectionHeaderBase->SizeOfRawData > pLaswSectionHeaderBase->Misc.VirtualSize)
{
MAX = pLaswSectionHeaderBase->SizeOfRawData;
}
else
{
MAX = pLaswSectionHeaderBase->Misc.VirtualSize;
}
//SizeOfRawData = VirtualSize = 最后一个节的VirtualAddress + Max - SizeOfHeaders内存对齐后的大小
SizeOfRawData = pLaswSectionHeaderBase->VirtualAddress + MAX - SizeOfHeadersmem;
/*
修改节表属性或操作
*/
for (int x = 1; x < pNtHeaders->FileHeader.NumberOfSections ; x++) {
PIMAGE_SECTION_HEADER pSectiontime = (PIMAGE_SECTION_HEADER)((BYTE*)pSectionHeaderBase +x* 0x28);
pSectionHeaderBase->Characteristics = pSectiontime->Characteristics | pSectionHeaderBase->Characteristics;
}
//修改节表数量
pNtHeaders->FileHeader.NumberOfSections = 1;

//修改第一个节表属性
pSectionHeaderBase->SizeOfRawData = SizeOfRawData;
pSectionHeaderBase->Misc.VirtualSize = SizeOfRawData;

//修改所有节的属性



return pImageBuffer;

代码遇到的问题

1.忘记修改节表属性了

动态链接库 静态链接库

静态链接库:直接把函数编译到exe里,但是如果需要修改函数就得重新编译exe

动态链接库:修改时直接编译dll即可,exe不用重新生成

导出表

在可选PE头里的数据目录第一项

image-20220307232838234

数据目录结构

1
2
3
4
5
// 数据目录 _IMAGE_DATA_DIRECTORY结构体
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; /**指向某个数据的相对虚拟地址 RAV 偏移0x00**/
DWORD Size; /**某个数据块的大小 偏移0x04**/
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

要在文件中找到需要RVA转FOA查看文件中的位置

导出表结构

image-20220308230652307

*不一定是导出的函数地址长度最大,有可能几个函数指向同一个的地址

numberoffunctions,不一定就是真正的导出函数个数(用序号的最后一个减去开始的序号然后加1)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!,本博客仅用于交流学习,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此站文章的修改和解释权。如欲转载此站文章,需取得作者同意,且必须保证此文章的完整性,包括版权声明等全部内容。未经文章作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。若造成严重后果,本人将依法追究法律责任。 阅读本站文章则默认遵守此规则。