XCTF_Re

XCTF_Re_Source

XCTF 3rd-GCTF-2017

debug

PEID显示为.NET程序

丢进dnspy中查看,得到4个主要函数,分别为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ᜅ
// Token: 0x06000007 RID: 7
private static void encodeᜀ(string A_0, int A_1, ref string A_2)
{
int num = 0;
if (0 < A_0.Length)
{
do
{
char c = A_0[num];
int num2 = 1;
do
{
c = Convert.ToChar(ᜅ.xor(Convert.ToInt32(c), num2));
num2++;
}
while (num2 < 15);
A_2 += c;
num++;
}
while (num < A_0.Length);
}
A_2 = ᜅ.flag_md5ᜀ(A_2);
}
1
2
3
4
5
6
7
// ᜅ
// Token: 0x06000006 RID: 6
private static string flag_md5ᜀ(string A_0)
{
byte[] bytes = Encoding.ASCII.GetBytes(A_0);
return "flag{" + BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", "") + "}";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ᜅ
// Token: 0x06000008 RID: 8
private static void maybe_mainᜀ(string[] A_0)
{
string b = null;
string value = string.Format("{0}", DateTime.Now.Hour + 1);
string a_ = "CreateByTenshine";
ᜅ.encodeᜀ(a_, Convert.ToInt32(value), ref b);
string a = Console.ReadLine();
if (a == b)
{
Console.WriteLine("u got it!");
Console.ReadKey(true);
}
else
{
Console.Write("wrong");
}
Console.ReadKey(true);
}
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
// ᜅ
// Token: 0x06000005 RID: 5
private static int xor(int A_0, int A_1)
{
return (new int[]
{
2,
3,
5,
7,
11,
13,
17,
19,
23,
29,
31,
37,
41,
43,
47,
53,
59,
61,
67,
71,
73,
79,
83,
89,
97,
101,
103,
107,
109,
113
})[A_1] ^ A_0;
}

ᜅ.encodeᜀ(a_, Convert.ToInt32(value), ref b);处下断点进行调试,可以直接得到flag

1
2
3
4
5
6
7
8
9
10
11
import hashlib
a=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113]
b="CreateByTenshine"
flag=""
for i in b:
ch=i
for j in range(1,15):
ch=chr(ord(ch)^a[j])
flag+=ch
flag=hashlib.md5(flag.encode("utf-8"))
print("flag{"+flag.hexdigest().upper()+"}")#提交的flag要大写....

hackme

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
__int64 __fastcall maybe_main(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
__int64 v3; // rcx
__int64 v4; // r8
__int64 v5; // r9
int rand; // eax
char password[136]; // [rsp+10h] [rbp-B0h]
int v9; // [rsp+98h] [rbp-28h]
char v10; // [rsp+9Fh] [rbp-21h]
int v11; // [rsp+A0h] [rbp-20h]
unsigned __int8 v12; // [rsp+A6h] [rbp-1Ah]
char v13; // [rsp+A7h] [rbp-19h]
int v14; // [rsp+A8h] [rbp-18h]
int v15; // [rsp+ACh] [rbp-14h]
unsigned int v16; // [rsp+B0h] [rbp-10h]
int v17; // [rsp+B4h] [rbp-Ch]
_BOOL4 v18; // [rsp+B8h] [rbp-8h]
int i; // [rsp+BCh] [rbp-4h]

maybe_print("Give me the password: ", a2);
sub_4075A0("%s", password);
for ( i = 0; password[i]; ++i )
;
v18 = i == 22; // 长度22
v17 = 10;
do
{
rand = sub_406D90("%s", password, v2, v3, v4, v5);
v3 = (rand % 22);
v14 = rand % 22;
v16 = 0;
v13 = byte_6B4270[rand % 22];
v12 = password[rand % 22];
v11 = rand % 22 + 1;
v15 = 0;
while ( v15 < v11 )
{
++v15;
v16 = 0x6D01788D * v16 + 0x3039;
}
v2 = v16;
v10 = v16 ^ v12;
if ( v13 != (v16 ^ v12) )
v18 = 0;
--v17;
}
while ( v17 );
if ( v18 )
v9 = maybe_print("Congras\n");
else
v9 = maybe_print("Oh no!\n");
return 0LL;
}

根据

1
2
3
for ( i = 0; password[i]; ++i )
;
v18 = i == 22; // 长度22

推测password的长度为22

do...while循环中,sub_406D90是一个较为复杂的函数,猜测是用来生成随机数

循环中的关键代码为

1
2
3
4
5
6
7
8
9
v13 = byte_6B4270[rand % 22];
v12 = password[rand % 22];
v11 = rand % 22 + 1;
v15 = 0;
while ( v15 < v11 )
{
++v15;
v16 = 0x6D01788D * v16 + 0x3039;
}

rand%22说明处理范围在[0,21]之间,而

1
2
v13 = byte_6B4270[rand % 22];
v12 = password[rand % 22];

说明两者存在一一对应的关系,由此可以得到exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
#include<cstdint>
using namespace std;
int main(){
int byte_6B4270[]={95, 242, 94, 139, 78, 14, 163, 170, 199, 147, 129, 61, 95, 116, 163, 9, 145, 43, 73, 40, 147, 103, 0, 0};
for(int i=0;i<22;++i){
int v13=byte_6B4270[i];
int v16=0;
int v15=0;
for(int j=0;j<=i;++j){
v16 = 0x6D01788D * v16 + 0x3039;
}
cout<<char(v16^v13);
}
return 0;
}

flag{d826e6926098ef46}

XCTF 3rd-NJCTF-2017

echo-server

错误如图所示,将整段代码Undefine,重新生成,将部分jmp修改成nop

main
1
2
3
4
5
6
7
8
9
int __cdecl main()
{
setbuf(stdin, 0);
setbuf(stdout, 0);
dword_804A088 = 1;
puts(" **************\n Echo Server 0.3 ALPHA\n **************");
sub_80487C4();
return 0;
}
sub_80487C4
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
unsigned int sub_80487C4()
{
size_t v0; // eax
unsigned __int8 *v1; // ST14_4
size_t v2; // eax
char input; // [esp+18h] [ebp-70h]
_BYTE v5[3]; // [esp+19h] [ebp-6Fh]
unsigned int v6; // [esp+7Ch] [ebp-Ch]

v6 = __readgsdword(0x14u);
memset(&input, 0, 0x14u);
read(0, &input, 0x14u);
if ( !strncmp(&input, unk_8048817, 5u) )
{
puts("You are very close! Now patch me~");
if ( dword_804A088 )
exit(1);
v0 = strlen(&input);
v1 = MD5(v5, v0, 0);
sub_804875D(v1, 16u);
}
else
{
v2 = strlen(&input);
sub_804875D(&input, v2 - 1);
}
fflush(stdout);
return __readgsdword(0x14u) ^ v6;
}
1
2
3
4
5
6
7
.text:08048817 ; char unk_8048817[]
.text:08048817 unk_8048817 db 46h ; F ; DATA XREF: sub_80487C4+61↓o
.text:08048818 db 31h ; 1
.text:08048819 db 40h ; @
.text:0804881A db 67h ; g
.text:0804881B db 41h ; A
.text:0804881C db 0

在Linux下无法直接运行

sudo dpkg --add-architecture i386启用i386架构

sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y进行刷新

sudo apt-get install libssl1.0.0:i386添加运行库

1
2
if ( dword_804A088 )
exit(1);

在main函数中,dword_804A088被设置成1,手动将其patch成0,或者将jz short loc_8048866patch成jnz short loc_8048866

运行,并输入F1@gA,得到flag

XCTF 4th-QCTF-2018

asong

main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
void *v3; // ST00_8
const char *flag; // ST08_8

v3 = malloc(188uLL);
flag = malloc(0x50uLL);
sub_400BBF(); //初始化
get_input(flag);
check_and_split(flag);
sub_400AAA("that_girl", v3); // 记录字符频数
sub_400E54(flag, v3);
return 0LL;
}
check_and_split
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void __fastcall check_and_split(__int64 a1)
{
int v1; // [rsp+14h] [rbp-Ch]
void *dest; // [rsp+18h] [rbp-8h]

dest = malloc(0x50uLL);
if ( memcmp(a1, "QCTF{", 5uLL) )
exit(-1);
memcpy(dest, (a1 + 5), 0x4BuLL);
v1 = strlen(dest);
if ( *(dest + v1 - 1) == '}' )
*(dest + v1 - 1) = 0;
memcpy(a1, dest, 0x50uLL);
free(dest);
}
sub_400AAA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __fastcall sub_400AAA(const char *a1, __int64 a2)
{
int v2; // eax
__int64 v4; // [rsp+0h] [rbp-20h]
char buf; // [rsp+13h] [rbp-Dh]
int fd; // [rsp+14h] [rbp-Ch]
unsigned __int64 v7; // [rsp+18h] [rbp-8h]

v7 = __readfsqword(0x28u);
fd = open(a1, 0, a2, a1);
while ( read(fd, &buf, 1uLL) == 1 )
{
v2 = get_character_new_index(buf);
++*(4LL * v2 + v4);
}
return close(fd);
}
get_character_frequency
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
__int64 __fastcall get_character_new_index(char a1)
{
__int64 result; // rax

result = (a1 - '\n');
switch ( a1 )
{
case '\n':
result = (a1 + 35);
break;
case ' ':
case '!':
case '"':
result = (a1 + 10);
break;
case '\'':
result = (a1 + 2);
break;
case ',':
result = (a1 - 4);
break;
case '.':
result = (a1 - 7);
break;
case ':':
case ';':
result = (a1 - 21);
break;
case '?':
result = (a1 - 27);
break;
case '_':
result = (a1 - 49);
break;
default:
if ( a1 <= '/' || a1 > '0' )
{
if ( a1 <= '@' || a1 > 'Z' )
{
if ( a1 > '`' && a1 <= 'z' )
result = (a1 - 87);
}
else
{
result = (a1 - 55);
}
}
else
{
result = (a1 - 48);
}
break;
}
return result;
}
sub_400E54
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 __fastcall sub_400E54(const char *flag, __int64 a2)
{
int i; // [rsp+18h] [rbp-48h]
int flag_len; // [rsp+1Ch] [rbp-44h]
char v5[56]; // [rsp+20h] [rbp-40h]
unsigned __int64 v6; // [rsp+58h] [rbp-8h]

v6 = __readfsqword(0x28u);
flag_len = strlen(flag);
for ( i = 0; i < flag_len; ++i )
v5[i] = *(4LL * get_character_new_index(flag[i]) + a2);
convert(v5);
encrypt(v5, flag_len);
sub_400CC0(v5, "out", flag_len);
return __readfsqword(0x28u) ^ v6;
}
convert
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall convert(unsigned __int8 *a1)
{
__int64 result; // rax
_BYTE v2[5]; // [rsp+13h] [rbp-5h]

v2[4] = 0;
*v2 = *a1;
while ( dword_6020A0[*&v2[1]] )
{
a1[*&v2[1]] = a1[dword_6020A0[*&v2[1]]];
*&v2[1] = dword_6020A0[*&v2[1]];
}
result = v2[0];
a1[*&v2[1]] = v2[0];
return result;
}
encrypt
1
2
3
4
5
6
7
8
9
10
11
12
13
_BYTE *__fastcall encrypt(_BYTE *a1, int a2)
{
_BYTE *result; // rax
char v3; // [rsp+17h] [rbp-5h]
int i; // [rsp+18h] [rbp-4h]

v3 = *a1 >> 5;
for ( i = 0; a2 - 1 > i; ++i )
a1[i] = 8 * a1[i] | (a1[i + 1] >> 5); // a_new[0]=a[0]<<3 | a[1]>>5
result = &a1[i];
*result = 8 * *result | v3;
return result;
}

encrypt函数得到的结果被保存在out文件中

可以根据encrypt函数进行反推

1
EC29E341E1F7AA1D29ED299939F3B7A9E7AC2BB7AB409FA931352C29EFA83D4BB0E9E1687B41

解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a=[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]
print(a)
tail5=[]
head3=[0]
for i in range(len(a)):
tail5.append(a[i]>>3)
for i in range(len(a)-1):
head3.append((a[i]<<5)&0xff)
head3[0]=(a[len(a)-1]<<5)&0xff
after_convert=[]
for i in range(len(tail5)):
after_convert.append(tail5[i]+head3[i])
print(after_convert)
#[61, 133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104]

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
using namespace std;
int main(){
unsigned char a1[]={61, 133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104};
unsigned char v3; // [rsp+17h] [rbp-5h]
int i; // [rsp+18h] [rbp-4h]
unsigned char *result=nullptr;
v3 = *a1 >> 5;
for ( i = 0; 38 - 1 > i; ++i )
a1[i] = 8 * a1[i] | (a1[i + 1] >> 5); // a_new[0]=a[0]<<3 | a[1]>>5
result = &a1[i];
*result = 8 * *result | v3;
for(i=0;i<38;++i){
cout<<int(a1[i])<<' ';
}
return 0;
}
/*[236, 41, 227, 65, 225, 247, 170, 29, 41, 237, 41, 153, 57, 243, 183, 169, 231, 172, 43, 183, 171, 64, 159, 169, 49, 53, 44, 41, 239, 168, 61, 75, 176, 233, 225, 104, 123, 65]*/

对convert函数进行逆向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dword_6020A0=[22, 0, 6, 2, 30, 24, 9, 1, 21, 7, 18, 10, 8, 12, 17, 23, 13, 4, 3, 14, 19, 11, 20, 16, 15, 5, 25, 36, 27, 28, 29, 37, 31, 32, 33, 26, 34, 35]
#print(sorted(dword_6020A0))
#[0, 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]
posi=0
after_convert=[61, 133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104]
convert=[0]
while dword_6020A0[posi]:
convert.append(dword_6020A0[posi])
posi=dword_6020A0[posi]
#print(convert)
#[0, 22, 20, 19, 14, 17, 4, 30, 29, 28, 27, 36, 34, 33, 32, 31, 37, 35, 26, 25, 5, 24, 15, 23, 16, 13, 12, 8, 21, 11, 10, 18, 3, 2, 6, 9, 7, 1]
posi=0
before_convert=[0 for i in range(38)]
for i in range(37):
before_convert[convert[i+1]]=after_convert[convert[i]]
before_convert[0]=after_convert[1]
print(before_convert)
#[133, 67, 104, 133, 245, 38, 60, 61, 39, 245, 51, 104, 62, 60, 118, 38, 245, 118, 165, 245, 19, 165, 61, 245, 62, 165, 45, 61, 245, 7, 60, 118, 29, 60, 15, 104, 133, 169]

根据反汇编对sub_400AAA进行模拟,从而得到字符频数数组

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
#include<iostream>
#include<cstring>
using namespace std;
int get_character_new_index(char a1)
{
int result; // rax

result = (a1 - '\n');
switch ( a1 )
{
case '\n':
result = (a1 + 35);
break;
case ' ':
case '!':
case '"':
result = (a1 + 10);
break;
case '\'':
result = (a1 + 2);
break;
case ',':
result = (a1 - 4);
break;
case '.':
result = (a1 - 7);
break;
case ':':
case ';':
result = (a1 - 21);
break;
case '?':
result = (a1 - 27);
break;
case '_':
result = (a1 - 49);
break;
default:
if ( a1 <= '/' || a1 > '0' )
{
if ( a1 <= '@' || a1 > 'Z' )
{
if ( a1 > '`' && a1 <= 'z' )
result = (a1 - 87);
}
else
{
result = (a1 - 55);
}
}
else
{
result = (a1 - 48);
}
break;
}
return result;
}
int main(){
int get_ch[80]={0};
const char * text="There's_a_girl_but_I_let_her_get_away\nIt's_all_my_fault_cause_pride_got_in_the_way\nAnd_I'd_be_lying_if_I_said_I_was_OK\nAbout_that_girl_the_one_I_let_get_away\nI_keep_saying_no\nThis_can't_be_the_way_we're_supposed_to_be\nI_keep_saying_no\nThere's_gotta_be_a_way_to_get_you_close_to_me\nNow_I_know_you_gotta\nSpeak_up_if_you_want_somebody\nCan't_let_him_get_away_oh_no\nYou_don't_wanna_end_up_sorry\nThe_way_that_I'm_feeling_everyday\nNo_no_no_no\nThere's_no_hope_for_the_broken_heart\nNo_no_no_no\nThere's_no_hope_for_the_broken\nThere's_a_girl_but_I_let_her_get_away\nIt's_my_fault_cause_I_said_I_needed_space\nI've_been_torturing_myself_night_and_day\nAbout_that_girl_the_one_I_let_get_away\nI_keep_saying_no\nThis can't be the way we're supposed to be\nI keep saying no\nThere's gotta be a way to get you\nThere's gotta be a way\nTo_get_you_close_to_me\nYou_gotta\nSpeak_up_if_you_want_somebody\nCan't_let_him_get_away_oh_no\nYou_don't_wanna_end_up_sorry\nThe_way_that_I'm_feeling_everyday\nNo_no_no_no\nThere's_no_hope_for_the_broken_heart\nNo no no no\nThere's no hope for the broken\nNo home for me\nNo home cause I'm broken\nNo room to breathe\nAnd I got no one to blame\nNo home for me\nNo_home_cause_I'm_broken\nAbout_that_girl\nThe_one_I_let_get_away\nSo_you_better\nSpeak_up_if_you_want_somebody\nYou_can't_let_him_bet_away_no_no\nYou_don't_wanna_end_up_sorry\nThe_way_that_I'm_feeling_everyday\nDon't_you_know\nNo_no_no_no\nThere's_no_hope_for_the_broken_hearty\nDon't you know\nNo no no no\nThere's no hope for the broken\nOh\nYou don't wanna lose at love\nIt's only gonna hurt too much\nI'm telling you\nYou_don't_wanna_lose_at_love\nIt's_only_gonna_hurt_too_much\nI'm_telling_you\nYou_don't_wanna_lose_at_love\nCause_there's_no_hope_for_the_broken_heart\nThat_girl\nThe_one_I_let_get_away\n";
for(int i=0;i<strlen(text);++i){//模拟反汇编代码进行字频读取
get_ch[get_character_new_index(text[i])]++;
}
for(int i=33;i<126;++i){//生成字符与下标的对应关系
cout<<get_character_new_index(i)<<":"<<"\""<<char(i)<<"\""<<',';
}
cout<<endl;
for(int i=0;i<80;++i){
cout<<get_ch[i]<<',';
}
return 0;
}

/*
43:"!",44:""",25:"#",26:"$",27:"%",28:"&",41:"'",30:"(",31:")",32:"*",33:"+",40:",",35:"-",39:".",37:"/",0:"0",39:"1",40:"2",41:"3",42:"4",43:"5",44:"6",45:"7",46:"8",47:"9",37:":",38:";",50:"<",51:"=",52:">",36:"?",54:"@",10:"A",11:"B",12:"C",13:"D",14:"E",15:"F",16:"G",17:"H",18:"I",19:"J",20:"K",21:"L",22:"M",23:"N",24:"O",25:"P",26:"Q",27:"R",28:"S",29:"T",30:"U",31:"V",32:"W",33:"X",34:"Y",35:"Z",81:"[",82:"\",83:"]",84:"^",46:"_",86:"`",10:"a",11:"b",12:"c",13:"d",14:"e",15:"f",16:"g",17:"h",18:"i",19:"j",20:"k",21:"l",22:"m",23:"n",24:"o",25:"p",26:"q",27:"r",28:"s",29:"t",30:"u",31:"v",32:"w",33:"x",34:"y",35:"z",113:"{",114:"|",115:"}",
0,0,0,0,0,0,0,0,0,0,104,30,15,29,169,19,38,67,60,0,20,39,28,118,165,26,0,61,51,133,45,7,34,0,62,0,0,0,0,0,0,40,71,0,0,66,245,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
*/

exp

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
a=[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]
#print(a)
tail5=[]
head3=[0]
for i in range(len(a)):
tail5.append(a[i]>>3)
for i in range(len(a)-1):
head3.append((a[i]<<5)&0xff)
head3[0]=(a[len(a)-1]<<5)&0xff
after_convert=[]
for i in range(len(tail5)):
after_convert.append(tail5[i]+head3[i])
#print(after_convert)
#[61, 133, 60, 104, 60, 62, 245, 67, 165, 61, 165, 51, 39, 62, 118, 245, 60, 245, 133, 118, 245, 104, 19, 245, 38, 38, 165, 133, 61, 245, 7, 169, 118, 29, 60, 45, 15, 104]
dword_6020A0=[22, 0, 6, 2, 30, 24, 9, 1, 21, 7, 18, 10, 8, 12, 17, 23, 13, 4, 3, 14, 19, 11, 20, 16, 15, 5, 25, 36, 27, 28, 29, 37, 31, 32, 33, 26, 34, 35]
#print(sorted(dword_6020A0))
#[0, 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]
posi=0
convert=[0]
while dword_6020A0[posi]:
convert.append(dword_6020A0[posi])
posi=dword_6020A0[posi]
#print(convert)
#[0, 22, 20, 19, 14, 17, 4, 30, 29, 28, 27, 36, 34, 33, 32, 31, 37, 35, 26, 25, 5, 24, 15, 23, 16, 13, 12, 8, 21, 11, 10, 18, 3, 2, 6, 9, 7, 1]
posi=0
before_convert=[0 for i in range(38)]
for i in range(37):
before_convert[convert[i+1]]=after_convert[convert[i]]
before_convert[0]=after_convert[1]
#print(before_convert)
#[133, 67, 104, 133, 245, 38, 60, 61, 39, 245, 51, 104, 62, 60, 118, 38, 245, 118, 165, 245, 19, 165, 61, 245, 62, 165, 45, 61, 245, 7, 60, 118, 29, 60, 15, 104, 133, 169]

dic={43:"!",44:"\"",25:"#",26:"$",27:"%",28:"&",41:"'",30:"(",31:")",32:"*",33:"+",40:",",35:"-",39:".",37:"/",0:"0",39:"1",40:"2",41:"3",42:"4",43:"5",44:"6",45:"7",46:"8",47:"9",37:":",38:";",50:"<",51:"=",52:">",36:"?",54:"@",10:"A",11:"B",12:"C",13:"D",14:"E",15:"F",16:"G",17:"H",18:"I",19:"J",20:"K",21:"L",22:"M",23:"N",24:"O",25:"P",26:"Q",27:"R",28:"S",29:"T",30:"U",31:"V",32:"W",33:"X",34:"Y",35:"Z",81:"[",82:"\\",83:"]",84:"^",46:"_",86:"`",10:"a",11:"b",12:"c",13:"d",14:"e",15:"f",16:"g",17:"h",18:"i",19:"j",20:"k",21:"l",22:"m",23:"n",24:"o",25:"p",26:"q",27:"r",28:"s",29:"t",30:"u",31:"v",32:"w",33:"x",34:"y",35:"z",113:"{",114:"|",115:"}"}

get_frequency=[0,0,0,0,0,0,0,0,0,0,104,30,15,29,169,19,38,67,60,0,20,39,28,118,165,26,0,61,51,133,45,7,34,0,62,0,0,0,0,0,0,40,71,0,0,66,245,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

for i in before_convert:
print(dic[get_frequency.index(i)],end="")

flag:QCTF{that_girl_saying_no_for_your_vindicate}

babymips

利用retdec进行反编译(并不准确,需要结合汇编来看)

python .\retdec-decompiler.py C:\Users\misaka\Downloads\babymips

得到c文件,内容为反编译代码

关键函数为

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
int32_t function_4009a8(void) {
// 0x4009a8
setbuf(g6, NULL);
setbuf(g7, NULL);
printf("Give me your flag:");
int32_t str; // bp-44, 0x4009a8
scanf("%32s", &str);
int32_t v1 = 0;
int32_t v2 = 0; // 0x400a70
char * v3 = (char *)((int32_t)&v1 + 4 + v2); // 0x400a28
*v3 = (char)((0x1000000 * (int32_t)*v3 ^ 0x20000000 - 0x1000000 * v2) / 0x1000000);
v2 = v1 + 1;
v1 = v2;
while (v2 < 32) {
// 0x400a1c
v3 = (char *)((int32_t)&v1 + 4 + v2);
*v3 = (char)((0x1000000 * (int32_t)*v3 ^ 0x20000000 - 0x1000000 * v2) / 0x1000000);
v2 = v1 + 1;
v1 = v2;
}
// 0x400a8c
int32_t puts_rc; // 0x4009a8
if (strncmp((char *)&str, g8, 5) == 0) {
// 0x400ab4
puts_rc = function_4007f0(&str);
} else {
// 0x400acc
puts_rc = puts("Wrong");
}
// 0x400adc
return puts_rc;
}
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
int32_t function_4007f0(int32_t * str) {
int32_t v1 = (int32_t)str;
int32_t v2 = 5; // 0x400930
if (strlen((char *)str) > 5) {
char * v3 = (char *)(v2 + v1);
unsigned char v4 = *v3;
char v5; // 0x4007f0
if (v2 % 2 == 0) {
// 0x400898
v5 = (0x1000000 * (int32_t)(v4 / 64) | 0x4000000 * (int32_t)v4) / 0x1000000;
} else {
// 0x400828
v5 = (0x1000000 * (int32_t)(v4 / 4) | 0x40000000 * (int32_t)v4) / 0x1000000;
}
// 0x400900
*v3 = v5;
v2++;
while (v2 < strlen((char *)str)) {
// 0x400814
v3 = (char *)(v2 + v1);
v4 = *v3;
if (v2 % 2 == 0) {
// 0x400898
v5 = (0x1000000 * (int32_t)(v4 / 64) | 0x4000000 * (int32_t)v4) / 0x1000000;
} else {
// 0x400828
v5 = (0x1000000 * (int32_t)(v4 / 4) | 0x40000000 * (int32_t)v4) / 0x1000000;
}
// 0x400900
*v3 = v5;
v2++;
}
}
// 0x400934
int32_t puts_rc; // 0x4007f0
if (strncmp((char *)(v1 + 5), (char *)g9, 27) == 0) {
// 0x400964
puts_rc = puts("Right!");
} else {
// 0x40097c
puts_rc = puts("Wrong!");
}
// 0x40098c
return puts_rc;
}

可知信息:

  1. 输入的flag的长度为32

  2. 对输入的flag进行某种异或操作后,前五个字符要与Q|j{g相同

  3. 剩下的字符进行位移操作和与操作后,得到的字符数组要与0x52, 0xFD, 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41, 0x54, 0xA0, 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0, 0x16, 0x79, 0x1A, 0x15, 0x5B, 0x75, 0x1F相同

然后从汇编得到更详细的信息

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
.text:00400A1C loc_400A1C:                              # CODE XREF: sub_4009A8+DC↓j
.text:00400A1C lw $v0, 0x48+var_30($fp)
.text:00400A20 addiu $v1, $fp, 0x48+var_30 # v1为字符数组中的each_char
.text:00400A24 addu $v0, $v1, $v0
.text:00400A28 lb $v1, 4($v0)
.text:00400A2C lw $v0, 0x48+var_30($fp)
.text:00400A30 nop
.text:00400A34 andi $v0, 0xFF
.text:00400A38 li $a0, 32
.text:00400A3C subu $v0, $a0, $v0 # v0=32-v0
.text:00400A40 andi $v0, 0xFF
.text:00400A44 sll $v0, 24
.text:00400A48 sra $v0, 24
.text:00400A4C xor $v0, $v1, $v0
.text:00400A50 sll $v1, $v0, 24
.text:00400A54 sra $v1, 24
.text:00400A58 lw $v0, 0x48+var_30($fp)
.text:00400A5C addiu $a0, $fp, 0x48+var_30
.text:00400A60 addu $v0, $a0, $v0
.text:00400A64 sb $v1, 4($v0)
.text:00400A68 lw $v0, 0x48+var_30($fp)
.text:00400A6C nop
.text:00400A70 addiu $v0, 1
.text:00400A74 sw $v0, 0x48+var_30($fp)
.text:00400A78
.text:00400A78 loc_400A78: # CODE XREF: sub_4009A8+6C↑j
.text:00400A78 lw $v0, 0x48+var_30($fp)
.text:00400A7C nop
.text:00400A80 slti $v0, 32 # 猜测v0为数组下标
.text:00400A84 bnez $v0, loc_400A1C
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
.text:004007F0 sub_4007F0:                              # CODE XREF: sub_4009A8+114↓p
.text:004007F0
.text:004007F0 var_10 = -0x10
.text:004007F0 var_8 = -8
.text:004007F0 var_4 = -4
.text:004007F0 arg_0 = 0
.text:004007F0
.text:004007F0 addiu $sp, -0x28 # Add Immediate Unsigned
.text:004007F4 sw $ra, 0x28+var_4($sp) # Store Word
.text:004007F8 sw $fp, 0x28+var_8($sp) # Store Word
.text:004007FC move $fp, $sp
.text:00400800 sw $a0, 0x28+arg_0($fp) # Store Word
.text:00400804 li $v0, 5 # Load Immediate
.text:00400808 sw $v0, 0x28+var_10($fp) # Store Word
.text:0040080C b loc_400910 # Branch Always
.text:00400810 nop
.text:00400814 # ---------------------------------------------------------------------------
.text:00400814
.text:00400814 loc_400814: # CODE XREF: sub_4007F0+13C↓j
.text:00400814 lw $v0, 0x28+var_10($fp) # v0为下标
.text:00400818 nop
.text:0040081C andi $v0, 1 # v0%2
.text:00400820 beqz $v0, loc_400898 # v0%2==0
.text:00400824 nop # v0%2==1
.text:00400828 lw $v0, 0x28+var_10($fp) # v0=str[v0]
.text:0040082C lw $v1, 0x28+arg_0($fp) # Load Word
.text:00400830 nop
.text:00400834 addu $v0, $v1, $v0 # Add Unsigned
.text:00400838 lb $v0, 0($v0) # Load Byte
.text:0040083C nop
.text:00400840 sra $v0, 2 # v0>>2
.text:00400844 sll $a0, $v0, 24 # Shift Left Logical
.text:00400848 sra $a0, 24 # Shift Right Arithmetic
.text:0040084C lw $v0, 0x28+var_10($fp) # Load Word
.text:00400850 lw $v1, 0x28+arg_0($fp) # Load Word
.text:00400854 nop
.text:00400858 addu $v0, $v1, $v0 # Add Unsigned
.text:0040085C lb $v0, 0($v0) # Load Byte
.text:00400860 nop
.text:00400864 sll $v0, 6 # v0<<6
.text:00400868 sll $v1, $v0, 24 # Shift Left Logical
.text:0040086C sra $v1, 24 # Shift Right Arithmetic
.text:00400870 lw $v0, 0x28+var_10($fp) # Load Word
.text:00400874 lw $a1, 0x28+arg_0($fp) # Load Word
.text:00400878 nop
.text:0040087C addu $v0, $a1, $v0 # Add Unsigned
.text:00400880 or $v1, $a0, $v1 # (v0>>2)|(v0<<6)
.text:00400884 sll $v1, 24 # Shift Left Logical
.text:00400888 sra $v1, 24 # Shift Right Arithmetic
.text:0040088C sb $v1, 0($v0) # Store Byte
.text:00400890 b loc_400900 # Branch Always
.text:00400894 nop
.text:00400898 # ---------------------------------------------------------------------------
.text:00400898
.text:00400898 loc_400898: # CODE XREF: sub_4007F0+30↑j
.text:00400898 lw $v0, 0x28+var_10($fp) # v0%2==0
.text:0040089C lw $v1, 0x28+arg_0($fp) # Load Word
.text:004008A0 nop
.text:004008A4 addu $v0, $v1, $v0 # Add Unsigned
.text:004008A8 lb $v0, 0($v0) # Load Byte
.text:004008AC nop
.text:004008B0 sll $v0, 2 # v0<<2
.text:004008B4 sll $a0, $v0, 24 # Shift Left Logical
.text:004008B8 sra $a0, 24 # Shift Right Arithmetic
.text:004008BC lw $v0, 0x28+var_10($fp) # Load Word
.text:004008C0 lw $v1, 0x28+arg_0($fp) # Load Word
.text:004008C4 nop
.text:004008C8 addu $v0, $v1, $v0 # Add Unsigned
.text:004008CC lb $v0, 0($v0) # Load Byte
.text:004008D0 nop
.text:004008D4 sra $v0, 6 # v0>>6
.text:004008D8 sll $v1, $v0, 24 # Shift Left Logical
.text:004008DC sra $v1, 24 # Shift Right Arithmetic
.text:004008E0 lw $v0, 0x28+var_10($fp) # Load Word
.text:004008E4 lw $a1, 0x28+arg_0($fp) # Load Word
.text:004008E8 nop
.text:004008EC addu $v0, $a1, $v0 # Add Unsigned
.text:004008F0 or $v1, $a0, $v1 # (v0>>6)|(v0<<2)
.text:004008F4 sll $v1, 24 # Shift Left Logical
.text:004008F8 sra $v1, 24 # Shift Right Arithmetic
.text:004008FC sb $v1, 0($v0) # Store Byte
.text:00400900
.text:00400900 loc_400900: # CODE XREF: sub_4007F0+A0↑j
.text:00400900 lw $v0, 0x28+var_10($fp) # Load Word
.text:00400904 nop
.text:00400908 addiu $v0, 1 # Add Immediate Unsigned
.text:0040090C sw $v0, 0x28+var_10($fp) # Store Word
.text:00400910
.text:00400910 loc_400910: # CODE XREF: sub_4007F0+1C↑j
.text:00400910 lw $a0, 0x28+arg_0($fp) # s
.text:00400914 jal strlen # Jump And Link
.text:00400918 nop
.text:0040091C move $v1, $v0
.text:00400920 lw $v0, 0x28+var_10($fp) # Load Word
.text:00400924 nop
.text:00400928 sltu $v0, $v1 # Set on Less Than Unsigned
.text:0040092C bnez $v0, loc_400814 # v0为下标
.text:00400930 nop
.text:00400934 lw $v0, 0x28+arg_0($fp) # Load Word
.text:00400938 nop
.text:0040093C addiu $v1, $v0, 5 # Add Immediate Unsigned
.text:00400940 lw $v0, off_410D04 # Load Word
.text:00400948 li $a2, 0x1B # n
.text:0040094C move $a1, $v0 # s2
.text:00400950 move $a0, $v1 # s1
.text:00400954 jal strncmp # Jump And Link
.text:00400958 nop
.text:0040095C bnez $v0, loc_40097C # Branch on Not Zero
.text:00400960 nop
.text:00400964 lui $v0, 0x40 # Load Upper Immediate
.text:00400968 addiu $a0, $v0, (aRight - 0x400000) # "Right!"
.text:0040096C jal puts # Jump And Link
.text:00400970 nop
.text:00400974 b loc_40098C # Branch Always
.text:00400978 nop
.text:0040097C # ---------------------------------------------------------------------------
.text:0040097C
.text:0040097C loc_40097C: # CODE XREF: sub_4007F0+16C↑j
.text:0040097C lui $v0, 0x40 # Load Upper Immediate
.text:00400980 addiu $a0, $v0, (aWrong - 0x400000) # "Wrong!"
.text:00400984 jal puts # Jump And Link
.text:00400988 nop
.text:0040098C
.text:0040098C loc_40098C: # CODE XREF: sub_4007F0+184↑j
.text:0040098C nop
.text:00400990 move $sp, $fp
.text:00400994 lw $ra, 0x28+var_4($sp) # Load Word
.text:00400998 lw $fp, 0x28+var_8($sp) # Load Word
.text:0040099C addiu $sp, 0x28 # Add Immediate Unsigned
.text:004009A0 jr $ra # Jump Register
.text:004009A4 nop
.text:004009A4 # End of function sub_4007F0

可得exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data=[81, 124, 106, 123, 103, 82, 253, 22, 164, 137, 189, 146, 128, 19, 65, 84, 160, 141, 69, 24, 129, 222, 252, 149, 240, 22, 121, 26, 21, 91, 117, 31]
for i in range(5,32):
if i%2==0:
for j in range(256):
v5=(j>>6)|(j<<2)
v5=v5&0xff
if v5==data[i]:
data[i]=j
break
else:
for j in range(256):
v5=(j>>2)|(j<<6)
v5=v5&0xff
if v5==data[i]:
data[i]=j
break
print(data)
for i in range(32):
print(chr(data[i]^(32-i)),end="")
1
2
[81, 124, 106, 123, 103, 73, 127, 88, 41, 38, 111, 74, 32, 76, 80, 81, 40, 54, 81, 96, 96, 123, 63, 86, 60, 88, 94, 104, 69, 109, 93, 124]
qctf{ReA11y_4_B@89_mlp5_4_XmAn_}

flagqctf{ReA11y_4_B@89_mlp5_4_XmAn_}

XCTF 4th-WHCTF-2017

BABYRE

动态分析

  • 通过汇编分析
main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [rsp+0h] [rbp-20h]
int v5; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= 181; ++i )
{
envp = (const char **)(*((unsigned __int8 *)judge + i) ^ 0xCu);
*((_BYTE *)judge + i) ^= 0xCu;
}
printf("Please input flag:", argv, envp);
__isoc99_scanf("%20s", &s);
v5 = strlen(&s);
if ( v5 == 14 && (unsigned int)judge(&s) )
puts("Right!");
else
puts("Wrong!");
return 0;
}

尝试进入judge函数

1
2
3
4
Decompilation failure:
600B01: positive sp value has been found

Please refer to the manual to find appropriate actions

judge汇编

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
.data:0000000000600B00                 public judge
.data:0000000000600B00 judge proc far ; CODE XREF: main+80↑p
.data:0000000000600B00 ; DATA XREF: main+16↑r ...
.data:0000000000600B00 pop rcx
.data:0000000000600B01 test ecx, r13d
.data:0000000000600B04 test [rcx-2Ch], r14d
.data:0000000000600B08 retf 0EC49h
.data:0000000000600B08 judge endp ; sp-analysis failed
.data:0000000000600B08
.data:0000000000600B08 ; ---------------------------------------------------------------------------
.data:0000000000600B0B db 6Ah ; j
.data:0000000000600B0C db 0CAh
.data:0000000000600B0D db 49h ; I
.data:0000000000600B0E db 0EDh
.data:0000000000600B0F db 61h ; a
.data:0000000000600B10 db 0CAh
.data:0000000000600B11 db 49h ; I
.data:0000000000600B12 db 0EEh
.data:0000000000600B13 db 6Fh ; o
.data:0000000000600B14 db 0CAh
.data:0000000000600B15 db 49h ; I
.data:0000000000600B16 db 0EFh
.data:0000000000600B17 db 68h ; h
.data:0000000000600B18 db 0CAh
.data:0000000000600B19 db 49h ; I
.data:0000000000600B1A db 0E8h
.data:0000000000600B1B db 73h ; s
.data:0000000000600B1C db 0CAh
.data:0000000000600B1D db 49h ; I
.data:0000000000600B1E db 0E9h
.data:0000000000600B1F db 67h ; g
.data:0000000000600B20 db 0CAh
.data:0000000000600B21 db 49h ; I
.data:0000000000600B22 db 0EAh
.data:0000000000600B23 db 3Bh ; ;
.data:0000000000600B24 db 0CAh
.data:0000000000600B25 db 49h ; I
.data:0000000000600B26 db 0EBh
.data:0000000000600B27 db 68h ; h
.data:0000000000600B28 db 0CAh
.data:0000000000600B29 db 49h ; I
.data:0000000000600B2A db 0E4h
.data:0000000000600B2B db 37h ; 7
.data:0000000000600B2C db 0CAh
.data:0000000000600B2D db 49h ; I
.data:0000000000600B2E db 0E5h
.data:0000000000600B2F db 5Ah ; Z
.data:0000000000600B30 db 0CAh
.data:0000000000600B31 db 49h ; I
.data:0000000000600B32 db 0E6h
.data:0000000000600B33 db 6Ch ; l
.data:0000000000600B34 db 0CAh
.data:0000000000600B35 db 49h ; I
.data:0000000000600B36 db 0E7h
.data:0000000000600B37 db 37h ; 7
.data:0000000000600B38 db 0CAh
.data:0000000000600B39 db 49h ; I
.data:0000000000600B3A db 0E0h
.data:0000000000600B3B db 62h ; b
.data:0000000000600B3C db 0CAh
.data:0000000000600B3D db 49h ; I
.data:0000000000600B3E db 0E1h
.data:0000000000600B3F db 7Ch ; |
.data:0000000000600B40 db 0CBh
.data:0000000000600B41 db 49h ; I
.data:0000000000600B42 db 0F0h
.data:0000000000600B43 db 0Ch
.data:0000000000600B44 db 0Ch
.data:0000000000600B45 db 0Ch
.data:0000000000600B46 db 0Ch
.data:0000000000600B47 db 0E7h
.data:0000000000600B48 db 24h ; $
.data:0000000000600B49 db 87h
.data:0000000000600B4A db 49h ; I
.data:0000000000600B4B db 0F0h
.data:0000000000600B4C db 44h ; D
.data:0000000000600B4D db 6Fh ; o
.data:0000000000600B4E db 0DCh
.data:0000000000600B4F db 44h ; D
.data:0000000000600B50 db 87h
.data:0000000000600B51 db 49h ; I
.data:0000000000600B52 db 0D4h
.data:0000000000600B53 db 44h ; D
.data:0000000000600B54 db 0Dh
.data:0000000000600B55 db 0DCh
.data:0000000000600B56 db 87h
.data:0000000000600B57 db 59h ; Y
.data:0000000000600B58 db 0F0h
.data:0000000000600B59 db 44h ; D
.data:0000000000600B5A db 6Fh ; o
.data:0000000000600B5B db 0C6h
.data:0000000000600B5C db 44h ; D
.data:0000000000600B5D db 87h
.data:0000000000600B5E db 59h ; Y
.data:0000000000600B5F db 0D4h
.data:0000000000600B60 db 44h ; D
.data:0000000000600B61 db 0Dh
.data:0000000000600B62 db 0C6h
.data:0000000000600B63 db 3
.data:0000000000600B64 db 0BAh
.data:0000000000600B65 db 1Eh
.data:0000000000600B66 db 87h
.data:0000000000600B67 db 41h ; A
.data:0000000000600B68 db 0F0h
.data:0000000000600B69 db 3Dh ; =
.data:0000000000600B6A db 0C6h
.data:0000000000600B6B db 84h
.data:0000000000600B6C db 1Ch
.data:0000000000600B6D db 8Fh
.data:0000000000600B6E db 49h ; I
.data:0000000000600B6F db 0F0h
.data:0000000000600B70 db 0Dh
.data:0000000000600B71 db 8Fh
.data:0000000000600B72 db 71h ; q
.data:0000000000600B73 db 0F0h
.data:0000000000600B74 db 1
.data:0000000000600B75 db 72h ; r
.data:0000000000600B76 db 0DEh
.data:0000000000600B77 db 0CBh
.data:0000000000600B78 db 49h ; I
.data:0000000000600B79 db 0F0h
.data:0000000000600B7A db 0Ch
.data:0000000000600B7B db 0Ch
.data:0000000000600B7C db 0Ch
.data:0000000000600B7D db 0Ch
.data:0000000000600B7E db 0E7h
.data:0000000000600B7F db 25h ; %
.data:0000000000600B80 db 87h
.data:0000000000600B81 db 49h ; I
.data:0000000000600B82 db 0F0h
.data:0000000000600B83 db 44h ; D
.data:0000000000600B84 db 6Fh ; o
.data:0000000000600B85 db 0DCh
.data:0000000000600B86 db 44h ; D
.data:0000000000600B87 db 87h
.data:0000000000600B88 db 49h ; I
.data:0000000000600B89 db 0D4h
.data:0000000000600B8A db 44h ; D
.data:0000000000600B8B db 0Dh
.data:0000000000600B8C db 0DCh
.data:0000000000600B8D db 3
.data:0000000000600B8E db 0BAh
.data:0000000000600B8F db 1Ch
.data:0000000000600B90 db 87h
.data:0000000000600B91 db 49h ; I
.data:0000000000600B92 db 0F0h
.data:0000000000600B93 db 44h ; D
.data:0000000000600B94 db 94h
.data:0000000000600B95 db 3
.data:0000000000600B96 db 0BAh
.data:0000000000600B97 db 48h ; H
.data:0000000000600B98 db 9
.data:0000000000600B99 db 0ECh
.data:0000000000600B9A db 34h ; 4
.data:0000000000600B9B db 0CEh
.data:0000000000600B9C db 78h ; x
.data:0000000000600B9D db 0Bh
.data:0000000000600B9E db 0B4h
.data:0000000000600B9F db 0Ch
.data:0000000000600BA0 db 0Ch
.data:0000000000600BA1 db 0Ch
.data:0000000000600BA2 db 0Ch
.data:0000000000600BA3 db 0E7h
.data:0000000000600BA4 db 3
.data:0000000000600BA5 db 8Fh
.data:0000000000600BA6 db 49h ; I
.data:0000000000600BA7 db 0F0h
.data:0000000000600BA8 db 0Dh
.data:0000000000600BA9 db 8Fh
.data:0000000000600BAA db 71h ; q
.data:0000000000600BAB db 0F0h
.data:0000000000600BAC db 1
.data:0000000000600BAD db 72h ; r
.data:0000000000600BAE db 0DDh
.data:0000000000600BAF db 0B4h
.data:0000000000600BB0 db 0Dh
.data:0000000000600BB1 db 0Ch
.data:0000000000600BB2 db 0Ch
.data:0000000000600BB3 db 0Ch
.data:0000000000600BB4 db 51h ; Q
.data:0000000000600BB5 db 0CFh
.data:0000000000600BB5 _data ends

if ( v5 == 14 && (unsigned int)judge(&s) )处下断点进行动态调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.data:0000000000600B08 mov     byte ptr [rbp-20h], 66h
.data:0000000000600B08 judge endp ; sp-analysis failed
.data:0000000000600B08
.data:0000000000600B0C mov byte ptr [rbp-1Fh], 6Dh
.data:0000000000600B10 mov byte ptr [rbp-1Eh], 63h
.data:0000000000600B14 mov byte ptr [rbp-1Dh], 64h
.data:0000000000600B18 mov byte ptr [rbp-1Ch], 7Fh
.data:0000000000600B1C mov byte ptr [rbp-1Bh], 6Bh
.data:0000000000600B20 mov byte ptr [rbp-1Ah], 37h
.data:0000000000600B24 mov byte ptr [rbp-19h], 64h
.data:0000000000600B28 mov byte ptr [rbp-18h], 3Bh
.data:0000000000600B2C mov byte ptr [rbp-17h], 56h
.data:0000000000600B30 mov byte ptr [rbp-16h], 60h
.data:0000000000600B34 mov byte ptr [rbp-15h], 3Bh
.data:0000000000600B38 mov byte ptr [rbp-14h], 6Eh
.data:0000000000600B3C mov byte ptr [rbp-13h], 70h

向栈中填入数值

loc_600B49
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.data:0000000000600B40 mov     dword ptr [rbp-4], 0            ; 初始化某个值为0
.data:0000000000600B47 jmp short loc_600B71 ; 将这个值与13进行比较,回跳到loc_600B49,猜测该值为输入的字符串的长度[0,13],在loc_600B49中对字符串逐个字符处理,因此可以推断[rbp-4]存储了字符串下标
.data:0000000000600B49 ; ---------------------------------------------------------------------------
.data:0000000000600B49
.data:0000000000600B49 loc_600B49: ; CODE XREF: .data:0000000000600B75↓j
.data:0000000000600B49 mov eax, [rbp-4] ; eax记录字符串下标
.data:0000000000600B4C movsxd rdx, eax
.data:0000000000600B4F mov rax, [rbp-28h]
.data:0000000000600B53 add rax, rdx ; rax=字符串下标所指的地址
.data:0000000000600B56 mov edx, [rbp-4]
.data:0000000000600B59 movsxd rcx, edx
.data:0000000000600B5C mov rdx, [rbp-28h]
.data:0000000000600B60 add rdx, rcx
.data:0000000000600B63 movzx edx, byte ptr [rdx] ; edx=字符串下标所指的值
.data:0000000000600B66 mov ecx, [rbp-4] ; ecx为字符串下标
.data:0000000000600B69 xor edx, ecx ; 将字符串下标与字符串下标所指的值进行异或
.data:0000000000600B6B mov [rax], dl ; 将异或所得的结果填入字符串下标的位置
.data:0000000000600B6D add dword ptr [rbp-4], 1
.data:0000000000600B71
.data:0000000000600B71 loc_600B71: ; CODE XREF: .data:0000000000600B47↑j
.data:0000000000600B71 cmp dword ptr [rbp-4], 13 ; 将这个值与13进行比较,回跳到loc_600B49,猜测该值为输入的字符串的长度[0,13],在loc_600B49中对字符串逐个字符处理,因此可以推断[rbp-4]存储了字符串下标
.data:0000000000600B75 jle short loc_600B49 ; eax记录字符串下标

1
2
3
fmcd=[102, 109, 99, 100, 127, 107, 55, 100, 59, 86, 96, 59, 110, 112]
for i in range(len(fmcd)):
print(chr(fmcd[i]^i),end="")

flag{n1c3_j0b}

  • 动态调试是重新生成伪代码

在第12行,即自解密完成后的地方下断点,进行动态调试,使用程序自身的解密程序进行解密

将有红色报错的代码按U(取消原来定义),再按C(重新生成汇编代码),选中600B00-600BB5(judge的起止位置)按P(重新生成function),这时就可以
F5生成judge的伪代码了

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
signed __int64 __fastcall judge(__int64 a1)
{
char v2; // [rsp+8h] [rbp-20h]
char v3; // [rsp+9h] [rbp-1Fh]
char v4; // [rsp+Ah] [rbp-1Eh]
char v5; // [rsp+Bh] [rbp-1Dh]
char v6; // [rsp+Ch] [rbp-1Ch]
char v7; // [rsp+Dh] [rbp-1Bh]
char v8; // [rsp+Eh] [rbp-1Ah]
char v9; // [rsp+Fh] [rbp-19h]
char v10; // [rsp+10h] [rbp-18h]
char v11; // [rsp+11h] [rbp-17h]
char v12; // [rsp+12h] [rbp-16h]
char v13; // [rsp+13h] [rbp-15h]
char v14; // [rsp+14h] [rbp-14h]
char v15; // [rsp+15h] [rbp-13h]
int i; // [rsp+24h] [rbp-4h]

v2 = 102;
v3 = 109;
v4 = 99;
v5 = 100;
v6 = 127;
v7 = 107;
v8 = 55;
v9 = 100;
v10 = 59;
v11 = 86;
v12 = 96;
v13 = 59;
v14 = 110;
v15 = 112;
for ( i = 0; i <= 13; ++i )
*(_BYTE *)(i + a1) ^= i;
for ( i = 0; i <= 13; ++i )
{
if ( *(_BYTE *)(i + a1) != *(&v2 + i) )
return 0LL;
}
return 1LL;
}

同样得到exp

静态分析

judge方法是判断的主要逻辑,在第15行时调用判断,但无法直接进行静态分析,因为judge模块已经被进行了加密,在第7-11行先进行smc自解密,后面才能正常运行,所以按照7-11行的逻辑对judge模块进行解密,使用IDApython手动解密

1
2
3
posi=0x600B00
for i in range(182):
PatchByte(posi+i,Byte(posi+i)^0xC)

Patch后,将有红色报错的代码按U(取消原来定义),再按C(重新生成汇编代码),选中600B00-600BB5(judge的起止位置)按P(重新生成function),这时就可以
F5生成judge的伪代码了

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
signed __int64 __fastcall judge(__int64 a1)
{
char v2; // [rsp+8h] [rbp-20h]
char v3; // [rsp+9h] [rbp-1Fh]
char v4; // [rsp+Ah] [rbp-1Eh]
char v5; // [rsp+Bh] [rbp-1Dh]
char v6; // [rsp+Ch] [rbp-1Ch]
char v7; // [rsp+Dh] [rbp-1Bh]
char v8; // [rsp+Eh] [rbp-1Ah]
char v9; // [rsp+Fh] [rbp-19h]
char v10; // [rsp+10h] [rbp-18h]
char v11; // [rsp+11h] [rbp-17h]
char v12; // [rsp+12h] [rbp-16h]
char v13; // [rsp+13h] [rbp-15h]
char v14; // [rsp+14h] [rbp-14h]
char v15; // [rsp+15h] [rbp-13h]
int i; // [rsp+24h] [rbp-4h]

v2 = 102;
v3 = 109;
v4 = 99;
v5 = 100;
v6 = 127;
v7 = 107;
v8 = 55;
v9 = 100;
v10 = 59;
v11 = 86;
v12 = 96;
v13 = 59;
v14 = 110;
v15 = 112;
for ( i = 0; i <= 13; ++i )
*(_BYTE *)(i + a1) ^= i;
for ( i = 0; i <= 13; ++i )
{
if ( *(_BYTE *)(i + a1) != *(&v2 + i) )
return 0LL;
}
return 1LL;
}
1
2
3
4
5
6
7
for ( i = 0; i <= 13; ++i )
*(_BYTE *)(i + a1) ^= i;
for ( i = 0; i <= 13; ++i )
{
if ( *(_BYTE *)(i + a1) != *(&v2 + i) )
return 0LL;
}

得到与前面相同的exp

EASYHOOK

进行动态调试

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
HANDLE v4; // eax
DWORD NumberOfBytesWritten; // [esp+4h] [ebp-24h]
char Buffer; // [esp+8h] [ebp-20h]

maybe_print(aPleaseInputFla);
scanf(a31s, &Buffer);
if ( strlen(&Buffer) == 19 )
{
sub_401220();
v4 = CreateFileA(FileName, 0x40000000u, 0, 0, 2u, 0x80u, 0);
WriteFile(v4, &Buffer, 0x13u, &NumberOfBytesWritten, 0);// 对输入进行了修改
// int __stdcall kernel32_WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
// {
// return sub_401080(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
// }
compare(&Buffer, &NumberOfBytesWritten);
if ( NumberOfBytesWritten == 1 )
maybe_print(aRightFlagIsYou);
else
maybe_print(aWrong);
system(aPause);
result = 0;
}
else
{
maybe_print(aWrong);
system(aPause);
result = 0;
}
return result;
}

将断点下在sub_401220();,进入函数,步过直到return sub_4010D0();

sub_401220
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int sub_401220()
{
HMODULE v0; // eax
DWORD v2; // eax

v2 = GetCurrentProcessId(); // 返回当前进程ID
hProcess = OpenProcess(0x1F0FFFu, 0, v2); // 打开一个已存在的进程对象,并返回进程的句柄
v0 = LoadLibraryA(LibFileName); // 链接DLL
dword_40C9C4 = GetProcAddress(v0, ProcName); // 获取WriteFile在DLL中的地址
// .data:0040C9C4 dword_40C9C4 dd 763E35B0h
// kernel32.dll:763E35B0 jmp off_7644105C
// kernel32.dll:7644105C off_7644105C dd offset kernelbase_WriteFile
writefile_address = dword_40C9C4;
if ( !dword_40C9C4 )
return maybe_print(&unk_40A044);
unk_40C9B4 = *writefile_address; // 取出跳转到WriteFile的指令
*(&unk_40C9B4 + 4) = *(writefile_address + 4);
byte_40C9BC = 0xE9u; // 0xE9 的汇编为 jmp
dword_40C9BD = hook_sub - writefile_address - 5;// 计算地址偏移量
return sub_4010D0(); // 对writefile函数进行hook,使其执行sub_4010D0
}

sub_401220();WriteFile进行hook,进入sub_4010D0()

sub_4010D0
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
BOOL sub_4010D0()
{
DWORD v1; // [esp+4h] [ebp-8h]
DWORD flOldProtect; // [esp+8h] [ebp-4h]

v1 = 0;
VirtualProtectEx(hProcess, writefile_address, 5u, 4u, &flOldProtect);// 改变在特定进程中内存区域的保护属性,使得writefile_address可以被更改
// BOOL VirtualProtectEx(
// HANDLE hProcess, // 要修改内存的进程句柄
// LPVOID lpAddress, // 要修改内存的起始地址
// DWORD dwSize, // 页区域大小
// DWORD flNewProtect, // 新访问方式
// PDWORD lpflOldProtect // 原访问方式 用于保存改变前的保护属性 易语言要传址
// );
WriteProcessMemory(hProcess, writefile_address, &byte_40C9BC, 5u, 0);// 将数据写入内存区域
// BOOL WriteProcessMemory(
// HANDLE hProcess,
// LPVOID lpBaseAddress, 指向要写入数据的指定进程中的基地址的指针,系统要先确认其是否可写,在VirtualProtectEx使其可写
// LPCVOID lpBuffer, 指向缓冲区的指针,该缓冲区包含要在指定进程的地址空间中写入的数据,byte_40C9BC的值为0xE9,即jmp
// SIZE_T nSize,
// SIZE_T *lpNumberOfBytesWritten
// );
//
// 执行完成后,得到
// .data:0040C9B0 writefile_address dd 763E35B0h
// kernel32.dll:763E35B0 kernel32_WriteFile proc near
// kernel32.dll:763E35B0 jmp sub_401080
// 可以推断writefile被hook成sub_401080
return VirtualProtectEx(hProcess, writefile_address, 5u, flOldProtect, &v1);// 恢复保护属性
}

WriteProcessMemory(hProcess, writefile_address, &byte_40C9BC, 5u, 0);执行完成时,writefile_address已经变成763E35B0h,被修改为jmp sub_401080,说明已经hook完成

此时,回到main函数中,步过到WriteFile(v4, &Buffer, 0x13u, &NumberOfBytesWritten, 0);,进入WriteFile

1
2
3
kernel32.dll:763E35B0
kernel32.dll:763E35B0 kernel32_WriteFile:
kernel32.dll:763E35B0 jmp hook_sub
1
2
3
4
int __stdcall kernel32_WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
return sub_401080(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
}

进入sub_401080

sub_401080
1
2
3
4
5
6
7
8
9
10
11
int __stdcall hook_sub(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
signed int v5; // ebx

v5 = encode(lpBuffer, nNumberOfBytesToWrite);
recover();
WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
if ( v5 )
*lpNumberOfBytesWritten = 1;
return 0;
}

encode函数即为对输入进行处理的函数

encode
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
signed int __cdecl encode(int input, signed int len)
{
char posi; // al
char v3; // bl
char v4; // cl
int v5; // eax

posi = 0;
if ( len > 0 )
{
do
{
if ( posi == 18 )
{
*(input + 18) ^= 0x13u;
}
else
{
if ( posi % 2 )
v3 = *(posi + input) - posi;
else
v3 = *(posi + input + 2);
*(posi + input) = posi ^ v3;
}
++posi;
}
while ( posi < len );
}
v4 = 0;
if ( len <= 0 )
return 1;
v5 = 0;
while ( byte_40A030[v5] == *(v5 + input) )
{
v5 = ++v4;
if ( v4 >= len )
return 1;
}
return 0;
}

根据逻辑进行恢复

1
2
3
4
5
6
7
8
9
10
11
byte_40A030=[97, 106, 121, 103, 107, 70, 109, 46, 127, 95, 126, 45, 83, 86, 123, 56, 109, 76, 110, 0]
flag=[0 for i in range(19)]
flag[18]=byte_40A030[18]^0x13
for i in range(18):
v3=byte_40A030[i]^i
if i%2:
flag[i]=v3+i
else:
flag[i+2]=v3
for i in flag:
print(chr(i),end="")

得到lag{Ho0k_w1th_Fun},补个f得到flag{Ho0k_w1th_Fun}