写了逆向方向3个题,队伍第12

ezDos

好像只能汇编分析

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
seg002:0000 seg002          segment byte public 'CODE' use16
seg002:0000 assume cs:seg002
seg002:0000 assume es:nothing, ss:seg001, ds:nothing, fs:nothing, gs:nothing
seg002:0000
seg002:0000 public start
seg002:0000 start:
seg002:0000 mov ax, seg dseg
seg002:0003 mov ds, ax
seg002:0005 assume ds:dseg
seg002:0005 mov es, ax
seg002:0007 assume es:dseg
seg002:0007 xor ax, ax
seg002:0009 test ax, ax
seg002:000B jnz short loc_104BF
seg002:000D jz short loc_104C0
seg002:000F
seg002:000F loc_104BF: ; CODE XREF: seg002:000B↑j
seg002:000F nop
seg002:0010
seg002:0010 loc_104C0: ; CODE XREF: seg002:000D↑j
seg002:0010 mov ah, 9
seg002:0012 lea dx, aShowMeYourFlag ; "\r\nShow me your flag: $\r\nCongradulat"...
seg002:0016 int 21h ; DOS - PRINT STRING
seg002:0016 ; DS:DX -> string terminated by "$"
seg002:0018 mov ah, 0Ah
seg002:001A lea dx, unk_10168
seg002:001E int 21h ; DOS - BUFFERED KEYBOARD INPUT
seg002:001E ; DS:DX -> buffer
seg002:0020 call sub_10630
seg002:0025 lea bx, unk_10168
seg002:0029 inc bx
seg002:002A cmp byte ptr [bx], 26h ; '&'
seg002:002D jz short loc_104E2
seg002:002F jmp loc_105B7

这部分是将输入放在168的位置,并且168里放的是大小,要比对大小是0x26

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
seg002:0032 ; ---------------------------------------------------------------------------
seg002:0032
seg002:0032 loc_104E2: ; CODE XREF: seg002:002D↑j
seg002:0032 xor si, si
seg002:0034 xor di, di
seg002:0036 xor cx, cx
seg002:0038 mov cx, 100h
seg002:003B
seg002:003B loc_104EB: ; CODE XREF: seg002:003D↓j
seg002:003B push di
seg002:003C inc di
seg002:003D loop loc_104EB
seg002:003F
seg002:003F loc_104EF: ; CODE XREF: seg002:0052↓j
seg002:003F pop bx
seg002:0040 call sub_10670
seg002:0045 mov bx, si
seg002:0047 mov [si+0], bl
seg002:004B inc si
seg002:004C cmp si, 100h
seg002:0050 jnb short loc_10504
seg002:0052 jmp short loc_104EF

本题第一个坑,rc4的init阶段,刚开始有一个push 0x100次压栈,但乍一看后面好像并没有用到但是我们去关注sub_10670函数本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
seg005:0000 sub_10670       proc far                ; CODE XREF: seg002:0040↑P
seg005:0000 pop dx
seg005:0001 push ax
seg005:0002 xor ax, ax
seg005:0004 mov ax, 0Fh
seg005:0007 and ax, 7
seg005:000A shl ax, 1
seg005:000C shl ax, 1
seg005:000E mov ah, 1Ah
seg005:0010 not ah
seg005:0012 shr ah, 1
seg005:0014 shr ah, 1
seg005:0016 xor al, ah
seg005:0018 sub ah, 1Eh
seg005:001B and al, ah
seg005:001D inc al
seg005:001F add dl, al
seg005:0021 pop ax
seg005:0022 push dx
seg005:0023 retf
seg005:0023 sub_10670 endp

ret=pop jmp,所以最后push进去的就会是跳转,刚开始pop dx,返回地址被取出,add dl,al,这里对返回地址改动,这个数字是固定的,经过计算这里的al是2,所以call sub_10670后紧跟的两字节是被越过的

所以再次观察就会发现,bl里的值就是先前压栈的值,并且栈是先进后出,所以这里rc4init是反着来的盒,所以第一部分造盒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void rc4init1() {
unsigned char a = 0xff;
for (int i = 0; i < 256; i++)
{
box[i] = a;
a--;
}
for (int i = 0; i < 0x10; i++)
{
for (int j = 0; j < 0x10; j++)
{
printf("%02x ", box[j + i * 0x10]);
}
printf("\n");
}
}

紧接着往后看

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
seg002:0054 loc_10504:                              ; CODE XREF: seg002:0050↑j
seg002:0054 xor si, si
seg002:0056 xor di, di
seg002:0058 mov bx, 134h
seg002:005B mov cl, [bx]
seg002:005D
seg002:005D loc_1050D: ; CODE XREF: seg002:00B9↓j
seg002:005D mov dl, [si+0]
seg002:0061 add di, dx
seg002:0063 mov ax, si
seg002:0065 div cl
seg002:0067 mov al, ah
seg002:0069 xor ah, ah
seg002:006B mov bx, 135h
seg002:006E add bx, ax
seg002:0070 mov al, [bx]
seg002:0072 push ax
seg002:0073 call jmp_$6
seg002:0078 shl ax, 1
seg002:007A shl ax, 1
seg002:007C shl ax, 1
seg002:007E shl ax, 1
seg002:0080 shl ax, 1
seg002:0082 shl ax, 1
seg002:0084 mov dx, ax
seg002:0086 pop ax
seg002:0087 push dx
seg002:0088 call jmp_$2
seg002:008D shr ax, 1
seg002:008F shr ax, 1
seg002:0091 shr ax, 1
seg002:0093 shr ax, 1
seg002:0095 shr ax, 1
seg002:0097 shr ax, 1
seg002:0099 pop dx
seg002:009A or al, dl
seg002:009C add di, ax
seg002:009E and di, 0FFh
seg002:00A2 mov al, [si+0]
seg002:00A6 mov dl, [di+0]
seg002:00AA xchg al, dl
seg002:00AC mov [di+0], dl
seg002:00B0 mov [si+0], al
seg002:00B4 inc si
seg002:00B5 cmp si, 100h
seg002:00B9 jb short loc_1050D

于是就该换盒了,si=i,di=j,慢慢看,134里面存储的是0xc,后面紧跟着是’NCTf2024nctF’,刚好12字节大小,并且0xc给了cl,[+0]代表的是前面造的盒

一开始是

dl=S[i];

j=j+dl;

al=key[i%0xc];

后面紧跟着的push ax是为了把前面左移和后面右移区分,同时这两个函数也和前面一样是用来改变返回地址的,所以这里实现的是:

push ax;

shl ax,3;

mov dx,ax;

pop ax;

push dx;

shr ax,5;

pop dx;

or al,dl:

add di,ax;

也就相当于(a<<3)|(a>>5),最后j+=a;

j%256;

change(S[i],S[j]);

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
seg002:00BB                 xor     cx, cx
seg002:00BD mov bx, 169h
seg002:00C0 mov cl, [bx]
seg002:00C2 inc bx
seg002:00C3 mov si, bx
seg002:00C5 xor bx, bx
seg002:00C7 xor dx, dx
seg002:00C9 xor ax, ax
seg002:00CB test ax, ax
seg002:00CD jnz short loc_10581
seg002:00CF jz short loc_10582
seg002:00D1
seg002:00D1 loc_10581: ; CODE XREF: seg002:00CD↑j
seg002:00D1 nop
seg002:00D2
seg002:00D2 loc_10582: ; CODE XREF: seg002:00CF↑j
seg002:00D2 ; seg002:00FE↓j
seg002:00D2 inc bl
seg002:00D4 mov al, [bx+0]
seg002:00D8 add dl, al
seg002:00DA push dx
seg002:00DB mov di, dx
seg002:00DD mov al, [bx+0]
seg002:00E1 xchg al, [di+0]
seg002:00E5 mov [bx+0], al
seg002:00E9 add al, [di+0]
seg002:00ED mov di, ax
seg002:00EF mov al, [di+0]
seg002:00F3 call jmp_$1
seg002:00F8 inc ax
seg002:00F9 inc ax
seg002:00FA xor [si], al
seg002:00FC pop dx
seg002:00FD inc si
seg002:00FE loop loc_10582
seg002:0100 jmp short loc_105C1

最后一段,怎么说呢,复杂归复杂,慢慢捋出来把,其实就是rc4稍微变了一下。。。服了卡了一天

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
#include<stdio.h>
#include<string.h>
#include<stdint.h>
#include<stdlib.h>
unsigned char box[256] = { 0 };
void rc4init1() {
unsigned char a = 0xff;
for (int i = 0; i < 256; i++)
{
box[i] = a;
a--;
}
for (int i = 0; i < 0x10; i++)
{
for (int j = 0; j < 0x10; j++)
{
printf("%02x ", box[j + i * 0x10]);
}
printf("\n");
}
}
void rc4init2(unsigned char* key) {
int i, j;
i = 0;
j = 0;
unsigned char high;
unsigned char low;
unsigned char mi;

for ( i = 0; i < 0x100; i++)
{
j = box[i] + j;
high = key[i % 0xc];
low = high;
mi = ((high << 3) & 0xff) | ((low >> 5) & 0xff);
j = (j + mi) % 256;
mi = box[i];
box[i] = box[j];
box[j] = mi;
}
printf("\n");
for (int i = 0; i < 0x10; i++)
{
for (int j = 0; j < 0x10; j++)
{
printf("%02x ", box[j + i * 0x10]);
}
printf("\n");
}
}
void rc4(unsigned char* flag) {
unsigned char i = 0;
unsigned char j = 0;
unsigned char mi;
unsigned char t;
for (int q = 0; q < strlen(flag); q++)
{
i++;
j = (j + box[i]) % 256;

mi = box[j];
box[j] = box[i];
box[i] = mi;

t = (box[i] + box[j]) % 256;
t = box[t];
t++;

flag[q] ^= t;
printf("%c", flag[q]);
}
}
int main() {
unsigned char key[] = "NCTf2024nctF";
unsigned char flag[] = {
0x7C, 0x3E, 0x0D, 0x3C, 0x88, 0x54, 0x83, 0x0E, 0x3B, 0xB8, 0x99, 0x1B, 0x9B, 0xE5, 0x23, 0x43,
0xC5, 0x80, 0x45, 0x5B, 0x9A, 0x29, 0x24, 0x38, 0xA9, 0x5C, 0xCB, 0x7A, 0xE5, 0x93, 0x73, 0x0E,
0x70, 0x6D, 0x7C, 0x31, 0x2B, 0x8C, 0
};
rc4init1();
rc4init2(key);
rc4(flag);
//NCTF{Y0u_4r3_Assemb1y_M4st3r_5d0b497e}
return;
}

SafeProgram

拖进去

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE v4[24]; // [rsp+38h] [rbp-70h] BYREF
_BYTE Buf1[16]; // [rsp+50h] [rbp-58h] BYREF
_BYTE Buf1_1[48]; // [rsp+60h] [rbp-48h] BYREF

sub_7FF6E42D15F0(argc, argv, envp);
memset(Str1, 0, sizeof(Str1));
memset(byte_7FF6E42FC380, 0, sizeof(byte_7FF6E42FC380));
memset(v4, 0, 0x14uLL);
sub_7FF6E42D1830(aWelcomeToNctf); // "Welcome to NCTF\n"
Sleep(0x1F4u);
sub_7FF6E42D1830(aEnterYourFlag); // "Enter your flag "
Sleep(0x1F4u);
sub_7FF6E42D1830(aAndHaveAGoodTi); // "And have a good time: "
sub_7FF6E42D18B0("%64s", Str1);
if ( strlen(Str1) != 38 )
{
sub_7FF6E42D1830(aLengthError); // "Length Error!"
ExitProcess(1u);
}
if ( !strncmp(
Str1,
Str2, // "NCTF{"
5uLL) && Str1[37] == 125 )
{
sub_7FF6E42D1E10(Str1, "NCTF{%32s}", byte_7FF6E42FC380);
memcpy(v4, &aNctf24nctf, 0xAuLL);
memcpy(&v4[10], &aNctf24nctf, 6uLL);
sub_7FF6E42D19D0(byte_7FF6E42FC380, v4, Buf1);
sub_7FF6E42D19D0(&byte_7FF6E42FC380[16], v4, Buf1_1);
}
if ( memcmp(Buf1, &Buf2_, 0x20uLL) )
{
sub_7FF6E42D1830(aWrongFlag); // "Wrong Flag!"
ExitProcess(1u);
}
sub_7FF6E42D1830(aCorrect); // "Correct!"
return 0;
}

题目提示说调试器在监视你,果然调试的时候闪退了,往start方向找下断点发现毫无作用,那看来是init或者tls的原因,看导出表发现果然有两个tls,第一个里面看不出是做什么的

1
2
3
4
5
void sub_140001520()
{
if ( !(unsigned int)sub_1400011D0() )
RaiseException(0xE0000001, 0, 0, 0LL);
}

顺着找很快就看到了,这里把逻辑改一下然后保存一下,就可以调试了,整体逻辑不难,就是sm4加密,当然如果没见过还是很难的,不过直接解密发现有问题,应该是sm4有改动,加密逻辑不好改的话那就只能是1.盒2.轮遍数,后面发现是Sbox变动了,是因为我发现key变了,看交叉引用发现是tls0里面的东西。。。前面说的话打脸了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 sub_7FF677251480()
{
__int64 result; // rax
int i; // [rsp+20h] [rbp-18h]
int j; // [rsp+24h] [rbp-14h]

for ( i = 0; i < 10; ++i )
{
byte_7FF67727A0C0[i] ^= 0x91u;
result = (unsigned int)(i + 1);
}
for ( j = 0; j < 10; ++j )
{
sub_7FF677251E80(byte_7FF67727A0D0, &byte_7FF67727A0D0[byte_7FF67727A0C0[j]]);
result = (unsigned int)(j + 1);
}
return result;
}

后面的就是盒,前面的是key,再次解密就出flag了

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#define _SM4_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define u8 unsigned char
#define u32 unsigned long
void four_uCh2uLong(u8* in, u32* out); //四字节转换成u32
void uLong2four_uCh(u32 in, u8* out); //u32转换成四字节
unsigned long move(u32 data, int length); //左移,保留丢弃位放置尾部
unsigned long func_key(u32 input); //先使用Sbox进行非线性变化,再将线性变换L置换为L'
unsigned long func_data(u32 input); //先使用Sbox进行非线性变化,再进行线性变换L
void print_hex(u8* data, int len); //无符号字符数组转16进制打印
void encode_fun(u8 len, u8* key, u8* input, u8* output); //加密函数
void decode_fun(u8 len, u8* key, u8* input, u8* output); //解密函数
/******************************定义系统参数FK的取值****************************************/
const u32 TBL_SYS_PARAMS[4] = {

0xa3b1bac6,
0x56aa3350,
0x677d9197,
0xb27022dc
};
/******************************定义固定参数CK的取值****************************************/
const u32 TBL_FIX_PARAMS[32] = {

0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};
/******************************SBox参数列表****************************************/
const u8 TBL_SBOX[256] = {

0xD1, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x17, 0xA9, 0x1C, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0x4F, 0xF3, 0x73, 0x71, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0xD6, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0xFC, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x78, 0x63, 0x58, 0x9F, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0xC9, 0x87,
0xD4, 0x00, 0x46, 0x57, 0x5E, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51,
0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8,
0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0,
0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84,
0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48
};
//4字节无符号数组转无符号long型
void four_uCh2uLong(u8* in, u32* out)
{

int i = 0;
*out = 0;
for (i = 0; i < 4; i++)
*out = ((u32)in[i] << (24 - i * 8)) ^ *out;
}
//无符号long型转4字节无符号数组
void uLong2four_uCh(u32 in, u8* out)
{

int i = 0;
//从32位unsigned long的高位开始取
for (i = 0; i < 4; i++)
*(out + i) = (u32)(in >> (24 - i * 8));
}
//左移,保留丢弃位放置尾部
u32 move(u32 data, int length)
{

u32 result = 0;
result = (data << length) ^ (data >> (32 - length));
return result;
}
//秘钥处理函数,先使用Sbox进行非线性变化,再将线性变换L置换为L'
u32 func_key(u32 input)
{

int i = 0;
u32 ulTmp = 0;
u8 ucIndexList[4] = {
0 };
u8 ucSboxValueList[4] = {
0 };
uLong2four_uCh(input, ucIndexList);
for (i = 0; i < 4; i++)
{

ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
}
four_uCh2uLong(ucSboxValueList, &ulTmp);
ulTmp = ulTmp ^ move(ulTmp, 13) ^ move(ulTmp, 23);
return ulTmp;
}
//加解密数据处理函数,先使用Sbox进行非线性变化,再进行线性变换L
u32 func_data(u32 input)
{

int i = 0;
u32 ulTmp = 0;
u8 ucIndexList[4] = {
0 };
u8 ucSboxValueList[4] = {
0 };
uLong2four_uCh(input, ucIndexList);
for (i = 0; i < 4; i++)
{

ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
}
four_uCh2uLong(ucSboxValueList, &ulTmp);
ulTmp = ulTmp ^ move(ulTmp, 2) ^ move(ulTmp, 10) ^ move(ulTmp, 18) ^ move(ulTmp, 24);
return ulTmp;
}
//加密函数(可以加密任意长度数据,16字节为一次循环,不足部分补0凑齐16字节的整数倍)
//len:数据长度(任意长度数据) key:密钥(16字节) input:输入的原始数据 output:加密后输出数据
void encode_fun(u8 len, u8* key, u8* input, u8* output)
{

int i = 0, j = 0;
u8* p = (u8*)malloc(50); //定义一个50字节缓存区
u32 ulKeyTmpList[4] = {
0 }; //存储密钥的u32数据
u32 ulKeyList[36] = {
0 }; //用于密钥扩展算法与系统参数FK运算后的结果存储
u32 ulDataList[36] = {
0 }; //用于存放加密数据
/***************************开始生成子秘钥********************************************/
four_uCh2uLong(key, &(ulKeyTmpList[0]));
four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));
ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];
for (i = 0; i < 32; i++) //32次循环迭代运算
{

//5-36为32个子秘钥
ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
}
/***********************************生成32轮32位长子秘钥结束**********************************/
for (i = 0; i < len; i++) //将输入数据存放在p缓存区
*(p + i) = *(input + i);
for (i = 0; i < 16 - len % 16; i++)//将不足16位补0凑齐16的整数倍
*(p + len + i) = 0;
for (j = 0; j < len / 16 + ((len % 16) ? 1 : 0); j++) //进行循环加密,并将加密后数据保存(可以看出此处是以16字节为一次加密,进行循环,即若16字节则进行一次,17字节补0至32字节后进行加密两次,以此类推)
{

/*开始处理加密数据*/
four_uCh2uLong(p + 16 * j, &(ulDataList[0]));
four_uCh2uLong(p + 16 * j + 4, &(ulDataList[1]));
four_uCh2uLong(p + 16 * j + 8, &(ulDataList[2]));
four_uCh2uLong(p + 16 * j + 12, &(ulDataList[3]));
//加密
for (i = 0; i < 32; i++)
{

ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[i + 4]);
}
/*将加密后数据输出*/
uLong2four_uCh(ulDataList[35], output + 16 * j);
uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
}
free(p);
}
//解密函数(与加密函数基本一致,只是秘钥使用的顺序不同,即把钥匙反着用就是解密)
//len:数据长度 key:密钥 input:输入的加密后数据 output:输出的解密后数据
void decode_fun(u8 len, u8* key, u8* input, u8* output)
{

int i = 0, j = 0;
u32 ulKeyTmpList[4] = {
0 };//存储密钥的u32数据
u32 ulKeyList[36] = {
0 }; //用于密钥扩展算法与系统参数FK运算后的结果存储
u32 ulDataList[36] = {
0 }; //用于存放加密数据
/*开始生成子秘钥*/
four_uCh2uLong(key, &(ulKeyTmpList[0]));
four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));
ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];
for (i = 0; i < 32; i++) //32次循环迭代运算
{

//5-36为32个子秘钥
ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
}
/*生成32轮32位长子秘钥结束*/
for (j = 0; j < len / 16; j++) //进行循环加密,并将加密后数据保存
{

/*开始处理解密数据*/
four_uCh2uLong(input + 16 * j, &(ulDataList[0]));
four_uCh2uLong(input + 16 * j + 4, &(ulDataList[1]));
four_uCh2uLong(input + 16 * j + 8, &(ulDataList[2]));
four_uCh2uLong(input + 16 * j + 12, &(ulDataList[3]));
//解密
for (i = 0; i < 32; i++)
{

ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[35 - i]);//与加密唯一不同的就是轮密钥的使用顺序
}
/*将解密后数据输出*/
uLong2four_uCh(ulDataList[35], output + 16 * j);
uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
}
}
//无符号字符数组转16进制打印
void print_hex(u8* data, int len)
{

int i = 0;
char alTmp[16] = {
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
for (i = 0; i < len; i++)
{

printf("%c", alTmp[data[i] / 16]);
printf("%c", alTmp[data[i] % 16]);
putchar(' ');
}
putchar('\n');
}
int main(void)
{
u8 i, len;
u8 encode_Result[] = {
0xFB, 0x97, 0x3C, 0x3B, 0xF1, 0x99, 0x12, 0xDF, 0x13, 0x30, 0xF7, 0xD8, 0x7F, 0xEB, 0xA0, 0x6C,
0x14, 0x5B, 0xA6, 0x2A, 0xA8, 0x05, 0xA5, 0xF3, 0x76, 0xBE, 0xC9, 0x01, 0xF9, 0x36, 0x7B, 0x46,
0
};
u8 decode_Result[50] = { 0 };
u8 key[16] = "NCTF24nctfNCTF24";
len = 32;

//encode_fun(sizeof(Data_plain), key, Data_plain, encode_Result);
/*printf("加密后数据是:\n");*/

decode_fun(len, key, encode_Result, decode_Result);
for (i = 0; i < len; i++)
printf("%c", *(decode_Result + i));
//58cb925e0cd823c0d0b54fd06b820b7e
return 0;
}

所以flag是NCTF{58cb925e0cd823c0d0b54fd06b820b7e}

x1Login

这是我第一次尝试用frida做安卓

1
2
3
4
5
6
7
8
9
10
private final void checkSecutity() {
if (Secure.checkDebug()) {
Toast.makeText(this, "Debugger Detected!", 0).show();
exit();
}
if (Secure.checkRoot()) {
Toast.makeText(this, "Root Detected!", 0).show();
exit();
}
}

有反调试,我选择frida到checkSecutity方法内,然后直接无操作返回(官方wp中显示用apktool解包,改,打包,签名,但是我一直报4字节的错,根本没办法解决,所以只得放弃)

然后就可以hook进去了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(C0890R.layout.activity_main);
checkSecutity();
final EditText editText = (EditText) findViewById(C0890R.C0893id.usernameEditText);
final EditText editText2 = (EditText) findViewById(C0890R.C0893id.passwordEditText);
Button button = (Button) findViewById(C0890R.C0893id.loginButton);
String str = DecStr.get("Exv3nhr5BNW0axn3aNz/DNv9C3q0wxj/Exe=");
Intrinsics.checkNotNullExpressionValue(str, "get(...)");
final Class<?> cls = getClass(str);
if (cls == null) {
Toast.makeText(this, "Error: Program load failure", 0).show();
finish();
System.exit(-1);
throw new RuntimeException("System.exit returned normally, while it was supposed to halt JVM.");
}
button.setOnClickListener(new View.OnClickListener() { // from class: com.nctf.simplelogin.MainActivity$$ExternalSyntheticLambda0
@Override // android.view.View.OnClickListener
public final void onClick(View view) {
MainActivity.onCreate$lambda$0(editText, editText2, this, cls, view);
}
});
}

主函数静态分析,前面是界面不必理会,一直到DecStr.get开始有东西,双击DecStr

1
2
3
4
5
6
7
public class DecStr {
public static native String get(String str);

static {
System.loadLibrary("simple");
}
}

是.so层的调用,如果.so分析的话

1
2
3
4
5
6
7
8
9
10
11
12
sub_7C0(
v5,
aAbcdefghijklmn, // "AbcdefghijklmnopqrstuvwxyzaBCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
s);
n8 = strlen((const char *)s);
v18 = n8 - n8_1;
v19 = (char *)s + n8_1;
do
{
--v18;
*v19++ ^= n8;
}

逻辑其实就是这样,base解码+异或长度Exv3nhr5BNW0axn3aNz/DNv9C3q0wxj/Exe=->com.nctf.simplelogin.Check

当然也可以frida

var str = DecStr.get(“Exv3nhr5BNW0axn3aNz/DNv9C3q0wxj/Exe=”);

console.log(‘[+] DecStr 解密结果: ‘ + str);

总之这里是获取了方法名,后面final Class<?> cls = getClass(str);+MainActivity.onCreate$lambda$0(editText, editText2, this, cls, view);

也就是加载方法,点击调用,跟着进到getClass里面

1
2
3
4
5
6
7
8
private final Class<?> getClass(String str) {
try {
return new InMemoryDexClassLoader(ByteBuffer.wrap(Secure.loadDex(getApplicationContext(), DecStr.get("ygvUF2vHFgbPiN9J"))), getClassLoader()).loadClass(str);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

这里实现的是加载dex文件,frida getApplicationContext()得到android.app.Application@da4db40

ygvUF2vHFgbPiN9J对应的是libsimple.so

所以这里是加载方法,关键方法是loadDex,所以可以进到这个函数中加载dex,然后用frida保存下来,整体dex如下

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
Java.perform(function() {
// 获取 MainActivity
var MainActivity = Java.use('com.nctf.simplelogin.MainActivity');

// Hook checkSecutity 方法,使其不执行
MainActivity.checkSecutity.implementation = function() {
console.log('[+] debugger');
};
var Secure = Java.use('com.nctf.simplelogin.Secure');
var ContextWrapper = Java.use('android.content.ContextWrapper');
var File = Java.use('java.io.File');
var FileOutputStream = Java.use('java.io.FileOutputStream');

Secure.loadDex.implementation = function (ctx, dexName) {
console.log("[+] Dex 载入自: " + dexName);
var dexData = this.loadDex(ctx, dexName); // dexData 是 Java bytes[]
console.log("[+] Dex 长度: " + dexData.length);

// 获取应用的外部存储目录
var context = Java.cast(ctx, ContextWrapper);
var path = context.getExternalFilesDir(null).getAbsolutePath() + "/dumped.dex";

console.log("[+] Dex 保存路径: " + path);

try {
// 写入文件
var file = File.$new(path);
var fos = FileOutputStream.$new(file);
fos.write(dexData); // 直接写入 Java bytes[]
fos.close();

console.log("[+] Dex 保存成功!");
} catch (e) {
console.log("[!] 保存 Dex 失败: " + e);
}

return dexData;
};
});

得到dex.dex

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
package com.nctf.simplelogin;

import android.content.Context;
import android.widget.Toast;
import java.security.MessageDigest;

/* loaded from: D:\study\ctf\2025nctf\x1Login-release\dex_dumped.dex */
public class Check {
Context context;
String password;
String username;

public Check(Context context, String username, String password) {
this.username = username;
this.password = password;
this.context = context;
}

public void check() {
try {
if (check_username()) {
MessageDigest digest = MessageDigest.getInstance(DecStr.get("tMC2"));
digest.update(this.username.getBytes());
byte[] output = digest.digest();
boolean result = check_password(output);
if (result) {
Toast.makeText(this.context, "Login Successful! Now submit your flag!", 0).show();
return;
}
}
Toast.makeText(this.context, "Login Failed!", 0).show();
} catch (Exception e) {
e.printStackTrace();
}
}

private boolean check_username() {
return this.username.equals(DecStr.get("uZPOs29goMu6l38="));
}

private boolean check_password(byte[] key) {
return Secure.doCheck(this.password, key);
}
}

简单解密,this.username.equals(DecStr.get(“uZPOs29goMu6l38=”));也就是账号=X1c@dM1n1$t

tMC2=Md5

逻辑就是加载Md5,然后对账号名Md5,调用Secure.doCheck

1
2
3
4
5
6
7
8
9
public class Secure {
public static native boolean doCheck(String str, byte[] bArr);

public static native byte[] loadDex(Context context, String str);

static {
System.loadLibrary(DecStr.get("agDYB3bJ"));
}

从这里可以看到doCheck也是.so层的,并且这个Secure调用的.so层是libnative.so(解密agDYB3bJ得到native),那么就找doCheck,但是发现.so文件中并没有标识,ida调试也会跑飞

不过好在函数也不多,找找也找到了

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
__int64 __fastcall sub_1E30(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
int n; // w20
int v8; // w0
unsigned __int8 *s; // x19
const void *src; // x22
__int64 *v11; // x23
unsigned __int8 *ptr; // x21
unsigned int v13; // w22
__int64 v14; // x22
__int64 v15; // x23
__int64 v16; // x26
__int64 *s_1; // x27
__int64 v18; // x0
__int64 v19; // x0
__int64 v20; // x0
unsigned __int8 *ptr_1; // x9
__int64 n_1; // x8
unsigned __int8 *s_2; // x10
int v24; // w11
int v25; // t1
int v26; // t1

n = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1312LL))(a1, a3);
v8 = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1312LL))(a1, a3);
s = (unsigned __int8 *)malloc(v8 + 1);
memset(s, 0, n + 1);
src = (const void *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0LL);
v11 = (__int64 *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1472LL))(a1, a4, 0LL);
memcpy(s, src, n);
ptr = (unsigned __int8 *)malloc(0x20uLL);
v13 = 0;
*(_OWORD *)ptr = xmmword_1804;
*((_OWORD *)ptr + 1) = unk_1814;
if ( (n & 7) == 0 )
{
if ( n >= 8 )
{
v14 = *v11;
v15 = v11[1];
v16 = (unsigned int)n >> 3;
s_1 = (__int64 *)s;
do
{
v18 = en_3des(*s_1, v14);
v19 = de_3des(v18, v15);
v20 = en_3des(v19, v14);
*s_1++ = v20;
__android_log_print(3, "native", "%llx", v20);
--v16;
}
while ( v16 );
}
ptr_1 = ptr;
if ( n <= 0 )
n_1 = 0LL;
else
n_1 = (unsigned int)n;
s_2 = s;
while ( n_1 )
{
v25 = *ptr_1++;
v24 = v25;
v26 = *s_2++;
--n_1;
if ( v24 != v26 )
{
v13 = 0;
goto LABEL_13;
}
}
v13 = 1;
}
LABEL_13:
free(ptr);
free(s);
return v13;
}

(可以看s盒来判断加密类型)逻辑就是3des加密,加密结果和xmmword_1804验证,大概是24字节,a1,a2参数是固定的,a3,a4就是传入的两个参数,所以不难猜出key就是账户的Md5,值得注意的是v11类型是__int64,8字节存储,所以会大小端序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
int main(void)
{
__int64 a = 0x7d53ecd36a43d3d2;
__int64 b = 0x37e7dd633dcf8497;
for (int i = 0; i < 8; i++)
{
printf("%02x", a >> (i * 8) & 0xff);
}
printf("\n");
for (int i = 0; i < 8; i++)
{
printf("%02x", b >> (i * 8) & 0xff);
}
return 0;
}

en_key=d2d3436ad3ec537d

de_key=9784cf3d63dde737

密文:0x8BA584B886EC9E40 0xBBB8D31AE2648A7E 0x523E454612FA4BDF

直接CyberChef选择Triple DES Decrypt

获得~DWPefaS+MY?x$y5=6mG50U5,这个也要大小端序转换因为输入的时候Safe->

S

a

f

e

0xefaS相当于是这样,所以解密出来的就是上面这样,转换后SafePWD~5y$x?YM+5U05Gm6=

NCTF{X1c@dM1n1$t_SafePWD~5y$x?YM+5U05Gm6=}

gogogo(复现)

第一次做go题,很遗憾,就差一点就有了,拖进ida

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// main.main
void __fastcall main_main()
{
int v0; // edi
int v1; // esi
__int64 v2; // rcx
int v3; // r8d
int v4; // r9d
int v5; // r10d
int v6; // r11d
int v7; // r8d
int v8; // r9d
int v9; // r10d
int v10; // r11d
__int64 v11; // rbx
int v12; // r10d
int v13; // r11d
int v14; // r8d
int v15; // r9d
int v16; // r10d
int v17; // r11d
main_coroutVM *p_main_coroutVM; // rax
int v19; // r8d
int v20; // r9d
int v21; // r10d
__int64 *v22; // r11
__m128i *ptr_1; // rdx
void *instr; // rcx
__m128i *checkres; // rbx
void *instrSet; // rsi
main_coroutVM *inputl; // rax
__int64 v28; // rcx
__int64 *v29; // r11
void *instr_1; // r8
void *checkres_1; // r9
void *instrSet_1; // r10
int p_string_1; // r10d
size_t len_1; // rdx
main_coroutVM *p_main_coroutVM_2; // rsi
uint8 *mem; // rdi
_QWORD *v37; // rax
int v38; // r8d
int v39; // r9d
int v40; // r10d
main_coroutVM **v41; // r11
__int64 inputh; // rcx
__int64 v43; // rcx
int v44; // r8d
int v45; // r9d
int v46; // r10d
int v47; // r11d
_QWORD *v48; // rax
int v49; // r8d
int v50; // r9d
int v51; // r10d
main_coroutVM **v52; // r11
__int64 inputl_2; // rcx
int v54; // r8d
int v55; // r9d
int v56; // r10d
int v57; // r11d
__int64 instr_4; // rcx
__int64 i; // rax
__int64 v60; // rcx
int v61; // r8d
int v62; // r9d
int v63; // r10d
int v64; // r11d
int v65; // r8d
int v66; // r9d
int v67; // r10d
int v68; // r11d
unsigned __int8 v69; // cl
__int64 v70; // rcx
int v71; // r8d
int v72; // r9d
int v73; // r10d
int v74; // r11d
__int64 v75; // r8
__int64 v76; // [rsp-3Ah] [rbp-E0h]
__int64 v77; // [rsp-3Ah] [rbp-E0h]
__int64 v78; // [rsp-3Ah] [rbp-E0h]
__int64 v79; // [rsp-32h] [rbp-D8h]
__int64 v80; // [rsp-32h] [rbp-D8h]
__int64 v81; // [rsp-32h] [rbp-D8h]
__int64 v82; // [rsp-2Ah] [rbp-D0h]
__int64 v83; // [rsp-22h] [rbp-C8h]
__int64 v84; // [rsp-1Ah] [rbp-C0h]
unsigned __int8 v85; // [rsp+0h] [rbp-A6h]
int v86; // [rsp+1h] [rbp-A5h] BYREF
unsigned __int8 v87; // [rsp+5h] [rbp-A1h] BYREF
size_t len; // [rsp+6h] [rbp-A0h]
__int64 i_1; // [rsp+Eh] [rbp-98h]
__int64 instr_3; // [rsp+16h] [rbp-90h]
main_coroutVM *inputl_1; // [rsp+1Eh] [rbp-88h]
__int64 instr_2; // [rsp+26h] [rbp-80h]
main_coroutVM *p_main_coroutVM_1; // [rsp+2Eh] [rbp-78h]
__int64 checkres_2; // [rsp+36h] [rbp-70h]
__m128i *_RSI_1; // [rsp+3Eh] [rbp-68h]
char *ptr; // [rsp+46h] [rbp-60h]
_QWORD v97[2]; // [rsp+4Eh] [rbp-58h] BYREF
_QWORD v98[2]; // [rsp+5Eh] [rbp-48h] BYREF
_QWORD v99[2]; // [rsp+6Eh] [rbp-38h] BYREF
_QWORD v100[2]; // [rsp+7Eh] [rbp-28h] BYREF
_QWORD v101[2]; // [rsp+8Eh] [rbp-18h] BYREF
string *p_string; // [rsp+9Eh] [rbp-8h]

instr_2 = runtime_makechan(&RTYPE_chan__4_uint8, 0);
instr_3 = runtime_makechan(&RTYPE_chan__4_uint8, 0);
checkres_2 = runtime_makechan(&RTYPE_chan_bool, 2);
p_string = runtime_newobject(&RTYPE_string, 2LL, v2, v0, v1, v3, v4, v5, v6);
v101[0] = &RTYPE_string;
v101[1] = &off_2B8B08;
fmt_Fprintln(off_2B9218, qword_350000, v101, 1, 1, v7, v8, v9, v10, v76, v79);
v100[0] = &RTYPE__ptr_string;
v100[1] = p_string;
v11 = qword_34FFF8;
fmt_Fscanf(
off_2B9238,
qword_34FFF8,
&a0ILm51YSiTvrru[19],
2,
v100,
1,
1,
v12,
v13,
v77,
v80,
v82,
HIDWORD(v82),
v83,
HIDWORD(v83),
v84,
HIDWORD(v84));
ptr = p_string->ptr;
len = p_string->len;
p_main_coroutVM = runtime_newobject(&RTYPE_main_coroutVM, v11, len, 2, v100, v14, v15, v16, v17);
ptr_1 = &qword_395420;
if ( ptr )
ptr_1 = ptr;
if ( dword_395690 )
{
p_main_coroutVM = runtime_gcWriteBarrier2(p_main_coroutVM);
instr = instr_2;
*v22 = instr_2;
checkres = checkres_2;
v22[1] = checkres_2;
}
else
{
instr = instr_2;
checkres = checkres_2;
}
_RSI_1 = ptr_1;
p_main_coroutVM->instr = instr;
p_main_coroutVM->checkres = checkres;
instrSet = ::instrSet;
if ( dword_395690 )
{
p_main_coroutVM = runtime_gcWriteBarrier1(p_main_coroutVM, checkres, instr, 2LL, ::instrSet);
*v22 = instrSet;
}
p_main_coroutVM_1 = p_main_coroutVM;
p_main_coroutVM->instrSet = instrSet;
inputl = runtime_newobject(&RTYPE_main_coroutVM, checkres, instr, 2, instrSet, v19, v20, v21, v22);
if ( dword_395690 )
{
inputl = runtime_gcWriteBarrier2(inputl);
instr_1 = instr_3;
*v29 = instr_3;
checkres_1 = checkres_2;
v29[1] = checkres_2;
}
else
{
instr_1 = instr_3;
checkres_1 = checkres_2;
}
inputl->instr = instr_1;
inputl->checkres = checkres_1;
instrSet_1 = instrSet_0;
if ( dword_395690 )
{
inputl = runtime_gcWriteBarrier1(inputl, checkres, v28, 2LL, instrSet);
*v29 = instrSet_1;
}
inputl->instrSet = instrSet_1;
p_string_1 = p_string;
if ( p_string->len == 40 )
{
len_1 = len;
if ( len >= 20 )
{
inputl_1 = inputl;
p_main_coroutVM_2 = p_main_coroutVM_1;
mem = p_main_coroutVM_1->mem;
checkres = _RSI_1;
if ( _RSI_1 != p_main_coroutVM_1->mem )
{
runtime_memmove(p_main_coroutVM_1->mem, _RSI_1, 0x14uLL);
inputl = inputl_1;
len_1 = len;
checkres = _RSI_1;
p_main_coroutVM_2 = p_main_coroutVM_1;
LODWORD(instr_1) = instr_3;
LODWORD(checkres_1) = checkres_2;
}
if ( len_1 >= 40 )
{
checkres = (checkres + 20);
if ( checkres != inputl->mem )
runtime_memmove(inputl->mem, checkres, 0x14uLL);
v37 = runtime_newobject(
&qword_27CF60,
checkres,
v28,
mem,
p_main_coroutVM_2,
instr_1,
checkres_1,
p_string_1,
v29);
*v37 = main_main_gowrap1;
if ( dword_395690 )
{
v37 = runtime_gcWriteBarrier1(v37, checkres, main_main_gowrap1, mem, p_main_coroutVM_2);
inputh = p_main_coroutVM_1;
*v41 = p_main_coroutVM_1;
}
else
{
inputh = p_main_coroutVM_1;
}
v37[1] = inputh;
runtime_newproc(v37, checkres, inputh, mem, p_main_coroutVM_2, v38, v39, v40, v41);
v48 = runtime_newobject(&qword_27CF60, checkres, v43, mem, p_main_coroutVM_2, v44, v45, v46, v47);
*v48 = main_main_gowrap2;
if ( dword_395690 )
{
v48 = runtime_gcWriteBarrier1(v48, checkres, main_main_gowrap2, mem, p_main_coroutVM_2);
inputl_2 = inputl_1;
*v52 = inputl_1;
}
else
{
inputl_2 = inputl_1;
}
v48[1] = inputl_2;
runtime_newproc(v48, checkres, inputl_2, mem, p_main_coroutVM_2, v49, v50, v51, v52);
instr_4 = instr_2;
for ( i = 0LL; ; i = i_1 )
{
if ( ::i <= i )
goto LABEL_28;
v86 = 0;
p_main_coroutVM_2 = (i + 4);
if ( qword_348480 < (i + 4) )
goto LABEL_41;
if ( i > p_main_coroutVM_2 )
break;
i_1 = i + 4;
mem = ::mem;
v75 = ((i - qword_348480) >> 63) & i;
if ( &v86 != (::mem + v75) )
v86 = *(::mem + v75);
runtime_chansend1(instr_4, &v86, instr_4, ::mem, p_main_coroutVM_2, v75, v55, v56, v57);
checkres = &v86;
runtime_chansend1(instr_3, &v86, v70, mem, p_main_coroutVM_2, v71, v72, v73, v74);
instr_4 = instr_2;
}
i = runtime_panicSliceB(i, checkres, i + 4);
LABEL_41:
inputl = runtime_panicSliceAcap(i, checkres, p_main_coroutVM_2);
}
inputl = runtime_panicSliceAcap(inputl, checkres, 40LL);
}
runtime_panicSliceAcap(inputl, checkres, 20LL);
}
v99[0] = &RTYPE_string;
v99[1] = &off_2B8B18;
LODWORD(mem) = 1;
LODWORD(p_main_coroutVM_2) = 1;
fmt_Fprintln(off_2B9218, qword_350000, v99, 1, 1, instr_1, checkres_1, p_string, v29, v78, v81);
LABEL_28:
v87 = 0;
runtime_chanrecv1(checkres_2, &v87, instr_4, mem, p_main_coroutVM_2, v54, v55, v56, v57);
v60 = v87;
v85 = v87;
v87 = 0;
runtime_chanrecv1(checkres_2, &v87, v60, mem, p_main_coroutVM_2, v61, v62, v63, v64);
v69 = v87;
v87 = v85;
if ( v85 )
v87 = v69;
if ( v87 )
{
v98[0] = &RTYPE_string;
v98[1] = &off_2B8B28;
fmt_Fprintln(off_2B9218, qword_350000, v98, 1, 1, v65, v66, v67, v68, v78, v81);
}
else
{
v97[0] = &RTYPE_string;
v97[1] = &off_2B8B38;
fmt_Fprintln(off_2B9218, qword_350000, v97, 1, 1, v65, v66, v67, v68, v78, v81);
}
}

看着很长,实际也不短,不过go语言废话多,动调分析一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fmt_Fscanf(
off_2B9238,
qword_34FFF8,
&a0ILm51YSiTvrru[19],
2,
v100,
1,
1,
v12,
v13,
v77,
v80,
v82,
HIDWORD(v82),
v83,
HIDWORD(v83),
v84,
HIDWORD(v84));
ptr = p_string->ptr;
len = p_string->len;

输入

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
if ( len_1 >= 40 )
{
checkres = (checkres + 20);
if ( checkres != inputl->mem )
runtime_memmove(inputl->mem, checkres, 0x14uLL);
v37 = runtime_newobject(
&qword_27CF60,
checkres,
v28,
mem,
p_main_coroutVM_2,
instr_1,
checkres_1,
p_string_1,
v29);
*v37 = main_main_gowrap1;
if ( dword_395690 )
{
v37 = runtime_gcWriteBarrier1(v37, checkres, main_main_gowrap1, mem, p_main_coroutVM_2);
inputh = p_main_coroutVM_1;
*v41 = p_main_coroutVM_1;
}
else
{
inputh = p_main_coroutVM_1;
}
v37[1] = inputh;
runtime_newproc(v37, checkres, inputh, mem, p_main_coroutVM_2, v38, v39, v40, v41);
v48 = runtime_newobject(&qword_27CF60, checkres, v43, mem, p_main_coroutVM_2, v44, v45, v46, v47);
*v48 = main_main_gowrap2;
if ( dword_395690 )
{
v48 = runtime_gcWriteBarrier1(v48, checkres, main_main_gowrap2, mem, p_main_coroutVM_2);
inputl_2 = inputl_1;
*v52 = inputl_1;
}
else
{
inputl_2 = inputl_1;
}
v48[1] = inputl_2;
runtime_newproc(v48, checkres, inputl_2, mem, p_main_coroutVM_2, v49, v50, v51, v52);

runtime_gcWriteBarrier1感觉是在加载函数,也就是main_main_gowrap1,main_main_gowrap2

加载之后后面runtime_newproc就是在调用了函数总之逻辑在上面这两个里(这里可以关注一下mem和mem赋值,这个是opcode)

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
// main.(*coroutVM).run
void __golang main__ptr_coroutVM_run(_ptr_main_coroutVM a1)
{
__int64 v1; // rdi
__int64 v2; // rsi
int v3; // r8d
int v4; // r9d
int v5; // r10d
int v6; // r11d
_chan_left_chan__4_uint8 instr; // rcx
int v8; // r8d
int v9; // r9d
int v10; // r10d
int v11; // r11d
map_uint8_main_handler instrSet; // rbx
__int64 **v13; // rax
__int64 v14; // [rsp+0h] [rbp-38h]
__int64 v15; // [rsp+8h] [rbp-30h]
__int64 v16; // [rsp+10h] [rbp-28h]
unsigned int v17; // [rsp+27h] [rbp-11h]
unsigned int v18; // [rsp+2Bh] [rbp-Dh] BYREF
char v19; // [rsp+2Fh] [rbp-9h] BYREF
_chan_left_chan__4_uint8 i; // [rsp+30h] [rbp-8h]

instr = a1->instr;
for ( i = instr; runtime_chanrecv2(instr, &v18, instr, v1, v2, v3, v4, v5, v6); instr = i )
{
v17 = v18;
v18 = 0;
v2 = (v17 >> 8);
v1 = HIBYTE(v17);
v19 = v17;
instrSet = a1->instrSet;
v13 = runtime_mapaccess2(
&RTYPE_map_uint8_main_handler,
instrSet,
&v19,
HIBYTE(v17),
v2,
v8,
v9,
v10,
v11,
v14,
v15,
v16);
if ( instrSet )
{
v2 = **v13;
LOWORD(v14) = v17 >> 8;
BYTE2(v14) = HIBYTE(v17);
(v2)(a1);
if ( v17 == 0xFF )
break;
}
}
}

这个就是主要逻辑了,runtime_chanrecv2是在解析instr然后给v18,给了v18之后v18给v17再次解析(这里的instr就是opcode的当前部分?或者是总体指针没太关注)

最后解析结果给v13,后面v13给了v2,v2动调发现是一个叫main_mov的函数(并且会变)跟踪者往上寻找

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
.rdata:0000000000299F60 add             dq offset main_ADD      ; DATA XREF: main_map_init_0:loc_267F5B↑o
.rdata:0000000000299F60 ; main_map_init_1:loc_2682FB↑o
.rdata:0000000000299F68 and dq offset main_AND ; DATA XREF: main_map_init_0:loc_2680B3↑o
.rdata:0000000000299F68 ; main_map_init_1:loc_268453↑o
.rdata:0000000000299F70 hlt dq offset main_HLT ; DATA XREF: main_map_init_0:loc_268141↑o
.rdata:0000000000299F70 ; main_map_init_1:loc_2684E1↑o
.rdata:0000000000299F78 ldr dq offset main_LDR ; DATA XREF: main_map_init_0:loc_267E3B↑o
.rdata:0000000000299F78 ; main_map_init_1:loc_2681DB↑o
.rdata:0000000000299F80 ldri dq offset main_LDRI ; DATA XREF: main_map_init_0:loc_267E73↑o
.rdata:0000000000299F80 ; main_map_init_1:loc_268213↑o
.rdata:0000000000299F88 lsl dq offset main_LSL ; DATA XREF: main_map_init_0:loc_268008↑o
.rdata:0000000000299F88 ; main_map_init_1:loc_2683A8↑o
.rdata:0000000000299F90 lsr dq offset main_LSR ; DATA XREF: main_map_init_0:loc_268040↑o
.rdata:0000000000299F90 ; main_map_init_1:loc_2683E0↑o
.rdata:0000000000299F98 mov dq offset main_MOV ; DATA XREF: main_map_init_0:loc_267F20↑o
.rdata:0000000000299F98 ; main_map_init_1:loc_2682C0↑o
.rdata:0000000000299FA0 mul dq offset main_MUL ; DATA XREF: main_map_init_0:loc_267FCD↑o
.rdata:0000000000299FA0 ; main_map_init_1:loc_26836D↑o
.rdata:0000000000299FA8 ret dq offset main_RET ; DATA XREF: main_map_init_0:loc_2680ED↑o
.rdata:0000000000299FA8 ; main_map_init_1:loc_26848D↑o
.rdata:0000000000299FB0 str dq offset main_STR ; DATA XREF: main_map_init_0:loc_267EAD↑o
.rdata:0000000000299FB0 ; main_map_init_1:loc_26824D↑o
.rdata:0000000000299FB8 stri dq offset main_STRI ; DATA XREF: main_map_init_0:loc_267EE8↑o
.rdata:0000000000299FB8 ; main_map_init_1:loc_268288↑o
.rdata:0000000000299FC0 sub dq offset main_SUB ; DATA XREF: main_map_init_0:loc_267F93↑o
.rdata:0000000000299FC0 ; main_map_init_1:loc_268333↑o
.rdata:0000000000299FC8 xor dq offset main_XOR ; DATA XREF: main_map_init_0:loc_26807B↑o
.rdata:0000000000299FC8 ; main_map_init_1:loc_26841B↑o

有点像vm的操作码了,并且在动调找操作码对应hex的时候,也就是runtime_mapaccess2解析后返回函数指针

1
2
3
4
v41 = &v48[size];
if ( (p_RTYPE_map_uint8_main_handler_1[2].ptrdata & 8) != 0 )
return *&v48[size];
return v41;

这里有可能返回v41,所以动调去看v41里的值

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
debug056:000000C000090000 qword_C000090000 dq 808041457215144Eh   ; DATA XREF: debug056:000000C000081F68↑o
debug056:000000C000090008 dq 12h
debug056:000000C000090010 dq offset ldri
debug056:000000C000090018 dq 15h
debug056:000000C000090020 dq offset str
debug056:000000C000090028 dq 16h
debug056:000000C000090030 dq offset stri
debug056:000000C000090038 qword_C000090038 dq 2Ah ; DATA XREF: debug056:000000C000081F58↑o
debug056:000000C000090040 dq offset mov
debug056:000000C000090048 dq 42h
debug056:000000C000090050 dq offset sub
debug056:000000C000090058 dq 73h
debug056:000000C000090060 dq offset lsr
debug056:000000C000090068 dq 0
debug056:000000C000090070 dq 0
debug056:000000C000090078 dq 0
debug056:000000C000090080 dq 0
debug056:000000C000090088 dq 7E70506902607B66h
debug056:000000C000090090 dq 11h
debug056:000000C000090098 dq offset ldr
debug056:000000C0000900A0 dq 41h
debug056:000000C0000900A8 dq offset add
debug056:000000C0000900B0 dq 47h
debug056:000000C0000900B8 dq offset mul
debug056:000000C0000900C0 dq 71h
debug056:000000C0000900C8 dq offset lsl
debug056:000000C0000900D0 dq 7Ah
debug056:000000C0000900D8 dq offset xor
debug056:000000C0000900E0 dq 7Bh
debug056:000000C0000900E8 dq offset and
debug056:000000C0000900F0 dq 0FEh
debug056:000000C0000900F8 dq offset ret
debug056:000000C000090100 dq 0FFh
debug056:000000C000090108 dq offset hlt
debug056:000000C000090110 dq 0
debug056:000000C000090118 dq 0
debug056:000000C000090120 dq 80805B7F4D195072h
debug056:000000C000090128 dq 14h
debug056:000000C000090130 dq offset ldri
debug056:000000C000090138 dq 18h
debug056:000000C000090140 dq offset stri
debug056:000000C000090148 dq 2Bh
debug056:000000C000090150 dq offset mov
debug056:000000C000090158 dq 0CAh
debug056:000000C000090160 dq offset xor
debug056:000000C000090168 dq 0FEh
debug056:000000C000090170 dq offset ret
debug056:000000C000090178 dq 0FFh
debug056:000000C000090180 dq offset hlt
debug056:000000C000090188 dq 0
debug056:000000C000090190 dq 0
debug056:000000C000090198 dq 0
debug056:000000C0000901A0 dq 0
debug056:000000C0000901A8 dq 497C2A1F61216141h
debug056:000000C0000901B0 dq 13h
debug056:000000C0000901B8 dq offset ldr
debug056:000000C0000901C0 dq 17h
debug056:000000C0000901C8 dq offset str
debug056:000000C0000901D0 dq 91h
debug056:000000C0000901D8 dq offset add
debug056:000000C0000901E0 dq 92h
debug056:000000C0000901E8 dq offset sub
debug056:000000C0000901F0 dq 97h
debug056:000000C0000901F8 dq offset mul
debug056:000000C000090200 dq 0C1h
debug056:000000C000090208 dq offset lsl
debug056:000000C000090210 dq 0C3h
debug056:000000C000090218 dq offset lsr
debug056:000000C000090220 dq 0CBh
debug056:000000C000090228 dq offset and

这里一个hex紧跟着一个操作函数,并且0x2A对应mov在之前,v17中经常出现,本来我以为这个v17是输入,但是多次改变输入发现仍然会是0x2A, 0x00, 0x37, 0x9E,后面看到这里再加上之前在v2函数跟踪的时候点到过mem,然后看到了0x2A, 0x00, 0x37, 0x9E发现十分熟悉,所以猜测出mem是opcode(后面我才知道可以看结构体的定义来猜测)

于是有了操作函数对应字节码,有opcode,就可以解密了,因为这个调用的函数相同并且都是opcode[i+0]做字节码,opcode[i+1,2,3]参与运算,并且函数固定,所以这个vm不是很难写

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include<stdio.h>  
#include<string.h>
#include<stdlib.h>
#include <stdbool.h>
#include<Windows.h>
unsigned char o[19457] = {
......
}
int main() {
int ax, bx, cx, dx;
int abcdx[16] = { 0 };
unsigned char stack[256] = { 0 };
for (size_t i = 0; i < 19444; i += 4)
{
printf("%x %x %x %x:", o[i], o[i + 1], o[i + 2], o[i + 3]);
switch (o[i]) {
//ldr
case 0x11:
printf("abcdx[%d] = %d //stack[%d]\n", o[i + 1] & 0xf, stack[o[i + 2]], o[i + 2]);
abcdx[o[i + 1]] = stack[o[i + 2]];
break;
case 0x13:
printf("abcdx[%d] = %d //stack[%d]\n", o[i + 1] & 0xf, stack[o[i + 2]], o[i + 2]);
abcdx[o[i + 1]] = stack[o[i + 2]];
break;
//ldri
case 0x12:
printf("------------------------------\n");
printf("abcdx[%d] = stack[%d]\n", o[i + 1] & 0xf, o[i + 3]);
abcdx[o[i + 1] & 0xf] = stack[o[i + 3]];
break;
case 0x14:
printf("------------------------------\n");
printf("abcdx[%d] = stack[%d]\n", o[i + 1] & 0xf, o[i + 3]);
abcdx[o[i + 1] & 0xf] = stack[o[i + 3]];
break;
//str
case 0x15:
printf("stack[%d] = %d //stack[%d]\n", o[i + 2], abcdx[o[i + 1] & 0xf], o[i + 1]);
stack[o[i + 2]] = abcdx[o[i + 1] & 0xf];
break;
case 0x17:
printf("stack[%d] = %d //stack[%d]\n", o[i + 2], abcdx[o[i + 1] & 0xf], o[i + 1]);
stack[o[i + 2]] = abcdx[o[i + 1] & 0xf];
break;
//stri
case 0x16:
printf("stack[%d] = %d //stack[%d]\n", o[i + 3], abcdx[o[i + 1] & 0xf], o[i + 1]);
stack[o[i + 3]] = abcdx[o[i + 1] & 0xf];
break;
case 0x18:
printf("stack[%d] = %d //stack[%d]\n", o[i + 3], abcdx[o[i + 1] & 0xf], o[i + 1]);
stack[o[i + 3]] = abcdx[o[i + 1] & 0xf];
break;
//mov
case 0x2A:
printf("abcdx[%d] = %x%x //", o[i + 1], o[i + 3], o[i + 2]);
ax = o[i + 2];
ax += o[i + 3] << 8;
abcdx[(o[i + 1]) & 0xf] = ax;
printf("%04x\n", abcdx[(o[i + 1]) & 0xf]);
break;
case 0x2B:
printf("abcdx[%d] = %x%x //", o[i + 1], o[i + 3], o[i + 2]);
ax = o[i + 2];
ax += o[i + 3] << 8;
abcdx[(o[i + 1]) & 0xf] = ax;
printf("%04x\n", abcdx[(o[i + 1]) & 0xf]);
break;
//add
case 0x91:
printf("abcdx[%d] = %d //%d + abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] + abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0x41:
printf("abcdx[%d] = %d //%d + abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] + abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
//sub
case 0x42:
printf("abcdx[%d] = %d //%d - abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] - abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0x92:
printf("abcdx[%d] = %d //%d - abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] - abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
//mul
case 0x47:
printf("abcdx[%d] = %d //%d * abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] * abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0x97:
printf("abcdx[%d] = %d //%d * abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] * abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
//xor
case 0x7A:
printf("abcdx[%d] = %d //%d + abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] ^ abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0xca:
printf("abcdx[%d] = %d //%d + abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] ^ abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
//lsl
case 0x71:
printf("abcdx[%d] = %d //%d << abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] << abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0xc1:
printf("abcdx[%d] = %d //%d << abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] << abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
//lsr
case 0x73:
printf("abcdx[%d] = %d //%d >> abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] >> abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0xc3:
printf("abcdx[%d] = %d //%d >> abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] >> abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
//and
case 0xcb:
printf("abcdx[%d] = %d //%d & abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] & abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;
case 0x7B:
printf("abcdx[%d] = %d //%d & abcdx[%d] ", o[i + 1] & 0xf, o[i + 2] & abcdx[o[i + 3] & 0xf], o[i + 2], o[i + 3] & 0xf);
abcdx[(o[i + 1]) & 0xf] = o[i + 2] + abcdx[(o[i + 3]) & 0xf];
printf("abcdx[%d] = %d\n", (o[i + 1]) & 0xf, abcdx[(o[i + 1]) & 0xf]);
break;




case 0xff:
printf("hlt\n");
break;
case 0xfe:
printf("ret\n");
break;
}
}
for (int i = 0; i < 0x100; i++)
{
printf("%02x ", stack[i]);
}
printf("\n");
for (int i = 0; i < 0x10; i++)
{
printf("%02x ", abcdx[i]);
}
return 0;

}

但是这玩意太太太长了。。。懒得分析了,到此结束

。。。是xxtea。。。不做了