小菜鸟一枚 发表于 2020-10-3 12:18

学破解第129天,《攻防世界reverse练习区key》学习

## 学破解第129天,《攻防世界reverse练习区key》学习
前言:
  从小学到大专(计算机网络技术专业),玩过去的,所以学习成绩惨不忍睹,什么证书也没考,直到找不到工作才后悔,不知道怎么办才好。

  2017年12月16日,通过19元注册码注册论坛账号,开始做伸手党,潜水一年多,上来就是找软件。(拿论坛高大上的软件出去装X)

  2018年8月某一天,报名了华中科技大学网络教育本科(计算机科学与技术专业)2018级秋季。(开始提升学历)

  2019年6月17日,不愿再做小菜鸟一枚,开始零基础学习破解。(感谢小糊涂虫大哥在我刚开始学习脱壳时,录制视频解答我的问题)

  2020年7月7日,感谢H大对我的鼓励,拥有了第一篇获得优秀的文章。(接下来希望学习逆向,逆天改命)

  坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:(https://www.52pojie.cn/thread-1278021-1-1.html)
立帖为证!--------记录学习的点点滴滴

### 0x1下载软件观察行为
  1.去攻防世界找到这个key题目,下载文件,PEID查壳,Microsoft Visual C++程序

  2.运行程序看一看,直接黑窗口一闪而过,会自动退出,好像看不出什么?

### 0x2动静结合调试分析
  1.既然会退出,那就找原因,丢到OD里面下一个exitprocess断点,停在了:
```
0031F7FC   7079F4ED/CALL 到 ExitProcess 来自 ucrtbase.7079F4E7
0031F800   FFFFFFFF\ExitCode = 0xFFFFFFFF
0031F804   00000000
0031F808   7079F474返回到 ucrtbase.7079F474 来自 ucrtbase.7079F4B4
```
不知道怎么回溯到用户代码,可能经过多层调用,换成exit断点试试
```
00C3123E|. /74 25         je short key.00C31265
00C31240|. |8B0D C850C300 mov ecx,dword ptr ds:[<&MSVCP140.std::ce>;msvcp140.std::cerr
00C31246|. |BA E452C300   mov edx,key.00C352E4                     ;?W?h?a?t h?a?p?p?e?n?
00C3124B|. |68 502CC300   push key.00C32C50
00C31250|. |E8 AB170000   call key.00C32A00
00C31255|. |8BC8          mov ecx,eax                              ;msvcp140.std::cerr
00C31257|. |FF15 9850C300 call dword ptr ds:[<&MSVCP140.std::basic>;msvcp140.std::basic_ostream<wchar_t,std::char_traits<wchar_t> >::operator<<
00C3125D|. |6A FF         push -0x1                              ; /status = FFFFFFFF (-1.)
00C3125F|. |FF15 6851C300 call dword ptr ds:[<&api-ms-win-crt-runt>; \exit
00C31265|> \8D55 AC       lea edx,
```
往上翻,记下段首00C31100

&emsp;&emsp;2.看退出函数的返回后的用户代码段首是00C31100 ,用IDA F5反编译一下401100这个函数,看,主要用户代码都出来了,省时省力。
```
int sub_401100()
{
signed int v0; // esi@1
signed int v1; // esi@3
unsigned int v2; // edi@3
void **v3; // ebx@3
void **v4; // eax@4
int v5; // ecx@7
int v6; // ST04_4@7
int v7; // ST08_4@7
int v8; // ST0C_4@7
int v9; // eax@8
char *v10; // esi@10
int v11; // ecx@16
void **v12; // eax@18
int v13; // eax@20
int v14; // ecx@20
int v15; // eax@21
int v16; // eax@21
int v17; // eax@21
int v18; // eax@21
int v19; // eax@21
int v20; // eax@21
int v21; // eax@21
const char *v22; // edx@21
int v23; // eax@23
int result; // eax@27
int (__cdecl *v25)(int); // @20
int Dst; // @7
char v27; // @7
char v28; // @10
int v29; // @16
char v30; // @16
int v31; // @16
int v32; // @16
char v33; // @16
FILE *File; // @10
char v35; // @23
void *v36; // @1
int v37; // @1
unsigned int v38; // @1
void *v39; // @1
int v40; // @1
unsigned int v41; // @1
void *Memory; // @1
int v43; // @1
unsigned int v44; // @1
__int128 v45; // @1
__int16 v46; // @1
char v47; // @1
int v48; // @1

v41 = 15;
v40 = 0;
LOBYTE(v39) = 0;
v48 = 0;
v38 = 15;
v37 = 0;
LOBYTE(v36) = 0;
LOBYTE(v48) = 1;
v0 = 0;
v43 = 1684630885;
LOWORD(v44) = 97;
*(_OWORD *)Memory = xmmword_40528C;
v46 = 11836;
v47 = 0;
v45 = xmmword_4052A4;
do
{
    sub_4021E0(1u, (*((_BYTE *)Memory + v0) ^ *((_BYTE *)&v45 + v0)) + 22);
    ++v0;
}
while ( v0 < 18 );
v1 = 0;
v44 = 15;
v43 = 0;
LOBYTE(Memory) = 0;
LOBYTE(v48) = 2;
v2 = v38;
v3 = (void **)v36;
do
{
    v4 = &v36;
    if ( v2 >= 0x10 )
      v4 = v3;
    sub_4021E0(1u, *((_BYTE *)v4 + v1++) + 9);
}
while ( v1 < 18 );
memset(&Dst, 0, 0xB8u);
sub_401620(v5, v6, v7, v8);
LOBYTE(v48) = 3;
if ( v27[*(_DWORD *)(Dst + 4)] & 6 )
{
    v9 = sub_402A00(std::cerr, "?W?h?a?t h?a?p?p?e?n?");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v9, sub_402C50);
    exit(-1);
}
sub_402E90(&Dst, &v39);
v10 = &v28;
if ( File )
{
    if ( !(unsigned __int8)sub_4022F0(&v28) )
      v10 = 0;
    if ( fclose(File) )
      v10 = 0;
}
else
{
    v10 = 0;
}
v33 = 0;
v30 = 0;
std::basic_streambuf<char,std::char_traits<char>>::_Init(&v28);
v31 = dword_408590;
File = 0;
v32 = dword_408594;
v29 = 0;
if ( !v10 )
    std::basic_ios<char,std::char_traits<char>>::setstate((char *)&Dst + *(_DWORD *)(Dst + 4), 2, 0);
v12 = Memory;
if ( v44 >= 0x10 )
    v12 = (void **)Memory;
v13 = sub_4020C0(v11, v40, v12, v43);
v14 = std::cout;
v25 = sub_402C50;
if ( v13 )
{
    v22 = "=W=r=o=n=g=K=e=y=";
}
else
{
    v15 = sub_402A00(std::cout, "|------------------------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v15, sub_402C50);
    v16 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v16, sub_402C50);
    v17 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v17, sub_402C50);
    v18 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_402C50);
    v19 = sub_402A00(std::cout, "\\/\\/\\/\\/\\==============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_402C50);
    v20 = sub_402A00(std::cout, " \\/\\/\\/\\/\\=============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v20, sub_402C50);
    v21 = sub_402A00(std::cout, "               |-------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v21, sub_402C50);
    std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_402C50);
    v14 = std::cout;
    v22 = "Congrats You got it!";
    v25 = sub_402C50;
}
v23 = sub_402A00(v14, v22);
std::basic_ostream<char,std::char_traits<char>>::operator<<(v23, v25);
sub_401570(&v35);
std::basic_ios<char,std::char_traits<char>>::~basic_ios<char,std::char_traits<char>>(&v35);
if ( v44 >= 0x10 )
    sub_402630(Memory, v44 + 1);
if ( v2 >= 0x10 )
    sub_402630(v3, v2 + 1);
result = v41;
if ( v41 >= 0x10 )
    result = sub_402630(v39, v41 + 1);
return result;
}
```

&emsp;&emsp;3.代码是有了,但是还是没办法看出为什么退出了,搜一下可疑字符串,看到了:


一个文件路径比较可疑,点开看看,有一个API函数在尝试打开这个文件。
```
01052583|.6A 40         push 0x40
01052585|.6A 01         push 0x1
01052587|.68 B8520501   push key.010552B8                                          ;C:\Users\CSAW2016\haha\flag_dir\flag.txt
0105258C|.FF15 BC500501 call dword ptr ds:[<&MSVCP140.std::_Fiopen>]               ;msvcp140.std::_Fiopen
```

&emsp;&emsp;4.那我就去对应目录创建一个对应的文件,内容随便写一个52pojie试试,打开cmd窗口,输入:
mkdir C:\Users\CSAW2016\haha\flag_dir\
echo 52pojie>C:\Users\CSAW2016\haha\flag_dir\flag.txt

然后可以看到跳过了0004126E 这个退出函数,然后再看IDA中的代码,v13为1,就提示错误的key,可以看到最近的地方是000420C0影响了v13的值。

&emsp;&emsp;5.OD下断点跑过来一看:
```
00041317|.FF75 D4       push                                               ;0x12
0004131A|.0F4345 C4   cmovnb eax,
0004131E|.50            push eax                                                   ;idg_cni~bjbfi|gsxb
0004131F|.FF75 BC       push                                               ;0x7
00041322|.51            push ecx                                                   ;52pojie
00041323|.8D4D AC       lea ecx,
00041326|.E8 950D0000   call key.000420C0
```
咦,为什么IDA里面会有5个参数看一下thiscall函数调用约定,从右往左传递,第一个参数是this指针。

&emsp;&emsp;6.又是一段很长的代码,我们记住自己的目的就好,返回值必须为0。
```
signed int __thiscall sub_4020C0(int this, int a2, unsigned int a3, int a4, unsigned int a5)
{
unsigned int v5; // edi@1
unsigned int v6; // edx@5
int v7; // esi@8
unsigned int v8; // edx@8
bool v9; // cf@12
unsigned __int8 v10; // al@14
unsigned __int8 v11; // al@16
unsigned __int8 v12; // al@18
signed int result; // eax@19

v5 = a3;
if ( *(_DWORD *)(this + 16) < a3 )
    v5 = *(_DWORD *)(this + 16);
if ( *(_DWORD *)(this + 20) >= 0x10u )
    this = *(_DWORD *)this;
v6 = a5;
if ( v5 < a5 )
    v6 = v5;
if ( v6 )
{
    v7 = a4;
    v9 = v6 < 4;
    v8 = v6 - 4;
    if ( v9 )
    {
LABEL_11:
      if ( v8 == -4 )
      goto LABEL_20;
    }
    else
    {
      while ( *(_DWORD *)this == *(_DWORD *)v7 )
      {
      this += 4;
      v7 += 4;
      v9 = v8 < 4;
      v8 -= 4;
      if ( v9 )
          goto LABEL_11;
      }
    }
    v9 = *(_BYTE *)this < *(_BYTE *)v7;
    if ( *(_BYTE *)this != *(_BYTE *)v7
      || v8 != -3
      && ((v10 = *(_BYTE *)(this + 1), v9 = v10 < *(_BYTE *)(v7 + 1), v10 != *(_BYTE *)(v7 + 1))
       || v8 != -2
       && ((v11 = *(_BYTE *)(this + 2), v9 = v11 < *(_BYTE *)(v7 + 2), v11 != *(_BYTE *)(v7 + 2))
      || v8 != -1 && (v12 = *(_BYTE *)(this + 3), v9 = v12 < *(_BYTE *)(v7 + 3), v12 != *(_BYTE *)(v7 + 3)))) )
    {
      result = -v9 | 1;
      goto LABEL_21;
    }
LABEL_20:
    result = 0;
LABEL_21:
    if ( result )
      return result;
}
if ( v5 >= a5 )
    result = v5 != a5;
else
    result = -1;
return result;
}
```
从result 0反向推导,然后定位到LABEL_11:,能看到v8不能等于-4,继续往下看result = -v9 | 1;上面计算中v9必须为0,再继续往下看result = v5 != a5;,可以知道这里v5必须等于a5,这个过程参数很多,有点头皮发麻,动态调试走一下,看看能不能看出点信息。

&emsp;&emsp;7.可以看到是拿52pojie和idg_cni~bjbfi|gsxb相比较,这里比较完,都相等,然后走je跳到xor eax,eax就是正确的流程。
```
00A52104|. /74 34         je short key.00A5213A
00A52106|> |8A01          mov al,byte ptr ds:                                     ;ecx寄存器:52pojie
00A52108|. |3A06          cmp al,byte ptr ds:                                     ;eax寄存器:idg_cni~bjbfi|gsxb
00A5210A|. |75 27         jnz short key.00A52133
00A5210C|. |83FA FD       cmp edx,-0x3
00A5210F|. |74 29         je short key.00A5213A
00A52111|. |8A41 01       mov al,byte ptr ds:
00A52114|. |3A46 01       cmp al,byte ptr ds:
00A52117|. |75 1A         jnz short key.00A52133
00A52119|. |83FA FE       cmp edx,-0x2
00A5211C|. |74 1C         je short key.00A5213A
00A5211E|. |8A41 02       mov al,byte ptr ds:
00A52121|. |3A46 02       cmp al,byte ptr ds:
00A52124|. |75 0D         jnz short key.00A52133
00A52126|. |83FA FF       cmp edx,-0x1
00A52129|. |74 0F         je short key.00A5213A
00A5212B|. |8A41 03       mov al,byte ptr ds:
00A5212E|. |3A46 03       cmp al,byte ptr ds:
00A52131|. |74 07         je short key.00A5213A
00A52133|> |1BC0          sbb eax,eax
00A52135|. |83C8 01       or eax,0x1
00A52138|. |EB 02         jmp short key.00A5213C
00A5213A|> \33C0          xor eax,eax
```

&emsp;&emsp;8.那我把文件的内容替换成idg_cni~bjbfi|gsxb,然后运行程序发现提示成功了,提交flag,正确:


### 0x3总结
&emsp;&emsp;1.这道题看上去很难,想要返回值0,好似要经过多番运算,最后OD一调试,实际上就是把两个传进来的字符串作比较。

&emsp;&emsp;2.刚开始下断点想着exitprocess,没回溯过去,习惯了点OD上面的API断点,后来想到exit就断下,回溯过去了。

&emsp;&emsp;3.有时候看不出反编译的那一段代码作用时,用OD跑起来看看,会有意外发现。

&emsp;&emsp;4.OD和IDA都用起来,便于定位和分析问题。

### 0x4参考资料
加密解密第四版-函数的调用约定

&emsp;&emsp;**PS:善于总结,善于发现,找到分析问题的思路和解决问题的办法。虽然我现在还是零基础的小菜鸟一枚,也许学习逆向逆天改命我会失败,但也有着成功的可能,只要还有希望,就决不放弃!**

小菜鸟一枚 发表于 2022-2-7 18:31

snowhee 发表于 2022-2-7 17:21
师傅我想问一下,result 0反向推导,定位到LABEL_11,然后判断v8等于-4才能跳转LABEL_20,如果v8不等于-4, ...

我们的目的是return 0,只有lable 20才会给将返回值置0,然后向上定位到lable 11,那么v8就必须等于-4,这样就就不会执行lable 21前面那个if,不然,result值必然非0,if一判断true,返回result了。我们必须让他跳转到lable 21,但是不能执行前两行,所以必须先让result为0,跳过第一个if,然后执行这个if ( v5 >= a5 ),这里v5必须等于a5,然后就return 0了.

pipi125874 发表于 2020-10-5 07:14

总有癫嘿想坑朕 发表于 2020-10-4 10:04
看不懂,但是感觉很厉害,支持你一下,活到老学到老,我快40了,刚开始学python,共勉一个。

共勉一个。同样的人,同样的在做,都是一开始!

cptw 发表于 2020-10-3 13:10

感谢楼主分享

樊汶鑫 发表于 2020-10-3 13:43

好像有点看懂,找了半天找出了判断条件和判断结果,然后修改判断结果来达到想要的结果是吧

dbg2020 发表于 2020-10-3 14:53

支持原创。破解是个体力活,学习破解是脑力活

fireant 发表于 2020-10-3 15:17

贵在坚持,看来我也得好好学习

Leland 发表于 2020-10-3 15:27

巧遇学长,我是华科计院的大一新生,在准备ctf的招新{:301_987:}
以后应该也会学这些吧。

静一静 发表于 2020-10-3 16:22

这个游戏还是不错的

行则将至 发表于 2020-10-3 16:23

前排膜拜大神

xiaogui00 发表于 2020-10-3 16:57

谢谢分享,学习了

cheng911001 发表于 2020-10-3 23:14

up加油。
页: [1] 2 3 4 5 6
查看完整版本: 学破解第129天,《攻防世界reverse练习区key》学习