查壳不说了,ida开64位,进入main,里面已经手动改了一些函数名字

进入func1看一下

好了都看不明白,上动调。这个时候习惯看一下导出,发现tls

知识点搜搜就知道了,直接改一下执行流跳转绕过就行

有虚函数表

能发现三个虚函数:



其中一个的密钥脚本解出,另外两个题目告诉我们了,106和0

1
2
3
4
5
6
7
8
9
10
11
12
13
data1=[0x11, 0x45, 0x14, 0x49, 0x86, 0x64, 0x40, 0x50, 0x60, 0xC1, 0x39, 0x2D, 0x00, 0x00, 0x00, 0x00]
str=[0x2F, 0x1F, 0x20, 0x2E, 0x34, 0x04, 0x37, 0x2D, 0x10, 0x39, 0x7C, 0x22, 0x7B, 0x75, 0x0A, 0x38, 0x39, 0x21,0,0,0]

for i in range(0x13):
str[i]^=data1[1]
v5=[1,5,4,2,3,0]
data=[0]*25
for i in range(0x12):
data[6 * (i // 6) + v5[i % 6]]=str[i]
for j in range(0x12):
data[j]^=j
print(chr(data[j]),end='')
# Akira_aut0_ch3ss_!

这时候往下跟进,StartAddress里面的一个作为参数的函数

创建共享内存(CreateFileMappingW 函数用于创建一个内存映射文件对象,MapViewOfFile 函数用于将内存映射文件对象映射到当前进程的地址空间),给了一堆数据,然后有“MZ”的格式判断。生成dll文件。

脚本dump一下dll:

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
#include <iostream>
#include <Windows.h>
#include <io.h>
#define DLLSIZE 19456

int main()
{
FILE* pFile = fopen("D:\\Akira_Homework\\WinRevfirst.exe", "rb+");
if (pFile == NULL)
return -1;
//获取文件大小
DWORD dwFileLen = _filelength(_fileno(pFile));
if (dwFileLen <= 0)
return -1;
// 分配缓冲区并读取文件内容:
PBYTE pFileBuff = new BYTE[dwFileLen];
ZeroMemory(pFileBuff,dwFileLen);
if (pFileBuff == NULL)
return -1;

fread(pFileBuff, 1, dwFileLen, pFile);
fclose(pFile);

PBYTE pDllBuff = new BYTE[19456];
ZeroMemory(pDllBuff, 19456);

memcpy(pDllBuff, pFileBuff + 0xEFA0, DLLSIZE);

//解密1001
DWORD pdwKey1001 = 106 ^ 0x33;
for (size_t i = 0; i < DLLSIZE; i++)
{
if (i % 3 == 1)
pDllBuff[i] ^= pdwKey1001; //函数解密
}

//解密1002
for (size_t i = 0; i < DLLSIZE; i++)
{
if (i % 3 == 2)
{
pDllBuff[i] = ((int)(unsigned __int8)pDllBuff[i] >> 4) | (16 * pDllBuff[i]);//函数解密
}
}

//解密1003
PCHAR pKey1003 = (PCHAR)"Akira_aut0_ch3ss_!";
DWORD dwKey1Len = strlen(pKey1003);
for (size_t i = 0; i < DLLSIZE; i++)
{
if (!(i % 3))
{
pDllBuff[i] ^= pKey1003[i / 3 % dwKey1Len];//函数解密
}
}

FILE* pFileNew = fopen("D:\\Akira_Homework\\Test.DLL", "wb+");
fwrite(pDllBuff, 1, DLLSIZE, pFileNew);
fclose(pFileNew);

delete pFileBuff;
delete pDllBuff;
return 0;
}

dll的逻辑很简单,上python解aes:

1
2
3
4
5
6
7
from Crypto.Cipher import AES
from Crypto.Util.number import *
key = long_to_bytes(0x416B316933615333637265374B337900) #密钥
mi = long_to_bytes(0x94BF7A0CA43550D1C215ECEF9D9AAA56) #密文
lun = AES.new(key, mode=AES.MODE_ECB)
flag = lun.decrypt(mi)
print(flag)

flag{Ak1rAWin!}

总结:

最开始有tls反调试,改一下执行流,然后main函数逐一分析

第一个是虚函数表的三个函数,指向Protector::ProcessCheckHandler` 类的虚函数表的指针,三个虚函数均对长度为19456的一大串数据进行了修改

第二个是创建线程函数beginthreadex,跟进StartAddress,有一个函数编号10001函数(发现sub_7FF6EB3B8300和sub_7FF6EB3B6C10两个成对出现)前面虚函数有三个,所以猜测一下另两个虚函数也有这两个函数的调用,对6C10交叉引用发现符合猜想,三个虚函数的密钥分别是106 0 Akira_aut0_ch3ss_!

StartAddress往下跟进函数,发现向apc队列添加函数,跟进这个函数,发现了里面有创建共享内存+写入密文+PE格式判断(文件头MZ)。生成dll后就是一个标准的aes,密钥给了,密文在原来的文件内存有写

感觉这道题就是函数要一个个看,一个函数有一个逻辑,最后串在一起。难点一个是过反调试;一个是要看出虚函数表那几个函数做了什么操作,那一大串数据是一个文件,共享内存的函数,apc队列;最后就是怎么写脚本把dll文件dump出来

题外话:

写wp的时候已经做完这道题好久了,只是简单复制一下当时做完题的思路总结,并没有写的很详细(懒得放图了),可以结合网上其他师傅的wp食用