BUUCTF Reverse WriteUp

easyre

直接拖进IDA分析,拖进PEID发现不是有效的PE文件

找到main函数,送分题

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
int b; // [rsp+28h] [rbp-8h]
int a; // [rsp+2Ch] [rbp-4h]

_main();
scanf("%d%d", &a, &b);
if ( a == b )
printf("flag{this_Is_a_EaSyRe}");
else
printf("sorry,you can't get flag");
return 0;
}

flag{this_Is_a_EaSyRe}

reverse1

直接拖进IDA分析,是个64位exe可执行文件

shift+F12大法找到wrong flag,然后查看引用找到main函数

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
__int64 sub_1400118C0()
{
char *v0; // rdi
signed __int64 i; // rcx
size_t v2; // rax
size_t v3; // rax
char v5; // [rsp+0h] [rbp-20h]
int j; // [rsp+24h] [rbp+4h]
char Str1; // [rsp+48h] [rbp+28h]
unsigned __int64 v8; // [rsp+128h] [rbp+108h]

v0 = &v5;
for ( i = 82i64; i; --i )
{
*(_DWORD *)v0 = -858993460;
v0 += 4;
}
for ( j = 0; ; ++j )
{
v8 = j;
v2 = j_strlen(Str2);
if ( v8 > v2 )
break;
if ( Str2[j] == 111 )
Str2[j] = 48;
}
sub_1400111D1("input the flag:");
sub_14001128F("%20s", &Str1);
v3 = j_strlen(Str2);
if ( !strncmp(&Str1, Str2, v3) )
sub_1400111D1("this is the right flag!\n");
else
sub_1400111D1("wrong flag\n");
sub_14001113B(&v5, &unk_140019D00);
return 0i64;
}

这个Str2就是我们要找的flag了,虽然可以下断点直接跳出来,但是觉得太麻烦,直接逆

上面的for就是把Str2里面的o换成0,跟一下发现是{hello_world}

提交flagflag{hell0_w0rld}

reverse2

惯例拖进IDA,发现这次是ELF的x86_64,直接找到main函数

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)
{
int result; // eax
int stat_loc; // [rsp+4h] [rbp-3Ch]
int i; // [rsp+8h] [rbp-38h]
__pid_t pid; // [rsp+Ch] [rbp-34h]
char s2; // [rsp+10h] [rbp-30h]
unsigned __int64 v8; // [rsp+28h] [rbp-18h]

v8 = __readfsqword(0x28u);
pid = fork();
if ( pid )
{
argv = (const char **)&stat_loc;
waitpid(pid, &stat_loc, 0);
}
else
{
for ( i = 0; i <= strlen(&flag); ++i )
{
if ( *(&flag + i) == 105 || *(&flag + i) == 114 )
*(&flag + i) = 49;
}
}
printf("input the flag:", argv);
__isoc99_scanf("%20s", &s2);
if ( !strcmp(&flag, &s2) )
result = puts("this is the right flag!");
else
result = puts("wrong flag!");
return result;
}

发现有个fork操作,多进程,不管这么多先直接看上面主线程的for

1
2
3
4
5
for ( i = 0; i <= strlen(flag); ++i )
{
if ( flag[i] == 'i' || flag[i] == 'r' )
flag[i] = '1';
}

把flag里面的i/r换成1

再跟一下flag{hacking_for_fun} 得到变换后的flagflag{hack1ng_fo1_fun}

提交得分,居然没有在多进程里面设坑……

新年快乐

拖进PEID查看发现加了个UPX的壳,随便找个脱壳机给脱了,发现我的脱壳机还脱不下来,upx官方脱壳机,直接在官方下载一个最新的UPX,脱下来之后, 用PEID再看发现是yoda's Protector什么的,直接拖进IDA看看,找到main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
char v4; // [esp+12h] [ebp-3Ah]
__int16 v5; // [esp+20h] [ebp-2Ch]
__int16 v6; // [esp+22h] [ebp-2Ah]

__main();
strcpy(&v4, "HappyNewYear!");
v5 = 0;
memset(&v6, 0, 0x1Eu);
printf("please input the true flag:");
scanf("%s", &v5);
if ( !strncmp((const char *)&v5, &v4, strlen(&v4)) )
result = puts("this is true flag!");
else
result = puts("wrong!");
return result;
}

直接拿到flag HappyNewYear!

内涵的软件

好家伙一来就是一个EXE,拖进IDA发现是个X86的exe,旁边函数列表一大堆,还是老规矩找字符串

手动运行一下程序,挺有趣的

1
2
3
4
5
6
7
8
9
10
11
12
13
PS .\70125468-0786-4705-bd91-87037f8f3e16.exe
距离出现答案还有5秒,请耐心等待!
距离出现答案还有4秒,请耐心等待!
距离出现答案还有3秒,请耐心等待!
距离出现答案还有2秒,请耐心等待!
距离出现答案还有1秒,请耐心等待!
距离出现答案还有0秒,请耐心等待!



这里本来应该是答案的,但是粗心的程序员忘记把变量写进来了,你要不逆向试试看:(Y/N)
Y
OD吾爱破解或者IDA这些逆向软件都挺好的!

于是在main里面找到字符串DBAPP{49d3c93df25caad81232130f3d2ebfad},交上去不对

然后看了下提示,把DBAPP ,换成flag,出了,艹

guessgame

拖进IDA,先跑一下,然后发现main函数逻辑非常明确

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)
{
unsigned int v3; // eax
int v4; // [rsp+2Ch] [rbp-4h]

_main();
v4 = 0;
v3 = time(0i64);
srand(v3);
while ( 1 )
{
menu();
printf(QingXuanZe);
scanf("%d", &v4);
if ( v4 == 1 )
{
game();
}
else if ( v4 == 2 )
{
puts("想啥呢弟弟");
}
else if ( v4 )
{
puts(XuanZeCuoWu);
}
else
{
puts("退出游戏");
}
}
}

优化一下变量名,还是比较清晰的,进入game里面看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int game()
{
int input; // [rsp+28h] [rbp-8h]
int randNum; // [rsp+2Ch] [rbp-4h]

input = 0;
randNum = rand() % 100 + 1;
while ( 1 )
{
while ( 1 )
{
printf(QingShuRuShuZi);
scanf("%d", &input);
if ( input <= randNum )
break;
puts(DaLe);
}
if ( input >= randNum )
break;
puts(XiaoLe);
}
return puts(aFlaga);
}

本质上就是一个猜数游戏,猜对了也没奖励,于是打开字符串窗口,第一个就是flag,BJD{S1mple_ReV3r5e_W1th_0D_0r_IDA},提交成功

逆了一下,原来就是把这个字符串,藏在menu的一个变量里面,估计是用的内联汇编之类的,伪代码是看不出来的,但是看汇编一下子就出来了

helloworld

下载下来发现是一个安卓apk文件,啊这,俺从来没逆向过安卓文件啊

网上找了一堆教程,发现,首先用dex2jar把apk里面的dex给反编译出jar来,然后用jar-decompiler一瞬出flag

MainActivity.class源码在这

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.example.helloword;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends ActionBarActivity {
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130903064);
"flag{7631a988259a00816deda84afb29430a}".compareTo("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}

public boolean onCreateOptionsMenu(Menu paramMenu) {
getMenuInflater().inflate(2131492864, paramMenu);
return true;
}

public boolean onOptionsItemSelected(MenuItem paramMenuItem) {
return (paramMenuItem.getItemId() == 2131034172) ? true : super.onOptionsItemSelected(paramMenuItem);
}
}

一瞬拿到flag

xor

下载下来二话不说直接拖进IDA,发现还是个mac平台的软件,不过也能分析出main函数的源码

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // rsi
int result; // eax
signed int i; // [rsp+2Ch] [rbp-124h]
char flag[264]; // [rsp+40h] [rbp-110h]
__int64 v7; // [rsp+148h] [rbp-8h]

memset(flag, 0, 0x100uLL);
v3 = (char *)256;
printf("Input your flag:\n", 0LL);
get_line(flag, 256LL);
if ( strlen(flag) != 33 )
goto failed;
for ( i = 1; i < 33; ++i )
flag[i] ^= flag[i - 1];
v3 = global;
if ( !strncmp(flag, global, 0x21uLL) )
printf("Success", v3);
else
failed:
printf("Failed", v3);
result = __stack_chk_guard;
if ( __stack_chk_guard == v7 )
result = 0;
return result;
}

输入的flag每一位都和前一位进行xor,然后和内置的数据进行比较

然后就是写脚本出结果了

1
2
3
4
5
6
7
8
9
10
data = [
0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11,
0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F,
0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F,
0x47, 0x32, 0x4F, 0x00
]
for i in range(33, 0, -1):
data[i] ^= data[i-1]

print(''.join([chr(x) for x in data]))

得到结果flag{QianQiuWanDai_YiTongJiangHu}

reverse3

拖进IDA,直接找到main函数

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
__int64 main_0()
{
int flagLength; // eax
const char *v1; // eax
size_t v2; // eax
int v3; // edx
__int64 v4; // ST08_8
signed int j; // [esp+DCh] [ebp-ACh]
signed int i; // [esp+E8h] [ebp-A0h]
signed int v8; // [esp+E8h] [ebp-A0h]
char Dest[108]; // [esp+F4h] [ebp-94h]
char flag; // [esp+160h] [ebp-28h]
char v11; // [esp+17Ch] [ebp-Ch]

for ( i = 0; i < 100; ++i )
{
if ( (unsigned int)i >= 0x64 )
j____report_rangecheckfailure();
Dest[i] = 0;
}
print("please enter the flag:");
scanf("%20s", &flag);
flagLength = j_strlen(&flag);
v1 = (const char *)sub_4110BE((int)&flag, flagLength, (int)&v11);
strncpy(Dest, v1, 40u);
v8 = j_strlen(Dest);
for ( j = 0; j < v8; ++j )
Dest[j] += j;
v2 = j_strlen(Dest);
if ( !strncmp(Dest, Str2, v2) )
print("rigth flag!\n");
else
print("wrong flag!\n");
HIDWORD(v4) = v3;
LODWORD(v4) = 0;
return v4;
}

发现这次没有这么简单了,看了一下逻辑,大概是把输入的flag经过一个变换,然后再来一个每一位都加上当前的index这样的操作,最后和目的作比较

看一下这里面的变换函数

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
void *__cdecl sub_411AB0(char *flag, unsigned int flagLength, int *a3)
{
int v4; // STE0_4
int v5; // STE0_4
int v6; // STE0_4
int v7; // [esp+D4h] [ebp-38h]
signed int i; // [esp+E0h] [ebp-2Ch]
unsigned int v9; // [esp+ECh] [ebp-20h]
int v10; // [esp+ECh] [ebp-20h]
signed int v11; // [esp+ECh] [ebp-20h]
void *Dst; // [esp+F8h] [ebp-14h]
char *v13; // [esp+104h] [ebp-8h]

if ( !flag || !flagLength )
return 0;
v9 = flagLength / 3;
if ( (flagLength / 3) % 3 )
++v9;
v10 = 4 * v9;
*a3 = v10;
Dst = malloc(v10 + 1);
if ( !Dst )
return 0;
j_memset(Dst, 0, v10 + 1);
v13 = flag;
v11 = flagLength;
v7 = 0;
while ( v11 > 0 )
{
byte_41A144[2] = 0;
byte_41A144[1] = 0;
byte_41A144[0] = 0;
for ( i = 0; i < 3 && v11 >= 1; ++i )
{
byte_41A144[i] = *v13;
--v11;
++v13;
}
if ( !i )
break;
switch ( i )
{
case 1:
*(Dst + v7) = aAbcdefghijklmn[byte_41A144[0] >> 2];
v4 = v7 + 1;
*(Dst + v4++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
*(Dst + v4++) = aAbcdefghijklmn[64];
*(Dst + v4) = aAbcdefghijklmn[64];
v7 = v4 + 1;
break;
case 2:
*(Dst + v7) = aAbcdefghijklmn[byte_41A144[0] >> 2];
v5 = v7 + 1;
*(Dst + v5++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
*(Dst + v5++) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | 4 * (byte_41A144[1] & 0xF)];
*(Dst + v5) = aAbcdefghijklmn[64];
v7 = v5 + 1;
break;
case 3:
*(Dst + v7) = aAbcdefghijklmn[byte_41A144[0] >> 2];
v6 = v7 + 1;
*(Dst + v6++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
*(Dst + v6++) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | 4 * (byte_41A144[1] & 0xF)];
*(Dst + v6) = aAbcdefghijklmn[byte_41A144[2] & 0x3F];
v7 = v6 + 1;
break;
}
}
*(Dst + v7) = 0;
return Dst;
}

也有字母表,看上去真的很像base64,而且密码表也是base64的,就先猜是,然后写脚本

1
2
3
4
5
6
import base64
s1 = 'e3nifIH9b_C@n@dH'
s1 = list(s1)
for i in range(len(s1)):
s1[i] = chr(ord(s1[i]) - i)
print(base64.b64decode(''.join(s1)))

然后就是得到flag{i_l0ve_you},果然是这个base64

不一样的flag

是个x86下windows程序,看样子像是一个迷宫题,main函数也很直接

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // [esp+17h] [ebp-35h]
int x; // [esp+30h] [ebp-1Ch]
int y; // [esp+34h] [ebp-18h]
signed int input; // [esp+38h] [ebp-14h]
int i; // [esp+3Ch] [ebp-10h]
int map; // [esp+40h] [ebp-Ch]

__main();
x = 0;
y = 0;
qmemcpy(&v3, _data_start__, 0x19u);
while ( 1 )
{
puts("you can choose one action to execute");
puts("1 up");
puts("2 down");
puts("3 left");
printf("4 right\n:");
scanf("%d", &input);
if ( input == 2 )
{
++x;
}
else if ( input > 2 )
{
if ( input == 3 )
{
--y;
}
else
{
if ( input != 4 )
LABEL_13:
exit(1);
++y;
}
}
else
{
if ( input != 1 )
goto LABEL_13;
--x;
}
for ( i = 0; i <= 1; ++i )
{
if ( *(&x + i) < 0 || *(&x + i) > 4 )
exit(1);
}
if ( *(&map + 5 * x + y - 41) == '1' )
exit(1);
if ( *(&map + 5 * x + y - 41) == '#' )
{
puts("\nok, the order you enter is the flag!");
exit(0);
}
}
}

然后看汇编发现push了一串字符,应该就是地图了

*11110100001010000101111#然后是五个一行

1
2
3
4
5
*1111
01000
01010
00010
1111#`

没病走两步,得到flag222441144222

SimpleRev

x64的一个elf文件,拖进IDA,WSL下面直接跑不了,linux下也跑不了直接提示段错误,淦,main函数就是一个菜单,重点在decry()函数里面,跟进去

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
unsigned __int64 Decry()
{
char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h]
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9; // [rsp+40h] [rbp-20h]
__int64 v10; // [rsp+48h] [rbp-18h]
int v11; // [rsp+50h] [rbp-10h]
unsigned __int64 v12; // [rsp+58h] [rbp-8h]

v12 = __readfsqword(0x28u);
*(_QWORD *)src = 'SLCDN';
v7 = 0LL;
v8 = 0;
v9 = 'wodah';
v10 = 0LL;
v11 = 0;
text = (char *)join(key3, &v9);
strcpy(key, key1);
strcat(key, src);
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > '@' && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + ' '; // 大写变小写
++v3;
}
printf("Please input your flag:", src);
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 122 )
{
if ( v1 > 64 && v1 <= 90 )
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
else
{
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) )
puts("Congratulation!\n");
else
puts("Try again!\n");
return __readfsqword(0x28u) ^ v12;
}

进去之后有几个阴间计算,跟一下

text = killshadow key = adsfkndcls

大概就是输入的字符和这个key进行运算之后要等于text,注意这个式子

str2[idx] = (inputChar - '\'' - key[cnt++ % kenLength] + 'a') % 26 + 'a'

这个取模让人有点难受,但是想到了取模的一个运算法则
$$
(a + b) % c=(a % c + b % c)%c
$$
又因为key里面全部都是小写字符,不可能是超过26的,而inputChar - ‘\‘的值大概在25-100之间

写个脚本穷举一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
text = list('killshadow')
text = [ord(x) for x in text]
key = list('adsfkndcls')
key = [ord(x) for x in key]
flag = [0] * len(text)
for i in range(len(text)):
flag[i] = text[i] - ord('a')
flag[i] += 26 * 2
flag[i] += key[i] - ord('a')
flag[i] += ord('\'')

#print(flag)
print(''.join([chr(x) for x in flag]))

KL^Q]UDFZiefxkwo^t 什么阴间flag,交上去完全不对

得,看一下网上的wp,直接爆破,因为这个是按位加密的,爆破脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def change(x:int, index:int):
char = (x - ord('\'') - (key[index] - ord('a'))) % 26 + ord('a')
return char

text = list('killshadow')
text = [ord(x) for x in text]
key = list('adsfkndcls')
key = [ord(x) for x in key]
flag = [0] * len(text)
#charset = list(range(ord('a'), ord('z')+1))
charset = list(range(ord('A'), ord('Z')+1))
for cnt in range(len(text)):
for i in charset:
res = change(i, cnt)
if res == text[cnt]:
flag[cnt] = i
print(''.join([chr(x) for x in flag]))

出了多解,一个是大写的KLDQCUDFZO,一个是小写的efxkwoxzti,按理来说中间随便混杂都满足条件,垃圾题实锤了

刮开有奖

IDA分析是个win32下的窗口化程序,然而俺只能看见一个刮开有奖,其余啥也看不见,不管,进入diagFunc 查看对话框逻辑

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
BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int key[11]; // [esp+8h] [ebp-20030h]
char input[8]; // [esp+34h] [ebp-20004h]
char chunk[3]; // [esp+10034h] [ebp-10004h]

if ( a2 == 272 )
return 1;
if ( a2 != 273 )
return 0;
if ( a3 == 1001 )
{
memset(input, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, input, 0xFFFF);
if ( strlen(input) == 8 )
{
key[0] = 'Z';
key[1] = 'J';
key[2] = 'S';
key[3] = 'E';
key[4] = 'C';
key[5] = 'a';
key[6] = 'N';
key[7] = 'H';
key[8] = '3';
key[9] = 'n';
key[10] = 'g';
sub_4010F0(key, 0, 10);
memset(chunk, 0, 0xFFFFu);
chunk[0] = input[5];
chunk[2] = input[7];
chunk[1] = input[6];
v4 = sub_401000(chunk, strlen(chunk));
memset(chunk, 0, 0xFFFFu);
chunk[1] = input[3];
chunk[0] = input[2];
chunk[2] = input[4];
v5 = sub_401000(chunk, strlen(chunk));
if ( input[0] == key[0] + 34
&& input[1] == key[4]
&& 4 * input[2] - 141 == 3 * key[2]
&& input[3] / 4 == 2 * (key[7] / 9)
&& !strcmp(v4, "ak1w")
&& !strcmp(v5, "V1Ax") )
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
}
return 0;
}
if ( a3 != 1 && a3 != 2 )
return 0;
EndDialog(hDlg, a3);
return 1;
}

可以看到就是这两个函数,第一个sub_4010F0是对key进行加密,进去跟了一下逻辑好像还有个递归,不管直接动态调试,在OD里面改几个跳转指令,跟一下发现key由ZJSECaNH3ng变为了3CEHJNSZagn 就是个简单的位置变换,

然后就是对sub_401000进行分析了,发现了一个密码表,看样子就是base64了,然后根据后面的判断条件推测一下就能知道input[0]input[1]了,后面还有个input[3]input[4]进行验证一下就知道猜想对不对,毕竟我也在字符串列表中找到了变表的base64

接出来是UJWP1jMp,出了

Java 逆向解密

压缩包打开是个class文件,使用Java Decompiler一把梭,发现加密逻辑很简单

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
import java.util.ArrayList;
import java.util.Scanner;

public class Reverse {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("Please input the flag );
String str = s.next();
System.out.println("Your input is );
System.out.println(str);
char[] stringArr = str.toCharArray();
Encrypt(stringArr);
}

public static void Encrypt(char[] arr) {
ArrayList<Integer> Resultlist = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
int result = arr[i] + 64 ^ 0x20;
Resultlist.add(Integer.valueOf(result));
}
int[] KEY = {
180, 136, 137, 147, 191, 137, 147, 191, 148, 136,
133, 191, 134, 140, 129, 135, 191, 65 };
ArrayList<Integer> KEYList = new ArrayList<>();
for (int j = 0; j < KEY.length; j++)
KEYList.add(Integer.valueOf(KEY[j]));
System.out.println("Result:");
if (Resultlist.equals(KEYList)) {
System.out.println("Congratulations);
} else {
System.err.println("Error);
}
}
}

直接白给,写脚本出结果

1
2
3
4
5
6
table = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 
133, 191, 134, 140, 129, 135, 191, 65]
for i in range(len(table)):
table[i] ^= 0x20
table[i] -= 64
print(''.join([chr(x) for x in table]))

flag{This_is_the_flag_!}

findit

是个apk,使用dex2jar变成jar之后再使用java decompiler查看逻辑

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
package com.example.findit;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {
protected void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
setContentView(2130903064);
Button button = (Button)findViewById(2131034173);
final EditText edit = (EditText)findViewById(2131034174);
final TextView text = (TextView)findViewById(2131034175);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View param1View) {
char[] arrayOfChar1 = new char[17];
char[] arrayOfChar2 = new char[38];
int i = 0;
while (true) {
String str;
if (i >= 17) {
if (String.valueOf(arrayOfChar1).equals(edit.getText().toString())) {
for (i = 0;; i++) {
if (i >= 38) {
str = String.valueOf(arrayOfChar2);
text.setText(str);
return;
}
if ((b[i] >= 'A' && b[i] <= 'Z') || (b[i] >= 'a' && b[i] <= 'z')) {
arrayOfChar2[i] = (char)(b[i] + 16);
if ((arrayOfChar2[i] > 'Z' && arrayOfChar2[i] < 'a') || arrayOfChar2[i] >= 'z')
arrayOfChar2[i] = (char)(arrayOfChar2[i] - 26);
} else {
arrayOfChar2[i] = b[i];
}
}
break;
}
} else {
if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
str[i] = (char)(a[i] + 18);
} else if ((a[i] >= 'A' && a[i] <= 'Z') || (a[i] >= 'a' && a[i] <= 'z')) {
str[i] = (char)(a[i] - 8);
} else {
str[i] = a[i];
}
i++;
continue;
}
text.setText(");
return;
}
}
});
}

public boolean onOptionsItemSelected(MenuItem paramMenuItem) {
return (paramMenuItem.getItemId() == 2131034176) ? true : super.onOptionsItemSelected(paramMenuItem);
}
}

用a/b两个字符串里面的字符变换之后赋值给两个数组,但是,我并不知道这两个数组的内容啊

那就用apktools给解包看看有啥内容,发现MainActivity.smail有俩文件,其中有一个就是数组,复制粘贴下来

1
2
3
4
table1 = [0x54, 0x68, 0x69, 0x73, 0x49, 0x73, 0x54, 0x68,
0x65, 0x46, 0x6c, 0x61, 0x67, 0x48, 0x6f, 0x6d, 0x65, ]
table2 = [0x70, 0x76, 0x6b, 0x71, 0x7b, 0x6d, 0x31, 0x36, 0x34, 0x36, 0x37, 0x35, 0x32, 0x36, 0x32, 0x30, 0x33, 0x33, 0x6c,
0x34, 0x6d, 0x34, 0x39, 0x6c, 0x6e, 0x70, 0x37, 0x70, 0x39, 0x6d, 0x6e, 0x6b, 0x32, 0x38, 0x6b, 0x37, 0x35, 0x7d, ]

然后就是去看看两个数组到底是啥内容,直接复制粘贴进idea跑

发现str是LzakAkLzwXdsyZgew直接交,不对,看来还得看看table2的,结果是flag{c164675262033b4c49bdf7f9cda28a75},出了

8086

IDA加载发现是个DOS文件,没有函数,看看汇编

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
seg001:0000 sub_10030       proc near               ; CODE XREF: sub_10030↓j
seg001:0000 ; start+5↓p
seg001:0000 jmp short sub_10030
seg001:0000 sub_10030 endp
seg001:0000
seg001:0002 ; ---------------------------------------------------------------------------
seg001:0002 mov cx, 22h ; '"'
seg001:0005 lea bx, aUDuTZWjQGjzZWz ; "]U[du~|t@{z@wj.}.~q@gjz{z@wzqW~/b;"
seg001:0009
seg001:0009 loc_10039: ; CODE XREF: seg001:000F↓j
seg001:0009 mov di, cx
seg001:000B dec di
seg001:000C xor byte ptr [bx+di], 1Fh
seg001:000F loop loc_10039
seg001:0011 lea dx, aUDuTZWjQGjzZWz ; "]U[du~|t@{z@wj.}.~q@gjz{z@wzqW~/b;"
seg001:0015 mov ah, 9
seg001:0017 int 21h ; DOS - PRINT STRING
seg001:0017 ; DS:DX -> string terminated by "$"
seg001:0019 retn
seg001:001A assume ss:dseg, ds:nothing
seg001:001A
seg001:001A ; =============== S U B R O U T I N E =======================================
seg001:001A
seg001:001A ; Attributes: noreturn
seg001:001A
seg001:001A public start
seg001:001A start proc near
seg001:001A mov ax, seg dseg
seg001:001D mov ds, ax
seg001:001F assume ds:dseg
seg001:001F call sub_10030
seg001:001F start endp
seg001:001F
seg001:0022 ; ---------------------------------------------------------------------------
seg001:0022 mov ah, 4Ch
seg001:0024 int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
seg001:0024 seg001 ends ; AL = exit code
seg001:0024
seg001:0024
seg001:0024 end start

非常简单,对列表里面没一个字符取xor 0x1F就行

写个脚本

1
2
3
4
5
6
7
a = ']U[du~|t@{z@wj.}.~q@gjz{z@wzqW~/b;'
a = list(a)
a = [ord(x) for x in a]
flag = []
for i in a:
flag.append(i ^ 0x1f)
print(''.join([chr(x) for x in flag]))

出结果BJD{jack_de_hu1b1an_xuede_henHa0}

check_1n

打开一看还真是字符画版的笔记本电脑,有点东西,照样拖进IDA,发现里面一堆函数,main函数的逻辑并不明显,通过找字符找到打印笔记本电脑的函数,然后查看引用

里面的字符串太多了,静态分析感觉都不太好用了,只好上od

md上OD也跟不出个什么鬼,发现一个奇怪的字符串2i9Q8AtFJTfL3ahU2XGuemEqZJ2ensozjg1EjPJwCHy4RY1Nyvn1ZE1bZe

然后大概是有个解密函数,跟进去,发现加密函数异常难看,但是这个应该是解密出来的函数,要是能够跟一下就好了,使用OD在main函数直接跳转,发现在malloc这个函数被卡出来,而且中途还有一堆反调的东西,淦

最后还是通过Shift + F12的方法找到了开机密码HelloWorld

然后有个flag文件,打开之后是虚假的flag, base64解密得Why don't you try the magic brick game

然后试试打砖块

然后输了之后flag自己就出来了,人才.jpg

flag{f5dfd0f5-0343-4642-8f28-9adbb74c4ede}