小菜鸟一枚 发表于 2020-1-4 15:58

学破解第78天,《攻防世界reverse新手练习区game分析》

本帖最后由 小菜鸟一枚 于 2020-1-4 16:02 编辑

# 学破解第78天,《攻防世界reverse新手练习区game分析》
**前言:
一直对黑客充满了好奇,觉得黑客神秘,强大,无所不能,来论坛两年多了,天天看各位大佬发帖,自己只能做一个伸手党。也看了官方的入门视频教程,奈何自己基础太差,看不懂。自我反思之下,决定从今天(2019年6月17日)开始定下心来,从简单的基础教程开始学习,希望能从照抄照搬,到能独立分析,能独立破解。
不知不觉学习了好几个月,发现自己离了教程什么都不会,不懂算法,不懂编程,随着破解学习的深入,越发的觉得自己没有基础,所以从第71天起,开始接触一门编程语言C++,开启新的学习之路。**

**立帖为证!--------记录学习的点点滴滴**


## 为啥选择它呢?当然是因为楼主是个小菜鸟,这道题简单,就拿它分析分析了!

### 1.下载程序
这是一个exe文件,那我就放心,至少可以在window上执行,可以用OD。

### 2.观察程序
直接将程序运行起来,一堆英文,看不懂,右键复制,丢到有道翻译里面去。

有道翻译如下:
玩一个游戏
n是灯的序列号,m是灯的状态
如果第n盏灯的m是1,它是亮的,如果不是,它是灭的
起初所有的灯都是关着的
现在你可以输入n来改变它的状态
但是你要注意一件事,如果你改变第N盏灯的状态,第(N-1)个和第(N+1)个的状态也会改变
当所有的灯都亮着时,旗子就会出现
现在,输入n
输入n, n (1 - 8)

游戏规则清楚了,但是我显然智商没那么高,玩不来,那就只能老老实实对它使用吾爱的神器OD了。前灯亮,相邻的两个灯同时改变自身状态。

思考:我们能不能点一个亮一个,不改变相邻灯的状态呢?

### 3.使用OD调试
首先查壳,Microsoft Visual C++ ver.14 / Visual Studio 2015 [ Debug:02 ] ,ep区段.text   应该是无壳的C++程序!

  然后将程序丢进OD,停在了01417839 这里,直接右键搜索中文字符串,搜到了一大堆提示信息,我找到程序提示的游戏规则的最后一句input n,n(1-8),找到这一行回车进去,来到了:
```asm
0141F508|.68 7CB54C01   |push 9c91e9fd.014CB57C                  ;input n,n(1-8)\n
0141F50D|.E8 ACB2FFFF   |call 9c91e9fd.0141A7BE
0141F512|.83C4 04       |add esp,0x4
0141F515|.E8 FE9EFFFF   |call 9c91e9fd.01419418
0141F51A|.68 90B54C01   |push 9c91e9fd.014CB590                  ;n=
```
  一路F8单步往下跟,在0131F530 这一个call程序跑起来等待我们输入n,输入完之后继续单步向下走,然后就看到了CLS清屏命令,随后灯的状态就改变了。

**暂时失去了思路**
  再去看看字符串有没有哪些可用的信息,我看到了这个字符串:done!!! the flag is ,大意就是说成功了,flag是
但是我们没有看到这句话出来,我猜测是不是把这句话显示出来就能看到flag呢?

  我回车进去,定位到了这一段代码:
```asm
013EE940/> \55            push ebp
013EE941|.8BEC          mov ebp,esp
013EE943|.81EC 58010000 sub esp,0x158
013EE949|.53            push ebx
013EE94A|.56            push esi
013EE94B|.57            push edi
013EE94C|.8DBD A8FEFFFF lea edi,
013EE952|.B9 56000000   mov ecx,0x56
013EE957|.B8 CCCCCCCC   mov eax,0xCCCCCCCC
013EE95C|.F3:AB         rep stos dword ptr es:
013EE95E|.A1 04204C01   mov eax,dword ptr ds:         ;蛾Am
013EE963|.33C5          xor eax,ebp
013EE965|.8945 FC       mov ,eax
013EE968|.68 F0B04901   push 9c91e9fd.0149B0F0                   ;done!!! the flag is
013EE96D|.E8 4CBEFFFF   call 9c91e9fd.013EA7BE
```
我们现在就要一步一步向上回溯,找到跳转到这里的位置
点击段首,看信息窗口,CTRL+G向上找,依次找到:013EE940《-013E7AB4 《-013EF66C
**我们向上翻翻,看到了CLS,我现在又有一个思路了:能不能在清屏后重新显示菜单的的时候让它调用这个call直接显示flag呢?**
那我就F4到CLS这里,n随便输入一个5
```asm
013EF5B7|> \68 BCB54901   |push 9c91e9fd.0149B5BC                  ;CLS
013EF5BC|.E8 F68BFFFF   |call 9c91e9fd.013E81B7
013EF5C1|.83C4 04       |add esp,0x4
013EF5C4|.E8 8B8AFFFF   |call 9c91e9fd.013E8054
013EF5C9|.B8 01000000   |mov eax,0x1
```
  看这里,第一个call就是调用系统的清屏命令,第二个call回车进去看了下,就是打印菜单,那就说明灯的状态在CLS之前就已经改变了,看来前面是我错过那个了CALL,继续看,后面紧接着就是013EF66C这个call,肯定是被跳转过去了,继续单步,来到这里,看图,这个jnz不能让它跳!

然后后面代码和这一块类似,很容易猜测到是判断其它7个灯是不是亮的,我就不用去一个一个nop了,直接将第一个jnz改成jmp。
**0023F5DB   /E9 8C000000   jmp 9c91e9fd.0023F66C**
复制到可执行文件,保存为1.exe,运行程序后,随便输入一个数字即可得到正确的falg,如图所示!

到这里,我们就成功的使用OD找到了flag!
### 4.使用IDA进行分析
将程序丢进IDA,在左侧的函数栏CTRL+F搜索main函数,F5反汇编一下,看到
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax

main_0();
return result;
}
```
  我们往下跟,看到它调用了main_0函数,点进去,来到了如图所示的地方!

前面都是些打印提示语函数,直接略过,看下面这段反汇编代码,我给它加了注释

```c
while ( 1 )//死循环,让游戏不会结束
{
    while ( 1 )
    {
      sub_45A7BE("input n,n(1-8)\n");
      sub_459418();\\1.△ 2.○ 3.◇ 4.□ 5.☆ 6.▽ 7.( ̄▽ ̄)/ 8.(;°Д°) 0.restart
      sub_45A7BE("n=");
      sub_4596D4("%d", &v1);\\接收输入的数字
      sub_45A7BE("\n");
      if ( v1 >= 0 && v1 <= 8 )//判断输入的值是否在1-8的范围内,否则重新开始游戏
      break;
      sub_45A7BE("sorry,n error,try again\n");
    }
    if ( v1 )//v1也不能等于0
    {
      sub_4576D6(v1 - 1);//
    }
    else//否则将所有灯关掉,m全部设为0
    {
      for ( i = 0; i < 8; ++i )
      {
      if ( (unsigned int)i >= 9 )
          j____report_rangecheckfailure();
      byte_532E28 = 0;
      }
    }
    j__system("CLS");
    sub_458054();//改变灯的状态
    if ( byte_532E28 == 1
      && byte_532E28 == 1
      && byte_532E28 == 1
      && byte_532E28 == 1
      && byte_532E28 == 1
      && byte_532E28 == 1
      && byte_532E28 == 1
      && byte_532E28 == 1 )
    {
      sub_457AB4();//如果灯全部亮,输出flag
    }
}
```
加上注释后,是不是一目了然呢,我们看到上面的代码瞬间涌现出两种思路:
1.输入一个0,在OD中找到'00FDF59D      C681 282E0B01>mov byte ptr ds:,0x0'这一行,然后改为0x1,我们保存出去,输入0,即可得到flag。
2.跟进sub_457AB4();这个函数,看flag是怎么算出来的。

### 5.flag算法还原与总结
前面已经通过IDA和OD分别获取到了flag,我们进去看一下sub_457AB4()函数的代码,如何运算出我们的flag的,F5反编译一下
核心算法在这:
```c
for ( i = 0; i < 56; ++i )
{
    *(&v2 + i) ^= *(&v59 + i);
    *(&v2 + i) ^= 0x13u;
}
```
  前面都是定义的一堆变量v59-v115为一段,v2-v58为一段,看for循环*(&v2 + i) ^= *(&v59 + i);,可以看做a^b,然后a^0x13u得到解密后的字符。

**整理一下,最终改写成C语言代码如下:**
```c
int sub_45E940()
{
      int i;

      int arr1[]={18,64,98,5,2,4,6,3,6,48,49,65,32,12,48,65,31,78,62,32,49,32,1,57,96,3,21,9,4,62,3,5,4,1,2,3,44,65,78,32,16,97,54,16,44,52,32,64,89,45,32,65,15,34,18,16,0};
      int arr2[]={123,32,18,98,119,108,65,41,124,80,125,38,124,111,74,49,83,108,94,108,84,6,96,83,44,121,104,110,32,95,117,101,99,123,127,119,96,48,107,71,92,29,81,107,90,85,64,12,43,76,86,13,114,1,117,126,0};

      for(i = 0;i < 56; i++)
      {
                arr1 ^= arr2;
                arr1 ^= 0x13u;
                printf("%c",arr1);
      }
      return 1;
}

int main(int argc, char* argv[])
{
      printf("Hello World!\n");
      sub_45E940();
      getchar();
      return 0;
}
```

**总结:楼主是个小菜鸟,离了教程啥都不会!**

PS:IDA不会用,这一部分的分析参考了其他大佬的分析!

storm 发表于 2020-1-4 20:06

楼主学的哪套教程 能学这么长时间

小菜鸟一枚 发表于 2020-1-4 20:10

本帖最后由 小菜鸟一枚 于 2020-1-4 22:05 编辑

storm 发表于 2020-1-4 20:06
楼主学的哪套教程 能学这么长时间
当然是论坛的零基础入门教程呀:lol,这是我全部的学习记录https://www.52pojie.cn/thread-1067012-1-1.html,你也可以参考下

D.c 发表于 2020-1-4 16:15

dhz0105 发表于 2020-1-4 16:39

能学了78天,牛逼!我是没耐心看教程,收藏从未停止。

xiaoxiaoshitou 发表于 2020-1-4 16:52

都是大神啊,看都看不懂,佩服

俺是大绅士 发表于 2020-1-4 17:16

感谢分享,来看看

速度富贵浮云 发表于 2020-1-4 17:20

已经很不错了

夏南离 发表于 2020-1-4 17:30

30900 发表于 2020-1-4 17:48

路过,真的是精彩绝伦,感谢大神分享,学习了!
页: [1] 2 3 4
查看完整版本: 学破解第78天,《攻防世界reverse新手练习区game分析》