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 31
和 05 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{}替换后提交验证