吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 16885|回复: 31
上一主题 下一主题
收起左侧

[CTF] CTF-逆向教程2菜鸟级

  [复制链接]
跳转到指定楼层
楼主
小白670 发表于 2018-5-5 22:32 回帖奖励
本帖最后由 小白670 于 2018-5-6 14:48 编辑

1. Bugku  游戏过关 (找到关键函数进入正确跳转,异或得flag)
运行程序,看到界面跟平常的不太一样,是让我们玩个游戏,大概意思就是要让灯都关上,就是让中间的开关闭合,不想玩直接开始逆向

IDA搜索字符串



发现flag就是程序内的固定字符进行处理然后输出,现在可以直接dump下数据然后写脚本跑出flag,也可以修改程序逻辑,让它直接跳转到正确分支输出flag。但是不管怎么说,这样做都没有达到考察逆向能力的目的,也是这题的缺点,在正规比赛中,是不会出现这种题目的,一般都是最后的输出跟你的输入有关,这样就不能直接修改程序弹出flag,而是要分析程序的校验算法了,这才是真正考察能力的地方。




其实想要修改难度也很简单,你输入的数字要通过这个游戏,最后flag就是你输入的数字,这就倒逼你去分析校验函数,写脚本逆向算法,这也是逆向题的常规做法,找到关键函数,分析对我们输入的检验算法,逆向算法然后跑出正确的输入,而这个正确输入就是我们要的flag!!!这和crackme很类似,就是为了成功注册写个注册机,虽然我看很多人在做160crackme都是爆破,而不是去耐心的分析算法。之所以说这么多,也是因为我入门的时候做题就是这样,以为修改程序逻辑就算做出来了。

接下来,我们来分析一下它的检验算法,通过搜素字符串很容易找到关键函数(而且这个程序字符串还特别多)程序用了两个循环,一个循环不断接受输入,然后进行处理,最后判断是否正确,正确则输出flag,否则接着循环




   接着让我们进去关键函数看一下,里面涉及三个数组,


[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
if ( a1 )
 
{
 
  if ( a1 == 7 )
 
  {
 
    v2 = byte_532E28[7] == 0;
 
    byte_532E28[7] = v2;
 
    v2 = byte_532E27[7] == 0;
 
    byte_532E27[7] = v2;
 
    v2 = byte_532E28[0] == 0;
 
    result = 1;
 
    byte_532E28[0] = byte_532E28[0] == 0;
 
  }
 
  else
 
  {
 
    v2 = byte_532E28[a1] == 0;
 
    byte_532E28[a1] = v2;
 
    v2 = byte_532E27[a1] == 0;
 
    byte_532E27[a1] = v2;
 
    v2 = byte_532E29[a1] == 0;
 
    result = v2;
 
    byte_532E29[a1] = v2;
 
  }
 
}
 
else
 
{
 
  byte_532E28[0] = byte_532E28[0] == 0;
 
  v2 = byte_532E29[0] == 0;
 
  byte_532E29[0] = byte_532E29[0] == 0;
 
  v2 = byte_532E28[7] == 0;
 
  result = 1;
 
  byte_532E28[7] = v2;
 
}
 
return result;



三个数组是连着的,最后只需要byte_532E28数组全为1即可,我上面画了简陋的示意图,大概意思就是这三个数组指向的数据有重复分析可知这个函数就是说打开一盏灯,可能就会关上另外一盏灯,根据它们之间的规律,就可以得出正确的开灯顺序,如果题目设置成正确的开灯顺序就是flag的话,这题会更有意思点。  


检验算法分析到这里,这题我懒得正确的输入了,既然可以爆破那我就省点力气了,用OD搜索字符串,找到输出flag的函数,找到跳转,转到call



往上找到第一个判断的地方,下断点,然后运行时修改ZF标志位,就可以顺利到达call flag的地方




最后flag就自己出来了

     





2. BugKu love(base64加密,明文比较)Ida打开,shift+12查看字符串,找到提示字符串,找到关键函数,F5分析





首先让我们输入字符串,接着对字符串处理,返回值拷贝给一个新的字符串Dest,然后比较字符串,这里的str2以明文e3nifIH9b_C@n@dH存放在程序中。 先看下sub_41127()这个函数,可以看到最后是通过判断eax的值来跳转的,strncmp的返回值是存放在eax中的,如果sub_41127()改变了eax的值,则说明在sub_41127()函数中还有另外的玄机

  


这里分析一下,在函数里面有调用一个函数,add esp8说明是cdecl调用,并且参数有2个,所以上面有两个push是压入参数,剩下的pushpop对应一下,发现eax的值是原来push的值,也就是说这个函数没有改变eax的值,所以这个函数没啥作用




接着分析处理字符串的函数,对密码学有所了解的人可以看出这是base64加密算法,这就很简单了,对e3nifIH9b_C@n@dH先循环递增减去i,然后进行base64解密就得到最后结果了
[C] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
if ( my_str && len )
 
  {
 
    v7 = len / 3;
 
    if ( (signed int)(len / 3) % 3 )
 
      ++v7;
 
    v7 *= 4;
 
    *a3 = v7;
 
    malloc(v7 + 1);
 
    Dst = (void *)sub_411127();
 
    if ( Dst )
 
    {
 
      j_memset(Dst, 0, v7 + 1);
 
      v9 = my_str;
 
      v7 = len;
 
      i = 0;
 
      v5 = 0;
 
      while ( v7 > 0 )
 
      {
 
        byte_41A144[2] = 0;
 
        byte_41A144[1] = 0;
 
        byte_41A144[0] = 0;
 
        for ( i = 0; i < 3 && v7 >= 1; ++i )
 
        {
 
          byte_41A144[i] = *v9;
 
          --v7;
 
          ++v9;
 
        }
 
        if ( !i )
 
          break;
 
        v4 = i;
 
        if ( i == 1 )
 
        {
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[(signed int)(unsigned __int8)byte_41A144[0] >> 2];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[64];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[64];
 
        }
 
        else if ( v4 == 2 )
 
        {
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[(signed int)(unsigned __int8)byte_41A144[0] >> 2];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | 4 * (byte_41A144[1] & 0xF)];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[64];
 
        }
 
        else if ( v4 == 3 )
 
        {
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[(signed int)(unsigned __int8)byte_41A144[0] >> 2];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | 4 * (byte_41A144[1] & 0xF)];
 
          *((_BYTE *)Dst + v5++) = aAbcdefghijklmn[byte_41A144[2] & 0x3F];
 
        }
 
      }
 
      *((_BYTE *)Dst + v5) = 0;
 
    }
 
  }
  


如果不熟悉base64加密函数的话,现逆算法的话还是有点麻烦和费时的,这需要一定的识别算法能力,因为大多数加密算法都是用现成的或者对现有的进行改进利用 要逆base64算法。


首先先了解一下base64加密的原理转换的时候,将三个byte的数据,先后放入一个24bit的缓冲区中,先来的byte占高位。数据不足3byte的话,于缓冲器中剩下的bit0补足。然后,每次取出6(因为26=64)个bit,按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出。不断进行,直到全部输入数据转换完成。 当原数据长度不是3的整数倍时, 如果最后剩下一个输入数据,在编码结果后加2个“=”;如果最后剩下两个输入数据,编码结果后加1个“=”;如果没有剩下任何数据,就什么都不要加,这样才可以保证数据还原的正确性。 这里还有一篇博文有实例介绍:https://blog.csdn.net/u013412497/article/details/51552335   

最后我就写了一个简单的脚本:
[Python] 纯文本查看 复制代码
import base64
 
  
 
str2="e3nifIH9b_C@n@dH"
 
flag=""
 
for i in range(len(str2)):
 
    flag+=chr(ord(str2[i])-i)
 
print base64.b64decode(flag)



Flag{i_l0ve_you}  






3. 南邮ctf WxyVM1(要用脚本处理大量数据)


ida分析,发现关键函数都在main函数中,输入字符串后先调用一个函数,再判断输入的字符串长度是否为24,接着和一个数组比较值,如果相等的话,v4的值就为1,程序puts正确


乍一看不怎么难,首先看下sub_4005B6这个函数,如果没有对输入字符串进行处理的话,那么这题就结束了,哈哈哈哈哈怎么可能这么简单呢,这好歹也是我选的肯定要比入门级难




进去后发现是一个很长的循环,为了好辨识,我把刚才的输入命名为my_str,可以看到这就是根据byte_6010C0数组的值来选择switch的不同分支,然后选择性的和byte_6010C0数组的值进行运算。其实就算法来说并不复杂,但是在处理数据的复杂度上可以说是比一般题目要难。  在这个时候,就需要用到我们idc脚本了,ida集成支持idcpython脚本,这题用idc脚本的话很快就能得到flag





简单介绍下idc脚本,idcc语言很像,在idc中,变量命名叫auto,比如说定义i,在idc中写auto i;

ida有很多内带的函数,我们这题要用到的一个函数叫PatchByte
ida自带的说明如下:
// Change value of a program byte
// If debugger was active then the debugged process memory will be patched too
//      ea    - linear address
//      value - new value of the byte
// Returns: 1 if successful, 0 if notsuccess PatchByte       (long ea,long value);   
// change a byte


这个函数的作用就是改变程序里一个byte的值
Idc脚本里必须声明main函数
static main()
{
//代码
}
Idc的介绍就到这里,下面是我们的脚本:


[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
static main()
 
{
 
auto i;
 
for ( i = 14997; i >= 0; i = i - 3 )
 
  {
 
    auto v0 = Byte(0x6010C0+i);
 
    auto v3 = Byte(0x6010C0+(i + 2));
 
    auto result = v0;
 
    if(v0==1){
 
        result =Byte(0x6010C0+i + 1);
 
        PatchByte(0x601060 + result*4,Byte(0x601060 + result*4)-v3);
 
            }
 
     if(v0==2){
 
         result =Byte(0x6010C0+i + 1);
 
       PatchByte(0x601060 + result*4,Byte(0x601060 + result*4)+v3);
 
            }
 
    if(v0==3){
 
        result =Byte(0x6010C0+i + 1);
 
        PatchByte(0x601060 + result*4,Byte(0x601060 + result*4)^v3);
 
            }
 
   if(v0==4){
 
        result =Byte(0x6010C0+i + 1);
 
        PatchByte(0x601060 + result*4,Byte(0x601060 + result*4)/v3);
 
            }
 
   if(v0==5){
 
         result =Byte(0x6010C0+i + 1);
 
PatchByte(0x601060 + result*4,Byte(0x601060 + result*4)^Byte(0x601060+v3*4));
 
            }
 
    else
 
        continue;
 
  
 
  }
 
  
 
for(i=0;i<24;i++)
 
Message("%c",Byte(0x601060+i*4));//最后输出修改后既逆向得到的flag
 
 
  
 
}





整个idc脚本结构和分析的函数一样,所以我没写注释了,地址在ida可以看到,ida中载入脚本,就能一键出flag




消息的输出是在ida下方的窗口,可以看到flag已经出来了 这题主要是介绍idc脚本的使用,并未涉及到更高深的用法,但是也可以认识到idc对提高逆向效率的实用性



4.实验吧 分道扬镳 (分析可知是在走迷宫,移动顺序既flag)

这个题目挺有意思的,除了验证函数不同,还加了一个混淆的错误验证算法。
同样可以搜索字符串找到关键函数,首先判断长度是否等于22,然后在一个循环里,一位一位判断输入,其中输入只能为107 106 104108,转化为ascii码为kjhl,接下来的判断需要先分析v3v5v6的值  





程序中并没有特意给v3v5v6赋值,所以需要分析变量存储的实际情况,看紫色框是在栈帧中的偏移量,在栈中可以看到,v5v4后面,v6v5后面,所以给v4赋值字符串时,v5v6处也覆盖了字符串file:///C:\Users\ASUS\AppData\Local\Temp\ksohtml\wpsF834.tmp.jpg file:///C:\Users\ASUS\AppData\Local\Temp\ksohtml\wpsF835.tmp.jpg   





知道这些变量的值之后,分析下面四个不同的分支,发现了以下规律
104 - h -  v3--
106 - j -  v3+=8
108 - l -  v3++
107 - k -  v3-=8
其中v3都不能等于42,既*,而等于35#就成功了 再看v4字符串,只有一个#,这看上去不就像是走迷宫么,hl表示左右,kj表示上下


********* *    ** * ** ** * ** ** * #* ** **** **      *********
结合前面的分析,v5的位置就是起始位置,把字符串复制到文档中,每8个一行,得到如下迷宫,起始位置和终点位置找到以后,再根据刚才分析出的方向键,即可得出正确的走法file:///C:\Users\ASUS\AppData\Local\Temp\ksohtml\wpsF836.tmp.jpg



jjjjjlllllkkkkkhhhjjjl


当然了,其实它还有另一条线,真正运行时不走那条线,而是先判断迷宫这条线而且也只是简单的异或,没有迷宫好玩,关键是那个函数算出来的flag还是假的哈哈哈。 file:///C:\Users\ASUS\AppData\Local\Temp\ksohtml\wpsF847.tmp.jpg  






未完待续······

1_11.png (9.14 KB, 下载次数: 4)

1_11.png

000838u7bypy2tpy6m4ibz.jpg (423.51 KB, 下载次数: 4)

000838u7bypy2tpy6m4ibz.jpg

免费评分

参与人数 10吾爱币 +9 热心值 +10 收起 理由
hui99995 + 1 + 1 我很赞同!
gk19110 + 1 + 1 谢谢@Thanks!
静叶流云 + 1 + 1 用心讨论,共获提升!
JoeyAlin + 1 + 1 我很赞同!
2728078153 + 1 + 1 看着眼花!
瑟瑟发抖小菜虾 + 1 我很赞同!
疯狂醉虎 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
阿尔卡伊达 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
hycool248 + 1 + 1 热心回复!
lin_xop + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
zttxjjh 发表于 2018-5-5 23:11
菜鸟前来学习!谢谢!
3#
amazingADC 发表于 2018-5-5 23:27
4#
wulianghao 发表于 2018-5-5 23:54
5#
xlhr123 发表于 2018-5-6 00:09
感谢楼主分享,支持一下!
6#
wulianghao 发表于 2018-5-6 00:40
感谢封箱  !!!! 大大
7#
qqqwww0078 发表于 2018-5-6 02:31
学习一下 学习lz
8#
moke666 发表于 2018-5-6 08:55
感谢楼主,学习了,
9#
0531chuxin 发表于 2018-5-6 09:54
嗯嗯.....挺不错的
10#
kanxue2018 发表于 2018-5-6 10:38
支持一下!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-1 19:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表