March 30, 2022

HFCTF2022-the_shellcode

0x00 日常查壳

DIE查不动了,直接出个Themida强壳,学习一下

image-20220330093106958

0x01 Dump

动调是可以发现有反调试

可以去装下sharp OD即可过反调试(开启这个选项)

image-20220330093420028

开启选项之后,F9直接跑,第一次会卡住,第二次再跑就好了(雾)

image-20220330093522944

按E再进入程序领空

image-20220330093552781

通过字符串到OEP,注意要下硬件断点,因为重调后这片数据又会成未解压缩状态,所以要硬件执行断点

image-20220330093801742

此时如果是打CTF是可以直接dump出来,已经可以看函数逻辑了

image-20220330094027326

0x02 修复IAT

如果直接导出会发现IAT都没修复,于是通过F7单步跟踪每个call IAT的指令可以发现有些IAT没有恢复(或者说没有准确的恢复)

image-20220330094201742

于是就可以写个OD脚本来恢复他们

于是判断每次进这些IAT函数,都会经历一个JMP,然后CTRL + F9,此时ESP栈顶的位置会是计算好的正确IAT地址

image-20220330094518064

同时要去掉几种不需要修复的IAT

  1. IAT已经到结束位置(XX30E0)
  2. IAT大于72C0 0000(说明IAT是正确指向)
  3. IAT表的值为0(就不需要修复了)

然后读取脚本运行会发现IAT全部修复

image-20220330095002611

OD脚本(注意基地址,960000是我这的基地址)

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
VAR OEP
MOV OEP, 009611C0

VAR IATAddr
VAR tmpAddr
MOV IATAddr, 00962FFC

RepairIATTable:
ADD IATAddr, 4
MOV tmpAddr, [IATAddr]
CMP tmpAddr, 72C00000
JA RepairIATTable

CMP tmpAddr, 0
JE RepairIATTable

MOV eip, tmpAddr
RTR
MOV [IATAddr], [esp]
CMP IATAddr, 009630E0
JAE Success
JMP RepairIATTable

Success:
MOV eip, OEP
RET

再用管理员运行Scylla(要不然可能看不到进程)

image-20220330095141005

修转储,再把转储转储修复(好像有点绕hh

image-20220330095249359

这时候再拉进IAT看,可以发现除了被作者编译时去掉的符号表,IAT全部正常

image-20220330095447005

0x03 GetShellCode

修复好IAT dump出来的还是跑不起来,自己去OD里试下就知道有些基地址还不对,需要再写OD脚本去修复CALL JMP指令

分析主函数是阅读感一般,有很多混淆,大部分都是因为你长度不对而进行的其他逻辑判断

不过真正要进行的加密就三个

简易版Base解密(好像base平常加解密还多了一步)

image-20220330101243300

逻辑左移3

image-20220330101338588

偷偷换个数字的XXTEA(密钥就在上面一点)

image-20220330101354351

再往下面找就能,找到shellcode密文

image-20220330101447902

拿出祖传xxtea脚本,再随笔糊个base加密和逻辑右移即可

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
#include <stdio.h>
#include <stdint.h>

#define DELTA 0x9e3779b9
#define MX (((z>>6^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
#define ROR(x, n) (((x) >> n) & 0xFF) | ((x) << (8 - n) & 0xFF)

char baseTable[] =
{
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x3E, 0x42, 0x42, 0x42, 0x3F, 0x34, 0x35,
0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x42, 0x42,
0x42, 0x41, 0x42, 0x42, 0x42, 0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
0x31, 0x32, 0x33, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x42, 0x42, 0x42, 0x42, 0x42, 0x42
};

void xxteaDecode(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}

int FindIndex(int x)
{
int i;

for ( i = 0; i < 256; i++ )
if ( baseTable[i] == x )
return i;
}

void BaseDecode(unsigned char * flag, int len, unsigned char * input)
{
int i, j;

for ( i = 0, j = 0; i < len; i += 3, j += 4 )
{
input[j] = (flag[i] >> 2) & 0x3F;
input[j + 1] = ((flag[i] & 0x3) << 4) | (flag[i + 1] & 0xF0 ) >> 4;
input[j + 2] = ((flag[i + 1] & 0xF ) << 2) | (flag[i + 2] & 0xC0) >> 6;
input[j + 3] = flag[i + 2] & 0x3F;
}
for ( i = 0; i < len / 3 * 4; i++ )
input[i] = FindIndex(input[i]);
}

int main()
{
uint32_t v[]=
{
0x4B6B89A1, 0x74C15453, 0x4092A06E, 0x429B0C07, 0x40281E84, 0x8B5B44C9, 0x66FEB37B, 0x3C77A603,
0x79C5892D, 0x0D7ADA97, 0x1D51AA56, 0x02D4D703, 0x4FA526BA, 0x32FAD64A, 0x0C0F6091, 0x562B7593,
0xDB9ADD67, 0x76165563, 0xA5F79315, 0x3AEB991D, 0x1AB721D4, 0xAACD9D2C, 0x825C2B27, 0x76A7761A,
0xB4005F18, 0x117F3763, 0x512CC540, 0xC594A16F, 0xD0E24F8C, 0x9CA3E2E9, 0x0A9CC2D5, 0x4629E61D,
0x637129E3, 0xCA4E8AD7, 0xF5DFAF71, 0x474E68AB, 0x542FBC3A, 0xD6741617, 0xAD0DBBE5, 0x62F7BBE3,
0xC8D68C07, 0x880E950E, 0xF80F25BA, 0x767A264C, 0x9A7CE014, 0x5C8BC9EE, 0x5D9EF7D4, 0xB999ACDE,
0xB2EC8E13, 0xEE68232D, 0x927C5FCE, 0xC9E3A85D, 0xAC74B56B, 0x42B6E712, 0xCD2898DA, 0xFCF11C58,
0xF57075EE, 0x5076E678, 0xD4D66A35, 0x95105AB9, 0x1BB04403, 0xB240B959, 0x7B4E261A, 0x23D129D8,
0xF5E752CD, 0x4EA78F70
};
uint32_t const k[4]= { 116, 111, 114, 97 };
int n= 66;
int i;

xxteaDecode(v, -n, k);
for ( i = 0; i < 66; i++ )
printf("0x%X, ", v[i]);

puts("\n");

unsigned char * t = (unsigned char *)v;
unsigned char baseCode[66 * 4];

for ( i = 0; i < 66 * 4; i++ )
{
baseCode[i] = ROR(t[i], 3);
printf("0x%X, ", baseCode[i]);
}

puts("\n");

char input[352];
BaseDecode(baseCode, 66 * 4, input);
for ( i = 0; i < 352; i++ )
printf("%c", input[i]);

//YPxoTHcmBzPSZItSMItSDItSFItyKA+3SiYz/zPArDxhfAIsIMHPDQP44vBSV4tSEItCPAPCi0B4hcAPhL4AAAADwlCLSBiLWCAD2oP5AA+EqQAAAEmLNIsD8jP/M8Cswc8NA/g6xHX0A3wkBDt8JAx12TP/M8mDwlAPtgQKwc8NA/hBg/kOdfHBzw1XM/8zyYtUJDxSD7YcDrhnZmZm9+vR+ovCwegfA8KNBIAr2FoPtgQKK8PBzw0D+EGD+Q511MHPDTs8JHQWaCVzAACLxGhubwAAVFCLXCRI/9PrFGglcwAAi8RoeWVzAFRQi1wkSP/TWFhYWFhYWFhYYcNYX1qLEukL////

return 0;
}

0x04 GetFlag

再往下看,可以发现通过baseCode生成的一段加密函数

image-20220330102855839

注意是通过xxtea解密完,再逻辑右移解密完后的shellcode,直接拉去010editor恢复,生成个新文件

再通过第二个是flag可以发现,这段为主逻辑

于是就有个等式

1
2
3
4
5
n1 = data1[] + ROR(n1)
n2 = flag[] - data2[] + ROR(n2)
因n1 == n2 所以循环每次 n += x + ROR(n)两式必相等
得证 data1[] = flag[] - data2[] % 5
即 flag[] = data1[] + data2[] % 5

image-20220330103938701

再去动调把data1和data2数据拿了

image-20220330111146293

image-20220330111237170

GetFlag!

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

data1 = "is program can"
data2 = "LoadLibraryExA"
shellCode = "YPxoTHcmBzPSZItSMItSDItSFItyKA+3SiYz/zPArDxhfAIsIMHPDQP44vBSV4tSEItCPAPCi0B4hcAPhL4AAAADwlCLSBiLWCAD2oP5AA+EqQAAAEmLNIsD8jP/M8Cswc8NA/g6xHX0A3wkBDt8JAx12TP/M8mDwlAPtgQKwc8NA/hBg/kOdfHBzw1XM/8zyYtUJDxSD7YcDrhnZmZm9+vR+ovCwegfA8KNBIAr2FoPtgQKK8PBzw0D+EGD+Q511MHPDTs8JHQWaCVzAACLxGhubwAAVFCLXCRI/9PrFGglcwAAi8RoeWVzAFRQi1wkSP/TWFhYWFhYWFhYYcNYX1qLEukL////"
flag = ''

for i in range(14):
flag += chr(ord(data1[i]) + ord(data2[i]) % 5)

md5Flag = shellCode + flag
m = hashlib.md5(md5Flag.encode())
print("hfctf{" + m.hexdigest() + "}")

image-20220330112541971

DASCTF X SU
🍬
HFCTF2022
🍪

About this Post

This post is written by P.Z, licensed under CC BY-NC 4.0.