小菜鸟一枚 发表于 2021-8-21 17:14

学破解第140天,《攻防世界reverse练习区流浪者》学习

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

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

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

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

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

  2021年8月11日,华科学位英语2次不过,仅取得了毕业证书,学业提升失败,开始琢磨考注册类和职称类证书,谋求涨薪

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

### 0x1下载文件
  1.下载CM,是一个压缩包,解压后发现是个exe文件。

  2.通过图标来看是一个MFC程序

  3.使用exeinfo工具查壳:
![https://z3.ax1x.com/2021/08/21/fvtLjJ.png](https://z3.ax1x.com/2021/08/21/fvtLjJ.png)

  4.根据上述信息,知道这是一个32位无壳的MFC程序,可以使用OD和IDA来调试。

### 0x2寻找爆破点
  1.对于window程序不像控制台程序,IDA找到main函数,一层层看就完了,这找到winmain函数,也不知道怎么找到逻辑,还是和我没有window编程基础有关。

  2.还是先用OD找到爆破点,定位关键函数再去看代码对比。

  2.那么我们OD载入程序,F9运行起来,随便输入123456,点击验证,弹出错误提示,再去401000处搜索一下字符串:
![https://z3.ax1x.com/2021/08/21/fvUoSU.png](https://z3.ax1x.com/2021/08/21/fvUoSU.png)

  3.根据上图中错误提示和搜索到的字符串,定位错误弹窗的地方:
```
004017B0/$55            push ebp
004017B1|.8BEC          mov ebp,esp
004017B3|.83EC 44       sub esp,0x44
004017B6|.53            push ebx
004017B7|.56            push esi
004017B8|.57            push edi
004017B9|.6A 00         push 0x0                                 ; /Style = MB_OK|MB_APPLMODAL
004017BB|.68 78354000   push cm.00403578                         ; |错了!
004017C0|.68 70354000   push cm.00403570                         ; |加油!
004017C5|.6A 00         push 0x0                                 ; |hOwner = NULL
004017C7|.FF15 00324000 call dword ptr ds:[<&USER32.MessageBoxA>>; \MessageBoxA
```

  4.通过堆栈按回车键返回调用处:
```
00401857|> \8B55 FC       mov edx,
0040185A|.C64415 DC 00mov byte ptr ss:,0x0
0040185F|.8B45 BC       mov eax,                     ;cm.004035C0
00401862|.50            push eax                                 ; /s2 = 00000001 ???
00401863|.8D4D DC       lea ecx,                        ; |
00401866|.51            push ecx                                 ; |s1 = "KanXueCTF2019JustForhappy"
00401867|.E8 84070000   call <jmp.&MSVCRT.strcmp>                ; \strcmp
0040186C|.83C4 08       add esp,0x8
0040186F|.85C0          test eax,eax
00401871|.75 07         jnz short cm.0040187A                  ;两个字符串不相等,就跳向失败
00401873|.E8 F8FEFFFF   call cm.00401770
00401878|.EB 05         jmp short cm.0040187F
0040187A|>E8 31FFFFFF   call cm.004017B0                         ;失败弹窗函数
```

  5.接着去段首004017F0 处F2下上断点,重启跑过来看一看,这段汇编代码:
```
0040180A|.C745 BC C0354>mov ,cm.004035C0               ;KanXueCTF2019JustForhappy
00401811|.C785 50FFFFFF>mov ,cm.00403580               ;abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ
```
有两个可疑字符串,继续看:
```
0040181B|> /8B45 FC       /mov eax,                     ;eax=0
0040181E|. |8B4D 08       |mov ecx,                         ;ecx=0018F700
00401821|. |833C81 3E   |cmp dword ptr ds:,0x3E       ;堆栈 ds:=00000001
00401825|. |7D 30         |jge short cm.00401857                   ;大于等于62则跳转
00401827|. |8B55 FC       |mov edx,                     ;edx=0
0040182A|. |8B45 08       |mov eax,                         ;eax=0018F700
0040182D|. |833C90 00   |cmp dword ptr ds:,0x0      ;堆栈 ds:=00000001
00401831|. |7C 24         |jl short cm.00401857                  ;小于0则跳转
00401833|. |8B4D FC       |mov ecx,                     ;ecx=0
00401836|. |8B55 08       |mov edx,                         ;edx=0018F700
00401839|. |8B048A      |mov eax,dword ptr ds:      ;eax=1
0040183C|. |8B4D FC       |mov ecx,                     ;ecx=0
0040183F|. |8B95 50FFFFFF |mov edx,                      ;edx="abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ"
00401845|. |8A0402      |mov al,byte ptr ds:            ;al = b
00401848|. |88440D DC   |mov byte ptr ss:,al       ;存入一块内存
0040184C|. |8B4D FC       |mov ecx,                     ;ecx=0
0040184F|. |83C1 01       |add ecx,0x1                           ;ecx+1
00401852|. |894D FC       |mov ,ecx                     ;变量1=ecx
00401855|.^\EB C4         \jmp short cm.0040181B                   ;开始下一轮循环
```
这是一个循环结构,每次我输入的处理一个我输入的变量存入某一块内存地址ebp+ecx-0x24,这个在学习数据结构时我们知道它大概率是数组,而且是4字节对齐的,从一个数组中取出数据al=edx+eax就是赋值的过程,a1=edx,i是我输入的值,并且我输入的值每一位取值必须在[0,62)这个区间。

  6.F4跳过循环,可以看到我输入的123456,变成了bcdefg:
```
00401862|.50            push eax                                 ; /s2 = "KanXueCTF2019JustForhappy"
00401863|.8D4D DC       lea ecx,                        ; |
00401866|.51            push ecx                                 ; |s1 = "bcdefg"
00401867|.E8 84070000   call <jmp.&MSVCRT.strcmp>                ; \strcmp
```
再回过头来看看这两行的可疑的字符串:
KanXueCTF2019JustForhappy
abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ
第一个没得说,一会和我输入的字符串处理后作比较,第二个字符串我们看看,这个字符串所在的起始地址为:0x403580,再看前面赋值语句:
0040183F|.8B95 50FFFFFF |mov edx,                      ;edx="abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ"
00401845|.8A0402      |mov al,byte ptr ds:            ;al = b
edx的值就是0x403580,是不是一下子很清楚了,a1=arr=b,a2=arr=c,a1=arr=c,以此类推通过替换的方式得到bcdefg。

### 0x3代码分析
  1.将程序用32位IDA打开,找到004017B0这个函数,打开看一看代码涨啥样:
```
int __cdecl sub_4017F0(int a1)
{
int result; // eax
char Str1; // BYREF
int v3; //
int v4; //

v4 = 0;
v3 = 0;
while ( *(int *)(a1 + 4 * v4) < 62 && *(int *)(a1 + 4 * v4) >= 0 )
{
    Str1 = aAbcdefghiabcde[*(_DWORD *)(a1 + 4 * v4)];
    ++v4;
}
Str1 = 0;
if ( !strcmp(Str1, "KanXueCTF2019JustForhappy") )
    result = sub_401770();
else
    result = sub_4017B0();
return result;
}
```
aAbcdefghiabcde这个数组存的就是“abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ”,上述代码反过来就是遍历这个数组中的每一个元素和KanXueCTF2019JustForhappy"相等就保存下来,逆向的代码为:
```
char arr = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ";

while (v4 < strlen(arr))
{
      if (flag == arr)
      //取v4的值
}
```
为了循环判断,所以外层还需要加一个循环。

  2.F5反编译的代码和我分析的差不多,接下来就写代码跑flag了,等等,似乎有什么不对?我输入123456是字符,为什么加密的时候作为数字下标呢???显然前面有将字符串如何变成数字下标的函数。
```
004019C2|.52            push edx
004019C3|.E8 28FEFFFF   call cm.004017F0
```
通过OD查找调用过程,发现从004019C3这个地址调用的004017F0函数,向上翻找到段首00401890。

  3.IDA中找到这个函数,F5反编译一下:
```
int __thiscall sub_401890(CWnd *this)
{
CWnd *v1; // eax
int v2; // eax
struct CString *v4; //
int v5; // BYREF
int i; //
char *Str; //
CWnd *v8; //

v8 = this;
v4 = (CWnd *)((char *)this + 100);
v1 = CWnd::GetDlgItem(this, 1002);
CWnd::GetWindowTextA(v1, v4);
v2 = sub_401A30((char *)v8 + 100);
Str = CString::GetBuffer((CWnd *)((char *)v8 + 100), v2);
if ( !strlen(Str) )
    return CWnd::MessageBoxA(v8, "请输入pass!", 0, 0);
for ( i = 0; Str; ++i )
{
    if ( Str > 57 || Str < 48 )
    {
      if ( Str > 122 || Str < 97 )
      {
      if ( Str > 90 || Str < 65 )
          sub_4017B0();
      else
          v5 = Str - 29;
      }
      else
      {
      v5 = Str - 87;
      }
    }
    else
    {
      v5 = Str - 48;
    }
}
return sub_4017F0(v5);
}
```
找到了转换的函数,这种层层嵌套读起来真是要人命,同样逆向过来,大于变成小于等于,小于变成大于等于,-变成+,得到:
```
if (v4 + 48 >= 48 && v4 + 48 <= 57)
{
      str = v4 + 48;
}
if (v4 + 87 >= 97 && v4 + 87 <= 122)
{
      str = v4 + 87;
}
if (v4 + 29 >= 65 && v4 + 29 <= 90)
{
      str = v4 + 29;
}
```

  4.最终整理后的代码如下:
```
#include <iostream>
#include <thread>

using namespace std;

int main()
{
      char flag = "KanXueCTF2019JustForhappy";
      char arr = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ";
      char str = "";
      int i = 0, v4 = 0;

      while (i < strlen(flag)){
                while (v4 < strlen(arr))
                {
                        if (flag == arr) {
                              if (v4 + 48 >= 48 && v4 + 48 <= 57)
                              {
                                        str = v4 + 48;
                              }
                              if (v4 + 87 >= 97 && v4 + 87 <= 122)
                              {
                                        str = v4 + 87;
                              }
                              if (v4 + 29 >= 65 && v4 + 29 <= 90)
                              {
                                        str = v4 + 29;
                              }
                              i++;
                              v4=0;
                              break;
                        }
                        ++v4;
                }
      }

      cout << str << endl;

      system("pause");
}
```

  5.运行后得到:j0rXI4bTeustBiIGHeCF70DDM,,加上flag{}就是flag{j0rXI4bTeustBiIGHeCF70DDM},输入答案验证一下。
![https://z3.ax1x.com/2021/08/21/fxpOtU.png](https://z3.ax1x.com/2021/08/21/fxpOtU.png)

### 0x4总结
  1.通过错误提示定位关键字符串,找到关键跳,关键跳前面的一般就是算法了。
  2.分析的时候不要大意,字符串可能不只经过一次变换,对字符串变化流程加以分析。
  3.编写逆向算法时,如果看不懂原算法,那就利用程序自身算法,脑袋里一定要反过来思考,才能写出逆向程序。
  4.这道1分题花了我6个多小时,还是基础不牢固啊。

### 0x5参考资料
  1.无

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

破解粉丝 发表于 2021-8-21 18:04

支持你这种坚持学习的精神!不管干任何事情都不能缺这种持之以恒的精神!给你点赞!!!!

骄阳似我 发表于 2021-8-21 18:22

真的从小菜鸟变成大佬了。我从菜鸟变成了菜鸡{:301_999:}

DrCatcher 发表于 2021-8-21 18:46

网络教育本科是不是函授?不用到学校坐到教室上课的那种?

kkyy123 发表于 2021-8-21 19:59

https://www.aliyundrive.com/s/ifzfsgrHHQn 加油,又兴趣分析下这个吗?

stevejobs111 发表于 2021-8-21 21:23

持之以恒,才能无懈可击

咔c君 发表于 2021-8-21 22:38

学习了不错

从那以后的你 发表于 2021-8-22 00:04

谢谢分享

tohyueyun 发表于 2021-8-22 00:09

佩服楼主的坚持。给您点赞 {:1_893:}

aidjgo 发表于 2021-8-22 00:11

待道友渡劫成功时,再考虑修不修真
页: [1] 2 3 4
查看完整版本: 学破解第140天,《攻防世界reverse练习区流浪者》学习