NEKO

南邮Reverse

2017/12/11

南邮

南邮oj:http://ctf.nuptsast.com/challenges#ReadAsm2

Hello,RE!

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+11h] [ebp-7Fh]
int v5; // [esp+75h] [ebp-1Bh]
int v6; // [esp+79h] [ebp-17h]
int v7; // [esp+7Dh] [ebp-13h]
int v8; // [esp+81h] [ebp-Fh]
int v9; // [esp+85h] [ebp-Bh]
int v10; // [esp+89h] [ebp-7h]
__int16 v11; // [esp+8Dh] [ebp-3h]
char v12; // [esp+8Fh] [ebp-1h]

__main();
printf(&fmt);
v5 = 'galf';
v6 = 'leW{';
v7 = 'emoc';
v8 = '_oT_';
v9 = 'W_ER';
v10 = 'dlro';
v11 = '}!';
v12 = '\0';
while ( scanf("%s", &v4) != -1 && strcmp(&v4, (const char *)&v5) )
printf(aFlag);
printf(aFlag_0);
printf(&byte_410030);
printf(&byte_410064);
printf(&byte_41008F);
getchar();
getchar();
return 0;
}

ida里R键可以将数字转换为字符串,注意小端序。
也可以用python脚本

1
2
3
4
5
6
7
8
import codecs
str=[1734437990,1818580859,1701670755,1601131615,1465861458,1684828783,32033]
s=''
for i in str:
m=hex(i)[2:]
for j in range(len(m)-2,-1,-2):
s=s+m[j:j+2]
print(codecs.decode(s,'hex_codec').decode())

od

先搜索字符串,跳到比较位置,strcmp处断点

ReadAsm2

一篇好文章:https://www.cnblogs.com/bangerlee/archive/2012/05/22/2508772.html
汇编教程:http://study.163.com/course/courseMain.htm?courseId=1640004#/courseDetail

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
00000000004004e6 <func>:
4004e6: 55 push rbp
4004e7: 48 89 e5 mov rbp,rsp
4004ea: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi input[0]的8字节地址(指针)存入栈
4004ee: 89 75 e4 mov DWORD PTR [rbp-0x1c],esi 28(int型 4字节)存入栈
4004f1: c7 45 fc 01 00 00 00 mov DWORD PTR [rbp-0x4],0x1 i=1
4004f8: eb 28 jmp 400522 <func+0x3c> for循环
4004fa: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] eax=i
4004fd: 48 63 d0 movsxd rdx,eax rdx=i
400500: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18] rax=input[0]的地址
400504: 48 01 d0 add rax,rdx rax=input[i]的地址
400507: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] edx=i
40050a: 48 63 ca movsxd rcx,edx rcx=i
40050d: 48 8b 55 e8 mov rdx,QWORD PTR [rbp-0x18] rdx=input[0]的地址
400511: 48 01 ca add rdx,rcx rdx=input[i]的地址
400514: 0f b6 0a movzx ecx,BYTE PTR [rdx] ecx=input[i]指针所指向地址的内容
400517: 8b 55 fc mov edx,DWORD PTR [rbp-0x4] edx=i
40051a: 31 ca xor edx,ecx i^input[i],结果存入dl
40051c: 88 10 mov BYTE PTR [rax],dl 结果存入input[i]
40051e: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1 i++
400522: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] eax=i
400525: 3b 45 e4 cmp eax,DWORD PTR [rbp-0x1c] 比较i和28的大小
400528: 7e d0 jle 4004fa <func+0x14> 小于等于则继续循环
40052a: 90 nop
40052b: 5d pop rbp
40052c: c3 ret

python脚本:

1
2
3
input=[0x67,0x6e,0x62,0x63,0x7e,0x74,0x62,0x69,0x6d,0x55,0x6a,0x7f,0x60,0x51,0x66,0x63,0x4e,0x66,0x7b,0x71,0x4a,0x74,0x76,0x6b,0x70,0x79,0x66,0x1c]
for i in range(28):
print(chr((i+1)^input[i]),end='')

py交易

pyc反编译:https://tool.lu/pyc/
反编译结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import base64

def encode(message):
s = ''
for i in message:
x = ord(i) ^ 32
x = x + 16
s += chr(x)

return base64.b64encode(s)

correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'
flag = ''
print 'Input flag:'
flag = raw_input()
if encode(flag) == correct:
print 'correct'
else:
print 'wrong'

解密脚本:

1
2
import base64
print(''.join([chr((i-16)^32) for i in base64.b64decode('XlNkVmtUI1MgXWBZXCFeKY+AaXNt')]))

WxyVM

思路很简单,输入flag后经sub_4005B6()处理后和dword_601060处进行比较.
逆过来求flag即可,主要是要会在ida里写代码,shift+F2调出脚本执行页面,ida里的python脚本语法是基于idc语法的.

看下sub_4005B6():

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
for ( i = 0; i <= 14999; i += 3 )
{
v0 = byte_6010C0[i];
v3 = byte_6010C0[i + 2];
result = v0;
switch ( v0 )
{
case 1u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) += v3;
break;
case 2u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) -= v3;
break;
case 3u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) ^= v3;
break;
case 4u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) *= v3;
break;
case 5u:
result = byte_6010C0[i + 1];
*(&byte_604B80 + result) ^= *(&byte_604B80 + byte_6010C0[i + 2]);
break;
default:
continue;
}

byte_6010C0是个15000byte的数组,3个一组循环,没啥可讲的,逻辑很清晰,逆过来写脚本就行了.

poc:

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
from __future__ import print_function
s=[]
for i in range(24):
s.append(Byte(0x601060+4*i))
for i in range(14997,-1,-3):
v0=Byte(0x6010C0+i)
v3=Byte(0x6010C0+i+2)
result=v0
if v0==1:
result = Byte(0x6010C0 + i + 1)
s[result]-=v3
elif v0==2:
result = Byte(0x6010C0 + i + 1)
s[result]+=v3
elif v0==3:
result = Byte(0x6010C0 + i + 1)
s[result] ^= v3
elif v0==4:
result = Byte(0x6010C0 + i + 1)
s[result] /= v3
else:
result = Byte(0x6010C0 + i + 1)
s[result] ^= s[Byte(0x6010C0+i+2)]
for i in s:
print(chr(i%256),end='')

这里要模256,因为chr()范围为256.

maze

把迷宫打印下:

1
2
3
4
for i in range(8):
for j in range(8):
print chr(Byte(0x601060+j+i*8)),
print

1
2
3
4
5
6
7
8
    * * * * * *
* * *
* * * * * *
* * * * *
* * # *
* * * * * *
* * *
* * * * * * * *

分析:

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 main(__int64 a1, char **a2, char **a3)
{
const char *v3; // rsi
signed __int64 v4; // rbx
signed int v5; // eax
char v6; // bp
char v7; // al
const char *v8; // rdi
__int64 v10; // [rsp+0h] [rbp-28h]

v10 = 0LL;
puts("Input flag:");
scanf("%s", &s1, 0LL);
if ( strlen(&s1) != 24 || (v3 = "nctf{", strncmp(&s1, "nctf{", 5uLL)) || *(&byte_6010BF + 24) != 125 )// flag长度24,开头为nctf{,最后一个字符为}
{
LABEL_22:
puts("Wrong flag!");
exit(-1);
}
v4 = 5LL;
if ( strlen(&s1) - 1 > 5 )
{
while ( 1 )
{
v5 = *(&s1 + v4);
v6 = 0;
if ( v5 > 78 )
{
v5 = (unsigned __int8)v5;
if ( (unsigned __int8)v5 == 'O' )
{
v7 = sub_400650((_DWORD *)&v10 + 1); // 往左走
goto LABEL_14;
}
if ( v5 == 'o' )
{
v7 = sub_400660((int *)&v10 + 1); // 往右走
goto LABEL_14;
}
}
else
{
v5 = (unsigned __int8)v5;
if ( (unsigned __int8)v5 == '.' )
{
v7 = sub_400670(&v10); // 往上走
goto LABEL_14;
}
if ( v5 == '0' )
{
v7 = sub_400680(&v10, v3); // 往下走
LABEL_14:
v6 = v7;
goto LABEL_15;
}
}
LABEL_15:
v3 = (const char *)HIDWORD(v10);

if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v10), v10) )// SHIDWORD表示取v10的下一个字节,百度ida宏定义;只能走空格和#
goto LABEL_22;
if ( ++v4 >= strlen(&s1) - 1 )
{
if ( v6 )
break;
LABEL_20:
v8 = "Wrong flag!";
goto LABEL_21;
}
}
}
if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 )// 从这能看出v10表示行,v10下个字节表示列,看看最后是不是等于35,也就是到没到#
goto LABEL_20;
v8 = "Congratulations!";
LABEL_21:
puts(v8);
return 0LL;
}

那就走呗,用wasd表示:
dsddssasssddddwwaa
再换成题目要求:
o0oo00O000oooo..OO
flag:
nctf{o0oo00O000oooo..OO}

WxyVM2

有毒吧,我的F5说代码量太大,人家的F5就不说.

bugku

Easy_vb

od

Easy_Re

1
2
3
4
flag=''
flag+='3074656D30633165577B465443545544'.decode('hex')[::-1]
flag+='7D465443545544'.decode('hex')[::-1]
print flag

游戏过关

ida交叉引用找到关键函数sub_45E940
简单的异或

逆向入门

https://www.base64decode.org/
这个逆向有个卵关系

原文作者: n3k0

发表日期: December 11th 2017, 10:23:08

发出嘶吼: 没有魔夜2玩我要死了

CATALOG
  1. 1. 南邮
    1. 1.1. Hello,RE!
      1. 1.1.1. ida
      2. 1.1.2. od
    2. 1.2. ReadAsm2
    3. 1.3. py交易
    4. 1.4. WxyVM
    5. 1.5. maze
    6. 1.6. WxyVM2
  2. 2. bugku
    1. 2.1. Easy_vb
    2. 2.2. Easy_Re
    3. 2.3. 游戏过关
    4. 2.4. 逆向入门