image-20260407010147583

这是主函数

先用findcrypt扫描算法发现有aes和base64编码并回溯找到aes函数位置为sub_4020D0(Block, (int)v22)

image-20260407010221904

接着运行调试时进入sub_402320(ModuleHandleW)发现有int3断点

image-20260407010437363

打开汇编流程图分析

image-20260407010520959

左边的为seh函数右边为主程序

并且在右边发现了注册she函数的汇编

image-20260407010823312

其中seh结构体中loc_4023EF就是主程序中左边的seh函数

image-20260407011015416

这个程序在注册seh后在下面的DebugBreak()会强制出发int3断点然后交给调试器进而反调试我们调试的时候可以选择交给程序自己解决来跳过

在seh中有个函数sub_402450

image-20260407011353264

image-20260407011411898

这其中就是smc代码对每个字节和aSycloversyclov中的字符循环加密然后取反

至于smc的区域肯定在后面可以找到

image-20260407011641548

这个函数中return ((int (*)(void))dword_404000[0])();就是很明显的smc代码

image-20260407011718433

我们用ida脚本来还原

image-20260407011830172

重新编码后可以看到

image-20260407011904719

这是一个对aPvfqyc4ttc2uxr每个字符减一再逆序的函数

image-20260407012102103

解密后可以得到nKnbHsgqD3aNEB91jB3gEzAr+IklQwT1bSs3+bXpeuo=

这很明显是一个base64加密后的结果

结合前面的加密函数中两串连续的字符

image-20260407012354573

image-20260407012404250

推测是aes的cbc模式

image-20260407012435345

解密拿到答案sctf{Ae3_C8c_I28_pKcs79ad4}

image-20260404202151969

这个题打开就知道是ollvm混淆但是用d—810就是去不掉混淆找不到主要逻辑但是我动调拿到了密钥,那个出题人说这个题的密钥生成是根据注册码来的。

image-20260404202516038

可以看到最后v78和v77对比,然而v78是有Src来的然后就直接动调拿答案

image-20260404203007169image-20260404203059298

peloader加载器

1.创建数组来保存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
HANDLE X1(char* x, unsigned char** pbuf)
{
HANDLE X;
DWORD FileSize;
LPDWORD IFFileSize=0;
LPDWORD IFReadFile=0;
X = CreateFileA(x,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
DWORD err= GetLastError();
if (X == INVALID_HANDLE_VALUE)
{
printf("未能打开文件%d", err);
exit(0);
}
FileSize = GetFileSize(X, IFFileSize);
if (IFFileSize == -1)
{
printf("未能得到文件尺寸");
exit(0);
}
*pbuf =(unsigned char *)malloc(sizeof(unsigned char)* FileSize);
ZeroMemory(*pbuf, FileSize);//清零
ReadFile(X, *pbuf, FileSize, IFReadFile, NULL);
if (IFReadFile == -1)
{
printf("未能复制文件");
exit(0);
}
}

2.申请内存将DOS头NT头写入

1
2
3
4
5
6
7
8
9
10
PIMAGE_DOS_HEADER DOS = (PIMAGE_DOS_HEADER)pbuf;
PIMAGE_NT_HEADERS64 NT = (PIMAGE_NT_HEADERS64)(pbuf + DOS->e_lfanew);
DWORD SizeOfImage = NT->OptionalHeader.SizeOfImage;
LPVOID Alloc;
if (NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0)
Alloc = VirtualAlloc(NULL, SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
else
Alloc = VirtualAlloc(NT->OptionalHeader.ImageBase, SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
ZeroMemory(Alloc, SizeOfImage);
CopyMemory(Alloc, pbuf, NT->OptionalHeader.SizeOfHeaders);//申请dos,nt空间

3.将节区循环写入申请内存

1
2
3
4
5
6
7
8
9
10
11
DWORD SectionNumber = NT->FileHeader.NumberOfSections;
for (DWORD n=0; n < SectionNumber; n++)//复制节区
{
PIMAGE_SECTION_HEADER SectionHead = (PIMAGE_SECTION_HEADER)((char*)NT+sizeof(IMAGE_NT_HEADERS64)+ sizeof(IMAGE_SECTION_HEADER)*n);
if (SectionHead->PointerToRawData!=0)//.bss节区(需要初始化)
CopyMemory(SectionHead->VirtualAddress + (PCHAR)Alloc, pbuf + SectionHead->PointerToRawData, SectionHead->SizeOfRawData);
else
{
ZeroMemory(SectionHead->VirtualAddress + (PCHAR)Alloc, SectionHead->Misc.VirtualSize);//清零
}
}

4.加载重定位表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0)
{
PIMAGE_BASE_RELOCATION ReLoc = (PIMAGE_BASE_RELOCATION)(NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (PCHAR)Alloc);
while (ReLoc->SizeOfBlock != 0)
{
PCHAR ReV = ReLoc->VirtualAddress + (PCHAR)Alloc;//每页的虚拟地址
int NumberOfBlock = (ReLoc->SizeOfBlock - 8) / 2;//每页有多少需要修改的值
for (int n = 0; n < NumberOfBlock; n++)
{
PWORD ChangByte = (PWORD)(ReLoc + 1);
char Type = ChangByte[n] >> 12;//获得标志位
if (Type == 10)
{
PDWORD64 Offest = (PDWORD64*)((ChangByte[n] & 0xfff) + ReV);//这页当中需要修改的具体位置
*Offest = *Offest + (ULONGLONG)Alloc - NT->OptionalHeader.ImageBase;//加上基址
}
}
ReLoc = (PIMAGE_SECTION_HEADER)((PCHAR)ReLoc + ReLoc->SizeOfBlock);//下一页
}
}

相关加载重定位表的理解可以看这篇文章:https://blog.csdn.net/Apollon_krj/article/details/77370452

5.填充IAT表

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
PIMAGE_IMPORT_DESCRIPTOR IntImport = (PIMAGE_IMPORT_DESCRIPTOR)(NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (PCHAR)Alloc);
while (IntImport->Name!=0)//循环dll库
{
HMODULE DllHandle;
char DllName[50];
strncpy(DllName,IntImport->Name+(PCHAR)Alloc,49);
DllHandle = LoadLibraryA(DllName);
PIMAGE_THUNK_DATA64 INT = IntImport->OriginalFirstThunk + (PCHAR)Alloc;
PIMAGE_THUNK_DATA64 IAT = IntImport->FirstThunk + (PCHAR)Alloc;
while (INT->u1.AddressOfData != 0)//循环dll中每个函数
{
if (INT->u1.Ordinal & IMAGE_ORDINAL_FLAG64)//通过序号查找函数
{
IAT->u1.AddressOfData = GetProcAddress(DllHandle, INT->u1.Ordinal);
}
else//通过名字查找函数
{
PIMAGE_IMPORT_BY_NAME Fcn = (PIMAGE_IMPORT_BY_NAME)(INT->u1.AddressOfData + (PCHAR)Alloc);
IAT->u1.AddressOfData = GetProcAddress(DllHandle, Fcn->Name);//填充IAT表
int l = 1;
}
INT++;
IAT++;
}
IntImport++;
}

相关填充IAT表的理解可以看这篇文章:https://blog.csdn.net/qq_35289660/article/details/107329444

6.修改程序入口

1
2
3
4
5
6
FARPROC EOP = (FARPROC)((LPBYTE)Alloc + NT->OptionalHeader.AddressOfEntryPoint);//修改程序入口点
EOP();

free(pbuf);
free(Alloc);
return 0;

汇总

peloader的加载原理就是将pe文件的内容读取后根据当中的VirtualAddress将内容分配到申请空间里,然后执行。(下面是所有代码)

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<windows.h>
#include <stdbool.h>
#include<winnt.h>

HANDLE X1(char* x, unsigned char** pbuf)
{
HANDLE X;
DWORD FileSize;
LPDWORD IFFileSize=0;
LPDWORD IFReadFile=0;
X = CreateFileA(x,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
DWORD err= GetLastError();
if (X == INVALID_HANDLE_VALUE)
{
printf("未能打开文件%d", err);
exit(0);
}
FileSize = GetFileSize(X, IFFileSize);
if (IFFileSize == -1)
{
printf("未能得到文件尺寸");
exit(0);
}
*pbuf =(unsigned char *)malloc(sizeof(unsigned char)* FileSize);
ZeroMemory(*pbuf, FileSize);
ReadFile(X, *pbuf, FileSize, IFReadFile, NULL);
if (IFReadFile == -1)
{
printf("未能复制文件");
exit(0);
}
}
VOID MAIN1(unsigned char* pbuf) {
printf("这是个64进制文件\n");
Sleep(500);
PIMAGE_DOS_HEADER DOS = (PIMAGE_DOS_HEADER)pbuf;
PIMAGE_NT_HEADERS64 NT = (PIMAGE_NT_HEADERS64)(pbuf + DOS->e_lfanew);
DWORD SizeOfImage = NT->OptionalHeader.SizeOfImage;
LPVOID Alloc;
if (NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0)
Alloc = VirtualAlloc(NULL, SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
else
Alloc = VirtualAlloc(NT->OptionalHeader.ImageBase, SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
ZeroMemory(Alloc, SizeOfImage);
CopyMemory(Alloc, pbuf, NT->OptionalHeader.SizeOfHeaders);//申请dos,nt空间
DWORD SectionNumber = NT->FileHeader.NumberOfSections;
for (DWORD n=0; n < SectionNumber; n++)//复制节区
{
PIMAGE_SECTION_HEADER SectionHead = (PIMAGE_SECTION_HEADER)((char*)NT+sizeof(IMAGE_NT_HEADERS64)+ sizeof(IMAGE_SECTION_HEADER)*n);
if (SectionHead->PointerToRawData!=0)//.bss特殊
CopyMemory(SectionHead->VirtualAddress + (PCHAR)Alloc, pbuf + SectionHead->PointerToRawData, SectionHead->SizeOfRawData);
else//.bss特殊
{
ZeroMemory(SectionHead->VirtualAddress + (PCHAR)Alloc, SectionHead->Misc.VirtualSize);
}
}
if (NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0)
{
PIMAGE_BASE_RELOCATION ReLoc = (PIMAGE_BASE_RELOCATION)(NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (PCHAR)Alloc);
while (ReLoc->SizeOfBlock != 0)
{
PCHAR ReV = ReLoc->VirtualAddress + (PCHAR)Alloc;
int NumberOfBlock = (ReLoc->SizeOfBlock - 8) / 2;
for (int n = 0; n < NumberOfBlock; n++)
{
PWORD ChangByte = (PWORD)(ReLoc + 1);
char Type = ChangByte[n] >> 12;
//PDWORD64 Offest = 0;
if (Type == 10)
{
// 找到目标地址
// 修改目标地址的值:方法是原来的值+偏移
PDWORD64 Offest = (PDWORD64*)((ChangByte[n] & 0xfff) + ReV);
*Offest = *Offest + (ULONGLONG)Alloc - NT->OptionalHeader.ImageBase;
}
}
ReLoc = (PIMAGE_SECTION_HEADER)((PCHAR)ReLoc + ReLoc->SizeOfBlock);
}
}
PIMAGE_IMPORT_DESCRIPTOR IntImport = (PIMAGE_IMPORT_DESCRIPTOR)(NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (PCHAR)Alloc);
while (IntImport->Name!=0)//引入表
{
HMODULE DllHandle;
char DllName[50];
strncpy(DllName,IntImport->Name+(PCHAR)Alloc,49);
DllHandle = LoadLibraryA(DllName);
PIMAGE_THUNK_DATA64 INT = IntImport->OriginalFirstThunk + (PCHAR)Alloc;
PIMAGE_THUNK_DATA64 IAT = IntImport->FirstThunk + (PCHAR)Alloc;
while (INT->u1.AddressOfData != 0)
{
if (INT->u1.Ordinal & IMAGE_ORDINAL_FLAG64)
{
IAT->u1.AddressOfData = GetProcAddress(DllHandle, INT->u1.Ordinal);
}
else
{
PIMAGE_IMPORT_BY_NAME Fcn = (PIMAGE_IMPORT_BY_NAME)(INT->u1.AddressOfData + (PCHAR)Alloc);
IAT->u1.AddressOfData = GetProcAddress(DllHandle, Fcn->Name);
int l = 1;
}
INT++;
IAT++;
}
IntImport++;
}
FARPROC EOP = (FARPROC)((LPBYTE)Alloc + NT->OptionalHeader.AddressOfEntryPoint);
EOP();

free(pbuf);
free(Alloc);
return 0;
}//64文件处理
int main(int argc,char* argv[])
{
unsigned char* pbuf=NULL;
X1(argv[1],&pbuf);
PIMAGE_DOS_HEADER DOS = (PIMAGE_DOS_HEADER)pbuf;
if (*(PWORD)pbuf == 0x5A4D)
printf("这是个PE文件\n");
WORD Magic = *(WORD*)(pbuf + (DOS->e_lfanew + 6 * 4));
if (Magic == 0x20b)
MAIN1(pbuf);
}
0%