-
-
[原创]Buuctf-reverse-crackMe
-
发表于: 2022-4-6 11:32 12627
-
ps:学习了一下大家写文章的方式,以后就都这么写了,之前的第一篇就懒得改了,参考了网上师傅的wp
题目链接:https://buuoj.cn/challenges#crackMe
通过题目内容可以了解到,用户名是welcomebeijing,密码MD5的小写哈希就是我们的flag。点开程序,可以看到关键字符串,大致的思路就是通过这个字符串可以定位到关键函数,再进行进一步分析。
先用查壳软件看一下程序情况
是一个无壳的32位PE文件,用IDA32打开,定位到主函数
对于一些不是很重要的函数的作用我都直接在上面的函数注明了,比较重要的,我会在下文着重分析。
这个函数就是对user进行了一系列操作,最后把值赋给了byte_416050这个数组,我看着很像RC4加密,后面可以直接动调出来
一下子就能看见有好几个反调试的语句,第一个while循环里的意思就是将输入的密码,例如123456,按0x12,0x34,0x56这样两位切割,然后放在v14数组里面。接下来重点就是第二个循环,我们先来看这个sub_401710函数
在上面这个函数中 v4是user用户名的长度,a3是两位一组后的密码分组个数计数,v4是永远大于a3的。else if里面的操作就是把V6和用户名异或再把值存放在v16里,为了区别,这里将新的v16记作v16'。然后就是sub_401470,我们通过return语句可以推出,v13 == 0xAB94,通过这个我们就可以推出v16'的值。
这里面的a2就是v16',只要v16'的每一位值能等于这里面if判断语句的每一个值就会输出v13 == 0xAB94,所以v16'=dbappfsec。然后我们就可以通过v16'和用户名异或得出v16,再把v16和byte_416050数组异或就可以得出我们要的两位一组分割后的密码,最后拼起来就行。现在我们唯一缺的就是byte_416050数组,这里可以通过动态调试得出
动态调试之前需要先将前面的反调试语句处理一下,将下图处的jz改为jmp就行了
然后我们再去看一下关键位置的汇编代码
可以看见我们只需要观察ecx的值就可以得出byte_416050数组的值,动调得出值为0x2A, 0xD7, 0x92, 0xE9, 0x53, 0xE2, 0xC4, 0xCD。ps:这里动调的值,不知道是程序原因还是什么原因,动调出来的值跟网上动调出来的值不一样,最后搬的网上师傅动调出来的值,有师傅知道的可以在评论区指点一手。
奇怪的是这个结果程序是可以通过的,但是提交到Buuctf没通过,网上的一些wp的结果提交到Buuctf是可以的,但是通不过程序的检验,不知道是不是Buuctf的flag错了。网上其他wp的结果是4eb5f3992391a1ae,大家可以自己试一下。
int
wmain()
{
FILE
*
v0;
/
/
eax
FILE
*
v1;
/
/
eax
char v3;
/
/
[esp
+
3h
] [ebp
-
405h
]
char v4;
/
/
[esp
+
4h
] [ebp
-
404h
] BYREF
char v5[
255
];
/
/
[esp
+
5h
] [ebp
-
403h
] BYREF
char
Format
;
/
/
[esp
+
104h
] [ebp
-
304h
] BYREF
char v7[
255
];
/
/
[esp
+
105h
] [ebp
-
303h
] BYREF
char v8;
/
/
[esp
+
204h
] [ebp
-
204h
] BYREF
char v9[
255
];
/
/
[esp
+
205h
] [ebp
-
203h
] BYREF
char v10;
/
/
[esp
+
304h
] [ebp
-
104h
] BYREF
char v11[
255
];
/
/
[esp
+
305h
] [ebp
-
103h
] BYREF
printf(
"Come one! Crack Me~~~\n"
);
v10
=
0
;
memset(v11,
0
, sizeof(v11));
v8
=
0
;
memset(v9,
0
, sizeof(v9));
while
(
1
)
{
do
{
do
{
printf(
"user(6-16 letters or numbers):"
);
scanf(
"%s"
, &v10);
#接收user
v0
=
(
FILE
*
)sub_4024BE();
fflush(v0);
}
while
( !(unsigned __int8)sub_401000(&v10) );
printf(
"password(6-16 letters or numbers):"
);
scanf(
"%s"
, &v8);
#接收password
v1
=
(
FILE
*
)sub_4024BE();
fflush(v1);
#这几个并没有对接收的字符串做什么操作,就不管了
}
while
( !(unsigned __int8)sub_401000(&v8) );
#检查是不是字母或者数字
sub_401090(&v10);
Format
=
0
;
memset(v7,
0
, sizeof(v7));
v4
=
0
;
memset(v5,
0
, sizeof(v5));
v3
=
((
int
(__cdecl
*
)(char
*
, char
*
))loc_4011A0)(&
Format
, &v4);
#这个应该是判断后输出再试一次的字符串
if
( (unsigned __int8)sub_401830(&v10, &v8) )
#关键的验证函数
{
if
( v3 )
break
;
}
printf(&v4);
}
printf(&
Format
);
return
0
;
}
int
wmain()
{
FILE
*
v0;
/
/
eax
FILE
*
v1;
/
/
eax
char v3;
/
/
[esp
+
3h
] [ebp
-
405h
]
char v4;
/
/
[esp
+
4h
] [ebp
-
404h
] BYREF
char v5[
255
];
/
/
[esp
+
5h
] [ebp
-
403h
] BYREF
char
Format
;
/
/
[esp
+
104h
] [ebp
-
304h
] BYREF
char v7[
255
];
/
/
[esp
+
105h
] [ebp
-
303h
] BYREF
char v8;
/
/
[esp
+
204h
] [ebp
-
204h
] BYREF
char v9[
255
];
/
/
[esp
+
205h
] [ebp
-
203h
] BYREF
char v10;
/
/
[esp
+
304h
] [ebp
-
104h
] BYREF
char v11[
255
];
/
/
[esp
+
305h
] [ebp
-
103h
] BYREF
printf(
"Come one! Crack Me~~~\n"
);
v10
=
0
;
memset(v11,
0
, sizeof(v11));
v8
=
0
;
memset(v9,
0
, sizeof(v9));
while
(
1
)
{
do
{
do
{
printf(
"user(6-16 letters or numbers):"
);
scanf(
"%s"
, &v10);
#接收user
v0
=
(
FILE
*
)sub_4024BE();
fflush(v0);
}
while
( !(unsigned __int8)sub_401000(&v10) );
printf(
"password(6-16 letters or numbers):"
);
scanf(
"%s"
, &v8);
#接收password
v1
=
(
FILE
*
)sub_4024BE();
fflush(v1);
#这几个并没有对接收的字符串做什么操作,就不管了
}
while
( !(unsigned __int8)sub_401000(&v8) );
#检查是不是字母或者数字
sub_401090(&v10);
Format
=
0
;
memset(v7,
0
, sizeof(v7));
v4
=
0
;
memset(v5,
0
, sizeof(v5));
v3
=
((
int
(__cdecl
*
)(char
*
, char
*
))loc_4011A0)(&
Format
, &v4);
#这个应该是判断后输出再试一次的字符串
if
( (unsigned __int8)sub_401830(&v10, &v8) )
#关键的验证函数
{
if
( v3 )
break
;
}
printf(&v4);
}
printf(&
Format
);
return
0
;
}
_BYTE
*
__cdecl sub_401090(_BYTE
*
a1)
{
_BYTE
*
result;
/
/
eax
int
v2;
/
/
[esp
+
Ch] [ebp
-
18h
]
int
v3;
/
/
[esp
+
10h
] [ebp
-
14h
]
_BYTE
*
v4;
/
/
[esp
+
14h
] [ebp
-
10h
]
int
i;
/
/
[esp
+
18h
] [ebp
-
Ch]
char v7;
/
/
[esp
+
20h
] [ebp
-
4h
]
char v8;
/
/
[esp
+
22h
] [ebp
-
2h
]
unsigned __int8 v9;
/
/
[esp
+
23h
] [ebp
-
1h
]
for
( i
=
0
; i <
256
;
+
+
i )
byte_416050[i]
=
i;
v2
=
0
;
v9
=
0
;
v3
=
0
;
result
=
a1;
v4
=
a1;
do
LOBYTE(result)
=
*
v4;
while
(
*
v4
+
+
);
while
( v2 <
256
)
{
v8
=
byte_416050[v2];
v9
+
=
v8
+
a1[v3];
v7
=
byte_416050[v9];
+
+
v3;
byte_416050[v9]
=
v8;
byte_416050[v2]
=
v7;
result
=
(_BYTE
*
)v3;
if
( v3 >
=
v4
-
(a1
+
1
) )
v3
=
0
;
+
+
v2;
}
return
result;
}
_BYTE
*
__cdecl sub_401090(_BYTE
*
a1)
{
_BYTE
*
result;
/
/
eax
int
v2;
/
/
[esp
+
Ch] [ebp
-
18h
]
int
v3;
/
/
[esp
+
10h
] [ebp
-
14h
]
_BYTE
*
v4;
/
/
[esp
+
14h
] [ebp
-
10h
]
int
i;
/
/
[esp
+
18h
] [ebp
-
Ch]
char v7;
/
/
[esp
+
20h
] [ebp
-
4h
]
char v8;
/
/
[esp
+
22h
] [ebp
-
2h
]
unsigned __int8 v9;
/
/
[esp
+
23h
] [ebp
-
1h
]
for
( i
=
0
; i <
256
;
+
+
i )
byte_416050[i]
=
i;
v2
=
0
;
v9
=
0
;
v3
=
0
;
result
=
a1;
v4
=
a1;
do
LOBYTE(result)
=
*
v4;
while
(
*
v4
+
+
);
while
( v2 <
256
)
{
v8
=
byte_416050[v2];
v9
+
=
v8
+
a1[v3];
v7
=
byte_416050[v9];
+
+
v3;
byte_416050[v9]
=
v8;
byte_416050[v2]
=
v7;
result
=
(_BYTE
*
)v3;
if
( v3 >
=
v4
-
(a1
+
1
) )
v3
=
0
;
+
+
v2;
}
return
result;
}
bool
__cdecl sub_401830(
int
a1, const char
*
a2)
{
int
v3;
/
/
[esp
+
18h
] [ebp
-
22Ch
]
int
v4;
/
/
[esp
+
1Ch
] [ebp
-
228h
]
int
v5;
/
/
[esp
+
28h
] [ebp
-
21Ch
]
unsigned
int
v6;
/
/
[esp
+
30h
] [ebp
-
214h
]
char v7;
/
/
[esp
+
36h
] [ebp
-
20Eh
]
char v8;
/
/
[esp
+
37h
] [ebp
-
20Dh
]
char v9;
/
/
[esp
+
38h
] [ebp
-
20Ch
]
unsigned __int8 v10;
/
/
[esp
+
39h
] [ebp
-
20Bh
]
unsigned __int8 v11;
/
/
[esp
+
3Ah
] [ebp
-
20Ah
]
char v12;
/
/
[esp
+
3Bh
] [ebp
-
209h
]
int
v13;
/
/
[esp
+
3Ch
] [ebp
-
208h
] BYREF
char v14;
/
/
[esp
+
40h
] [ebp
-
204h
] BYREF
char v15[
255
];
/
/
[esp
+
41h
] [ebp
-
203h
] BYREF
char v16;
/
/
[esp
+
140h
] [ebp
-
104h
] BYREF
char v17[
255
];
/
/
[esp
+
141h
] [ebp
-
103h
] BYREF
v4
=
0
;
v5
=
0
;
v11
=
0
;
v10
=
0
;
v16
=
0
;
memset(v17,
0
, sizeof(v17));
v14
=
0
;
memset(v15,
0
, sizeof(v15));
v9
=
0
;
v6
=
0
;
v3
=
0
;
while
( v6 < strlen(a2) )
{
if
( isdigit(a2[v6]) )
{
v8
=
a2[v6]
-
48
;
#这里将输入的字符0-9转化成十进制的0-9,就是转换一下类型
}
else
if
( isxdigit(a2[v6]) )
{
if
(
*
((_DWORD
*
)NtCurrentPeb()
-
>SubSystemData
+
3
) !
=
2
)
#反调试的
a2[v6]
=
34
;
v8
=
(a2[v6] |
0x20
)
-
87
;
#意思是减87加32,也就是将字母,例如a转化成16进制数a(就是10)
}
else
{
v8
=
((a2[v6] |
0x20
)
-
97
)
%
6
+
10
;
#将其他的字符转化到十六进制数a-f之间(10-16)
}
__rdtsc();
__rdtsc();
v9
=
v8
+
16
*
v9;
if
( !((
int
)(v6
+
1
)
%
2
) )
#将输入的密码两位一组,存放在v14数组里面
{
*
(&v14
+
v3
+
+
)
=
v9;
v9
=
0
;
}
+
+
v6;
}
while
( v5 <
8
)
{
v10
+
=
byte_416050[
+
+
v11];
v12
=
byte_416050[v11];
v7
=
byte_416050[v10];
byte_416050[v10]
=
v12;
byte_416050[v11]
=
v7;
if
( ((
int
)NtCurrentPeb()
-
>UnicodeCaseTableData &
0x70
) !
=
0
)
#反调试
v12
=
v10
+
v11;
*
(&v16
+
v5)
=
byte_416050[(unsigned __int8)(v7
+
v12)] ^
*
(&v14
+
v4);
#关键的异或操作
if
( (unsigned __int8)
*
(_DWORD
*
)&NtCurrentPeb()
-
>BeingDebugged )
#反调试
{
v10
=
-
83
;
v11
=
43
;
}
sub_401710(&v16, a1, v5
+
+
);
v4
=
v5;
if
( v5 >
=
(unsigned
int
)(&v14
+
strlen(&v14)
+
1
-
v15) )
v4
=
0
;
}
v13
=
0
;
sub_401470(&v16, &v13);
return
v13
=
=
0xAB94
;
}
bool
__cdecl sub_401830(
int
a1, const char
*
a2)
{
int
v3;
/
/
[esp
+
18h
] [ebp
-
22Ch
]
int
v4;
/
/
[esp
+
1Ch
] [ebp
-
228h
]
int
v5;
/
/
[esp
+
28h
] [ebp
-
21Ch
]
unsigned
int
v6;
/
/
[esp
+
30h
] [ebp
-
214h
]
char v7;
/
/
[esp
+
36h
] [ebp
-
20Eh
]
char v8;
/
/
[esp
+
37h
] [ebp
-
20Dh
]
char v9;
/
/
[esp
+
38h
] [ebp
-
20Ch
]
unsigned __int8 v10;
/
/
[esp
+
39h
] [ebp
-
20Bh
]
unsigned __int8 v11;
/
/
[esp
+
3Ah
] [ebp
-
20Ah
]
char v12;
/
/
[esp
+
3Bh
] [ebp
-
209h
]
int
v13;
/
/
[esp
+
3Ch
] [ebp
-
208h
] BYREF
char v14;
/
/
[esp
+
40h
] [ebp
-
204h
] BYREF
char v15[
255
];
/
/
[esp
+
41h
] [ebp
-
203h
] BYREF
char v16;
/
/
[esp
+
140h
] [ebp
-
104h
] BYREF
char v17[
255
];
/
/
[esp
+
141h
] [ebp
-
103h
] BYREF
v4
=
0
;
v5
=
0
;
v11
=
0
;
v10
=
0
;
v16
=
0
;
memset(v17,
0
, sizeof(v17));
v14
=
0
;
memset(v15,
0
, sizeof(v15));
v9
=
0
;
v6
=
0
;
v3
=
0
;
while
( v6 < strlen(a2) )
{
if
( isdigit(a2[v6]) )
{
v8
=
a2[v6]
-
48
;
#这里将输入的字符0-9转化成十进制的0-9,就是转换一下类型
}
else
if
( isxdigit(a2[v6]) )
{
if
(
*
((_DWORD
*
)NtCurrentPeb()
-
>SubSystemData
+
3
) !
=
2
)
#反调试的
a2[v6]
=
34
;
v8
=
(a2[v6] |
0x20
)
-
87
;
#意思是减87加32,也就是将字母,例如a转化成16进制数a(就是10)
}
else
{
v8
=
((a2[v6] |
0x20
)
-
97
)
%
6
+
10
;
#将其他的字符转化到十六进制数a-f之间(10-16)
}
__rdtsc();
__rdtsc();