BUUOJ 第三弹

1.findit

工具:JADX,在线凯撒密码转换

拖入JADX,定位到MainActivity类

跳过安卓支持函数,定位到onCreate函数,部分代码如下。

观察到有两个字符串,写个脚本跑一下,得到ThisIsTheFlagHome和pvkq{m164675262033l4m49lnp7p9mnk28k75}

当key为10时解得:flag{c164675262033b4c49bdf7f9cda28a75},验证

2.reverse_3

工具:DIE,IDA32

走流程

拖入IDA32中,跟进到_main_0,定位到关键代码。

就是对输入数据进行操作后加上本身所在的位置,再和Str2做对比。

那么我们反着推,先把原本的Dest解出来:

list = "e3nifIH9b_C@n@dH"
for i in range(len(list)):
    print (chr(ord(list[i]) - i), end ='')
# list = e2lfbDB2ZV95b3V9

然后跟进sub_4110BE,看到了类似base编码的位移代码。

查个字符串,是标准的base64码表。交叉引用后,发现刚好是上面的aAbcdefghijklmn变量,找个在线网址解一下e2lfbDB2ZV95b3V9,得到{i_l0ve_you},提交验证。

3.CrackRTF

工具:DIE,IDA32,ResourceHacker/7-zip

走流程

拖入IDA32中,跟进到main_0,定位到部分关键代码。

这里主要是对输入的转换和判断,必须要6个字符,且因为有atoi函数和必须大于100000,所以输入的必须全部是数字,接着就是接上@DBApp这个字符串并送入sub_40100A进行加密处理,再与"6E32D0943418C2C33385BC35A1470250DD8923A9"进行对比

跟进加密函数sub_40100A,看到关键的加密函数CryptCreateHash,查询标识符0x8004u在ALG_ID中对应的是SHA1加密。

根据上述信息,可以写出爆破脚本,要注意大小写。

import hashlib
part2 = "@DBApp"
for part1 in range(100000,999999):
    pasd = str(part1) + part2
    tmp = hashlib.sha1(pasd.encode("utf-8"))
    if tmp.hexdigest().upper() == "6E32D0943418C2C33385BC35A1470250DD8923A9":
        print (str(part1))
        break

得到123321,验证一下

继续分析下列代码,同样的和第一个密码的处理非常类似,但是只知道是输入6个字符,没有任何提示,估计无法爆破。接着就是将处理后的第一个密码和第二个密码拼接,送入sub_401019进行加密处理后

跟进加密函数sub_401019,和上面的sub_40100A非常像

就是改了个标识符,查询后得知0x8003对应的是MD5加密。但是网上的md5解密查询都没有结果。所以暂且跳过。

注意到函数sub_401019下面有个sub_40100F,而且是对未加密的string进行处理,跟进。

百度一番,发现是从程序自身的AAA文件夹中读取101文件数据并送入sub_401005与string进行处理。

一开始不知道文件怎么获取,但是我瞎猫碰见死耗子,右键打开压缩包,成功的得到101这个文件。取出得到数据。

后来百度到了一款查看程序资源文件的工具ResourceHacker。

跟进sub_401005。

根据sub_40100F中的代码sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite)可知,a2是101文件的起始位置,a3是文件字节数大小,则此函数的作用就是将string与文件以18个字符(用户两次输入共12个,程序本身加了6个)为一组进行循环异或。

而且,string与101文件异或后写入的是rtf文件。要想得到正常的rtf,那么标识头也要是rtf文件的标识头,即:7B 5C 72 74 66 31 7D

又因为第二个密码是6个字符,所以只要用前6个rtf的16进制的头部数据和前6个101文件的16进制的头部数据,即7B 5C 72 74 66 3105 7D 41 15 26 01异或即可得到第二个密码。

脚本如下:

rtf = [0x7B,0x5C,0x72,0x74,0x66,0x31]
fil = [0x05,0x7D,0x41,0x15,0x26,0x01]
flag = ""
for i in range(0,6):
    flag += chr(rtf[i]^fil[i])
print (flag)

得到~!3a@0。

运行程序,依次输入密码,即可得到带有flag的RTF文件:Flag{N0_M0re_Free_Bugs},记得要将Flag改成flag。

验证:

4.[GXYCTF2019]luck_guy

工具:DIE,IDA64

走流程

拖入IDA64,进入主函数main

获取用户输入V4,并传入patch_me函数,跟进。

判断了一下用户输入是否为偶数,继续跟进get_flag函数。

得到关键代码:

for ( i = 0; i <= 4; ++i )
  {
    switch ( rand() % 200 )
    {
      case 1:
        puts("OK, it's flag:");
        memset(&s, 0, 0x28uLL);
        strcat((char *)&s, f1); // f1 = "GXY{do_not_"
        strcat((char *)&s, &f2);
        printf("%s", &s);
        break;
      case 2:
        printf("Solar not like you");
        break;
      case 3:
        printf("Solar want a girlfriend");
        break;
      case 4:
        v6 = 0;
        s = 0x7F666F6067756369LL;
        strcat(&f2, (const char *)&s);
        break;
      case 5:
        for ( j = 0; j <= 7; ++j )
        {
          if ( j % 2 == 1 )
            v1 = *(&f2 + j) - 2;
          else
            v1 = *(&f2 + j) - 1;
          *(&f2 + j) = v1;
        }
        break;
      default:
        puts("emmm,you can't find flag 23333");
        break;
    }
  }

也就case 1 4 5 是有用的。而且只执行4次,根据观察,1为最后,4为最开始,5有可能一次或者两次。

脚本如下:

f1 = "GXY{do_not_"
f2 = [0x7F, 0x66, 0x6F, 0x60, 0x67, 0x75, 0x63, 0x69]
for i in range(0,2):
    tmp = ""
    for i in range(len(f2)):
        if i%2 == 1:
            f2[i] = f2[i] - 2
        else:
            f2[i] = f2[i] - 1
        tmp += chr(f2[i])
    print (f1 + tmp)
#GXY{do_not_~dn^fsbg
#GXY{do_not_}bmeqae    

但是,得到的两个flag均不是答案。

转入汇编,发现程序是把s倒过来复制到f2的

后来了解到是大端序和小端序的问题,且前面对s的声明为qword ptr(8字节的指针地址),说明它是小端序,要倒过来。

所以改写脚本:

f1 = "GXY{do_not_"
f2 = [0x7F, 0x66, 0x6F, 0x60, 0x67, 0x75, 0x63, 0x69][::-1]
for i in range(0,2):
    tmp = ""
    for i in range(len(f2)):
        if i%2 == 1:
            f2[i] = f2[i] - 2
        else:
            f2[i] = f2[i] - 1
        tmp += chr(f2[i])
    print (f1 + tmp)

当只走一次即 4->5->1 时得到flag:GXY{do_not_hate_me}

用flag{}替换后提交验证

原文地址:https://www.cnblogs.com/b1ank/p/13475674.html