未分类re题集

题目清单

  • QCTF asong
  • 网鼎杯 blend
  • hackergame2018 confused flxg

QCTF asong

这道题目逻辑理清楚之后其实很简单,所有的算法都是可逆的。比较坑的两点是sub_400D33函数很关键,但是IDA对它的反汇编一言难尽,要去看汇编;另外sub_400DB4函数其实是对数组的循环移位,一开始看真的没发现,后来看了官方writeup才反应过来,当然也有的大佬的writeup是爆破的,很强。逆向果然是一个考验耐心的工作。

  • 分析sub_400DB4函数

    对out中的数据做最后一步处理的关键代码

    一开始没发现,后来看了别人的writeup发现,是一个数组向左循环移动三位的功能的代码,于是写出解密函 数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main()
{
unsigned char arr[] = { 0xEC,0x29,0xE3,0x41,0xE1,0xF7,0xAA,0x1D,0x29,0xED,0x29,0x99,0x39,0xF3,0xB7,0xA9,0xE7,0xAC,0x2B,0xB7,0xAB,0x40,0x9F,0xA9,0x31,0x35,0x2C,0x29,0xEF,0xA8,0x3D,0x4B,0xB0,0xE9,0xE1,0x68,0x7B,0x41 };
int len = sizeof(arr) / sizeof(arr[0]);
unsigned char temp = arr[len - 1] << 5;
for (int i = len - 1; i >0; i--) {
arr[i] = arr[i] >> 3 | arr[i - 1] << 5;
}
arr[0] = arr[0] >> 3 | temp;
for (int i = 0; i < len; i++) {
printf("%x", arr[i]);
}
}

得到结果如下

1
0x3d,0x85,0x3c,0x68,0x3c,0x3e,0xf5,0x43,0xa5,0x3d,0xa5,0x33,0x27,0x3e,0x76,0xf5,0x3c,0xf5,0x85,0x76,0xf5,0x68,0x13,0xf5,0x26,0x26,0xa5,0x85,0x3d,0xf5,0x7,0xa9,0x76,0x1d,0x3c,0x2d,0xf,0x68
  • 分析sub_400D33函数

    再往前是sub_400D33函数对输出字符串的处理,F5之后是如下代码,

    上述反编译代码存在一些问题,直接看汇编发现是如下一段置换代码,肯定是可逆的。

1
2
3
4
5
6
7
8
9
10
sub_400D33(unsigned char* a){
int t = 0;
int* b = dword_6020A0;
unsigned char c = a[0];
while(b[t]){
a[t] = a[b[t]];
t = b[t];
}
a[t] = c;
}

动态调试得到其加密置换的顺序如下

1
0->0x16->0x14->0x13->0xe->0x11->0x4->0x1e->0x1d->0x1c->0x1b->0x24->0x22->0x21->0x20->0x1f->0x25->0x23->0x1a->0x19->0x5->0x18->0xf->0x17->0x10->0xd->0xc->0x8->0x15->0xb->0xa->0x12->0x3->0x2->0x6->0x9->0x7->0x1->0

编写解密置换代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
int main()
{
int b[] = { 0x16,0x14,0x13,0xe,0x11,0x4,0x1e,0x1d,0x1c,0x1b,0x24,0x22,0x21,0x20,0x1f,0x25,0x23,0x1a,0x19,0x5,0x18,0xf,0x17,0x10,0xd,0xc,0x8,0x15,0xb,0xa,0x12,0x3,0x2,0x6,0x9,0x7,0x1,0 };
unsigned char a[] = { 0x3d,0x85,0x3c,0x68,0x3c,0x3e,0xf5,0x43,0xa5,0x3d,0xa5,0x33,0x27,0x3e,0x76,0xf5,0x3c,0xf5,0x85,0x76,0xf5,0x68,0x13,0xf5,0x26,0x26,0xa5,0x85,0x3d,0xf5,0x7,0xa9,0x76,0x1d,0x3c,0x2d,0xf,0x68 };
int t = (sizeof(b) / sizeof(b[0])) - 1;
unsigned char c = a[b[t]];
for (int i = t; i > 0; i--) {
a[b[i]] = a[b[i - 1]];
}
a[b[0]] = a[0];
for (int i = 0; i <= t; i++) {
printf("0x%x,", a[i]);
}
}

结果如下

1
0x85,0x43,0x68,0x85,0xf5,0x26,0x3c,0x3d,0x27,0xf5,0x33,0x68,0x3e,0x3c,0x76,0x26,0xf5,0x76,0xa5,0xf5,0x13,0xa5,0x85,0xf5,0x3e,0xa5,0x2d,0x3d,0xf5,0x7,0x3c,0x76,0x1d,0x3c,0xf,0x68,0x85,0xa9
  • 分析sub_400E54函数

    再往前看对输出字符串的处理,为如下函数中的for循环部分

a1为输入字符串,我们进入sub_400936函数发现,是一段switch语句,如下

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
int __fastcall sub_400936(char a1)
{
int result; // eax

result = a1 - 10;
switch ( a1 )
{
case 10:
result = a1 + 35;
break;
case 32:
case 33:
case 34:
result = a1 + 10;
break;
case 39:
result = a1 + 2;
break;
case 44:
result = a1 - 4;
break;
case 46:
result = a1 - 7;
break;
case 58:
case 59:
result = a1 - 21;
break;
case 63:
result = a1 - 27;
break;
case 95:
result = a1 - 49;
break;
default:
if ( a1 <= 47 || a1 > 57 )
{
if ( a1 <= 64 || a1 > 90 )
{
if ( a1 > 96 && a1 <= 122 )
result = a1 - 87;
}
else
{
result = a1 - 55;
}
}
else
{
result = a1 - 48;
}
break;
}
return result;
}

这里写逆算法太麻烦,直接把这个函数复制下来,爆破。调试过程中发现每位符合条件的ascii码不确定,更具语义划定爆破范围。爆破代码如下:

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
#include <stdio.h>
int sub_400936(char a1);
int main()
{
unsigned char arr[] = { 0x85,0x43,0x68,0x85,0xf5,0x26,0x3c,0x3d,0x27,0xf5,0x33,0x68,0x3e,0x3c,0x76,0x26,0xf5,0x76,0xa5,0xf5,0x13,0xa5,0x85,0xf5,0x3e,0xa5,0x2d,0x3d,0xf5,0x7,0x3c,0x76,0x1d,0x3c,0xf,0x68,0x85,0xa9 };
unsigned char b[] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x68,0x1e,0xf,0x1d,0xa9,0x13,0x26,0x43,0x3c,0x0,0x14,0x27,0x1c,0x76,0xa5,0x1a,0x0,0x3d,0x33,0x85,0x2d,0x7,0x22,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x28,0x47,0x0,0x0,0x42,0xf5,0x0,0x0,0x0,0x61,0x0 };

int t = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < t; i++) {
for (unsigned char c = 'A'; c <= '_'; c++) {
if (arr[i] == b[sub_400936(c)])
printf("%c", c);
}
}
printf("\n");
for (int i = 0; i < t; i++) {
for (unsigned char c = '_'; c <= 'z'; c++) {
if (arr[i] == b[sub_400936(c)])
printf("%c", c);
}
}
}
int sub_400936(char a1)
{
int result; // rax

result = a1 - 10;
switch ( a1 )
{
case 10:
result = a1 + 35;
break;
case 32:
case 33:
case 34:
result = a1 + 10;
break;
case 39:
result = a1 + 2;
break;
case 44:
result = a1 - 4;
break;
case 46:
result = a1 - 7;
break;
case 58:
case 59:
result = a1 - 21;
break;
case 63:
result = a1 - 27;
break;
case 95:
result = a1 - 49;
break;
default:
if ( a1 <= 47 || a1 > 57 )
{
if ( a1 <= 64 || a1 > 90 )
{
if ( a1 > 96 && a1 <= 122 )
result = a1 - 87;
}
else
{
result = a1 - 55;
}
}
else
{
result = a1 - 48;
}
break;
}
return result;
}

结果如下:

1
2
THAT_GIRL_SAYING_NO_FOT_YOUR_VINDICATE
that_girl_saying_no_fot_your_vindicate
  • 得到flag

    最终flag有两个,如下

1
2
QCTF{THAT_GIRL_SAYING_NO_FOT_YOUR_VINDICATE}
QCTF{that_girl_saying_no_fot_your_vindicate}

网鼎杯 blend

这道题目主要是学习一下SSE指令集和用qemu模拟运行MBR并用gdb动态调试。

  • 静态分析

把文件拖到IDA中,用16位汇编分析,发现其大概逻辑是先判断输入是否是flag开头,然后剩下的部分进行如下判断

但是”cmp edi, [edx+7DA8h]”这个语句中,被比较数无法确定,所以需要开动态调试确定

  • 动态调试

用qemu模拟程序运行并用gdb attach进程进行调试,模拟程序运行的代码如下,-s使得这个程序可以attach

1
qemu-system-i386 -s -drive format=raw,file=./main.bin

然后打开gdb并执行以下命令

1
2
3
4
5
6
7
8
(gdb) set architecture i8086
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB. Attempting to continue with the default i8086 settings.

The target architecture is assumed to be i8086
(gdb) set disassembly-flavor intel
(gdb) target remote:1234
Remote debugging using :1234

然后就可以开始下断点调试,由于MBR代码是从0x7c00处开始的,注意下断点的时候要把ida中显示的offset与起始位置相加。

1
2
3
4
5
6
7
8
9
10
11
(gdb) b *0x7cB2
Breakpoint 1 at 0x7cb2
(gdb) continue
Continuing.

Breakpoint 1, 0x00007cb2 in ?? ()
(gdb) nexti
0x00007cba in ?? ()
(gdb) x/8x 0x7DA8
0x7da8: 0x02dd02f6 0x02dc02e8 0x02d802ed 0x02ce02e2
0x7db8: 0x02c402e2 0x02d402db 0x02d902cd 0x03110304

最后就可以根据这些数据编写爆破代码,这里借用z3求解器

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
from z3 import *
s = Solver()
a = Int('a')
b = Int('b')
c = Int('c')
d = Int('d')
e = Int('e')
f = Int('f')
g = Int('g')
h = Int('h')
i = Int('i')
j = Int('j')
k = Int('k')
l = Int('l')
m = Int('m')
n = Int('n')
o = Int('o')
p = Int('p')
s.add(a < 127)
s.add(b < 127)
s.add(c < 127)
s.add(d < 127)
s.add(e < 127)
s.add(f < 127)
s.add(g < 127)
s.add(h < 127)
s.add(i < 127)
s.add(j < 127)
s.add(k < 127)
s.add(l < 127)
s.add(m < 127)
s.add(n < 127)
s.add(o < 127)
s.add(p < 127)
s.add(a > 32)
s.add(b > 32)
s.add(c > 32)
s.add(d > 32)
s.add(e > 32)
s.add(f > 32)
s.add(g > 32)
s.add(h > 32)
s.add(i > 32)
s.add(j > 32)
s.add(k > 32)
s.add(l > 32)
s.add(m > 32)
s.add(n > 32)
s.add(o > 32)
s.add(p > 32)

def abs(x):
return If(x >= 0,x,-x)

s.add(abs(d-0x22)+abs(c-0xf)+abs(b-0x2)+abs(a-0xc8)+abs(h-0x83)+abs(g-0xfb)+abs(f-0xe0)+abs(0-0x83) ==0x304)
s.add(abs(p-0xc0)+abs(o-0x20)+abs(n-0xf)+abs(m-0x10)+abs(l-0xcd)+abs(k-0x00)+abs(j-0x13)+abs(0-0xb8) ==0x311)
s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(0-0x3)+abs(e-0x4) ==0x2cd)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x00)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(0-0x3)+abs(i-0x11) ==0x2d9)

s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(0-0x0)+abs(f-0x2)+abs(e-0xcd) ==0x2db)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x0)+abs(m-0x0)+abs(l-0x0)+abs(0-0x00)+abs(j-0x2)+abs(i-0xd9) ==0x2d4)

s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(0-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xdb) ==0x2e2)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x0)+abs(m-0x0)+abs(0-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xd4) ==0x2c4)

s.add(abs(d-0x0)+abs(c-0x0)+abs(b-0x0)+abs(0-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xe2) ==0x2e2)
s.add(abs(p-0x0)+abs(o-0x0)+abs(n-0x0)+abs(0-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xc4) ==0x2ce)

s.add(abs(d-0x0)+abs(c-0x0)+abs(0-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xe2) ==0x2ed)
s.add(abs(p-0x0)+abs(o-0x0)+abs(0-0x0)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xce) ==0x2d8)

s.add(abs(d-0x0)+abs(0-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xed) ==0x2e8)
s.add(abs(p-0x0)+abs(0-0x0)+abs(n-0x0)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xd8) ==0x2dc)

s.add(abs(0-0x0)+abs(c-0x0)+abs(b-0x0)+abs(a-0x0)+abs(h-0x0)+abs(g-0x0)+abs(f-0x2)+abs(e-0xe8) ==0x2f6)
s.add(abs(0-0x0)+abs(o-0x0)+abs(n-0x0)+abs(m-0x0)+abs(l-0x0)+abs(k-0x00)+abs(j-0x2)+abs(i-0xdc) ==0x2dd)

if s.check() == sat:
model = s.model()
answer = [model[a],model[b],model[c],model[d],model[e],model[f],model[g],model[h],model[i],model[j],model[k],model[l],model[m],model[n],model[o],model[p],0]
flag = ""
answer_str = ""
for i in range(len(answer)-1):
answer_str += chr(int(str(answer[i])))
print(answer_str)
else:
print('unsat')

得到falg如下

1
flag{mbr_is_funny__eh}

hackergame2018 confused flxg

打开ida时添加参数-dCULTURE=al,就可以看到中文字符串如下

1539785391712

通过交叉引用定位到关键函数sub_14000498F如下

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
void __usercall sub_14000498F(__int64 a1@<rbp>)
{
unsigned __int8 *v1; // rax
unsigned __int8 v2; // dl
int v3; // eax

*(_QWORD *)(a1 + 112) = a1 + 384;
*(_QWORD *)(a1 + 40) = -1i64;
do
++*(_QWORD *)(a1 + 40);
while ( *(_BYTE *)(*(_QWORD *)(a1 + 112) + *(_QWORD *)(a1 + 40)) );
*(_DWORD *)(a1 + 64) = *(_QWORD *)(a1 + 40);
qmemcpy((void *)(a1 + 800), &unk_1400054D8, 0x39ui64);
memset((void *)(a1 + 857), 0, 0x8Fui64);
sub_140001590(a1 + 144, a1 + 384, *(unsigned int *)(a1 + 64));
memset((void *)(a1 + 176), 0, 0xC8ui64);
*(_QWORD *)(a1 + 80) = sub_140001A40(a1 + 144);
*(_QWORD *)(a1 + 48) = a1 + 176;
*(_QWORD *)(a1 + 136) = *(_QWORD *)(a1 + 48);
do
{
*(_BYTE *)(a1 + 32) = **(_BYTE **)(a1 + 80);
**(_BYTE **)(a1 + 48) = *(_BYTE *)(a1 + 32);
++*(_QWORD *)(a1 + 80);
++*(_QWORD *)(a1 + 48);
}
while ( *(_BYTE *)(a1 + 0x20) );
strrev((char *)(a1 + 176));
memset((void *)(a1 + 592), 0, 0xC8ui64);
for ( *(_DWORD *)(a1 + 36) = 0; ; ++*(_DWORD *)(a1 + 36) )
{
*(_QWORD *)(a1 + 120) = a1 + 176;
*(_QWORD *)(a1 + 56) = -1i64;
do
++*(_QWORD *)(a1 + 56);
while ( *(_BYTE *)(*(_QWORD *)(a1 + 120) + *(_QWORD *)(a1 + 56)) );
if ( (unsigned __int64)*(signed int *)(a1 + 36) >= *(_QWORD *)(a1 + 56) )
break;
*(_BYTE *)(a1 + *(signed int *)(a1 + 36) + 592) = *(_BYTE *)(a1 + 36) ^ *(_BYTE *)(a1
+ *(signed int *)(a1 + 36)
+ 176);
}
v1 = (unsigned __int8 *)(a1 + 592);
while ( 1 )
{
v2 = *v1;
if ( *v1 != v1[208] )
break;
++v1;
if ( !v2 )
{
v3 = 0;
goto LABEL_15;
}
}
v3 = -(v2 < v1[208]) | 1;
LABEL_15:
if ( v3 )
{
sub_1400024B0(std::cout, "\n你的flxg不正确!");
*(_DWORD *)(a1 + 68) = 0;
}
else
{
sub_1400024B0(std::cout, "\n祝贺你,你输入的flxg是正确的!");
*(_DWORD *)(a1 + 72) = 0;
}
sub_140001AE0(a1 + 144);
JUMPOUT(&loc_1400019F4);
}

在阅读上述代码的时候要注意将a1 + 36这个整体看作一个变量。于是编写解密函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;
int main()
{
unsigned char ida_chars[] =
{
0x39, 0x65,
0x45, 0x54, 0x77, 0x5F, 0x34, 0x5F, 0x64, 0x5F, 0x66, 0x68,
0x3C, 0x34, 0x58, 0x55, 0x7F, 0x43, 0x21, 0x4B, 0x7F, 0x20,
0x43, 0x76, 0x5F, 0x20, 0x4C, 0x4D, 0x7A, 0x53, 0x70, 0x7D,
0x56, 0x4D, 0x65, 0x47, 0x4C, 0x5D, 0x71, 0x43, 0x18, 0x6F,
0x47, 0x48, 0x42, 0x18, 0x1C, 0x4D, 0x74, 0x45, 0x01, 0x69,
0x00, 0x4D, 0x5B, 0x6D
};
for (int i = 0; i < sizeof(ida_chars) / sizeof(unsigned char); i++) {
ida_chars[i] ^= i;
}
for (int i = sizeof(ida_chars) / sizeof(unsigned char); i > 0; i--) {
cout << ida_chars[i-1];
}
cout << endl;
}

运行得到结果如下

1
Zmx4Z3tDb25ncmF0dWxhdGlvbnNfVV9GaU5kX3RoZV90clVlX2ZsWGd9

是一个base64编码的字符串,解密得到flag

flxg{Congratulations_U_FiNd_the_trUe_flXg}

也可以写python脚本如下

1
2
3
4
5
6
7
8
import base64
list =[0x39, 0x65, 0x45, 0x54, 0x77, 0x5F, 0x34, 0x5F, 0x64, 0x5F, 0x66, 0x68,0x3C, 0x34, 0x58, 0x55, 0x7F, 0x43, 0x21, 0x4B, 0x7F, 0x20, 0x43, 0x76, 0x5F, 0x20, 0x4C, 0x4D, 0x7A, 0x53, 0x70, 0x7D, 0x56, 0x4D, 0x65, 0x47, 0x4C, 0x5D, 0x71, 0x43, 0x18, 0x6F, 0x47, 0x48, 0x42, 0x18, 0x1C, 0x4D, 0x74, 0x45, 0x01, 0x69, 0x00, 0x4D, 0x5B, 0x6D]
i = 0
flxg=""
for c in list:
flxg = flxg+ str(chr(c ^ i))
i += 1
print base64.b64decode(flxg[::-1])

同样可以得到flag

flxg{Congratulations_U_FiNd_the_trUe_flXg}

0%