BUUOJ 第二弹
1.Java逆向解密
工具:jd-gui
拖入jd-gui得到加密函数 public static void Encrypt(char[] arr)
易得出是将用户输入加上‘@’^0x20的值再与KEY比较是否相等。
0x20为16进制,异或时要转换为10进制即32
可写出脚本:
key=[180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65]
a=''
for i in key:
a += chr(i - ord('@')^32)
print (a)
解得 This_is_the_flag_!
2.[BJDCTF2020]BJD hamburger competition
工具:dnSpy,C#在线ide
下载打开压缩包,发现是以unity为框架的小游戏,直接搜索Assembly-CSharp.dll文件,拖入dnspy进行反编译,找到关键类函数 ButtonSpawnFruit .发现关键代码
找个网站解密哈希值得:
接着就是进行MD5加密,跟进函数。
一开始看不太懂,直接就用c#在线ide把这段代码搬上去。
后来查到是调用系统md5加密函数后将字母全部转换为大写,再取前20个字符作为结果返回.得到md5加密后的结果,注意是用flag{}包上,而不是BJDCTF{} 。
3.[HDCTF2019]Maze
工具:Exeinfo pe,upx,IDA
拖入Exeinfo pe,发现是一个加了upx壳的32位程序,走个流程脱壳。
拖入IDA中寻找start函数,跟进main。
发现main没有被ida识别成函数,看样子是被加花指令了。
注意到_mian中有个jnz跳转,前面有个cmp,且跳转地址和与指令相邻的下一地址相同,所以相当于没跳转,把他nop掉。
因为下面call另一个不是地址的地址,所以将反编译代码转换成机器码来看看。
其中
E8
对应指令为 call
58
对应指令为 pop
当ida把E8
识别为call时,58 C7 45 EC
便会被看作地址,此地址不存在便会无法反汇编,因此真正的指令应该是pop,所以把db 0E8h
nop掉后,IDA就可以自动转换出汇编指令了
接着按p将_mian转换为函数再进行反汇编。跟进 dword_408078和 dword_40807C查看到初始状态:dword_408078=7 dword_40807C=0,然后经过14次移动后要使 dword_408078=5 dword_40807C=-4才能解出答案。
结合代码和题目名字Maze可得是一道迷宫题,但是程序好像没有判断迷宫。只要满足两个a和四个s就可以得到Congratulations!
用ida搜索一下字符串,处理一下得到数据
整理一下
*******+**
******* **
**** **
** *****
** **F****
** ****
**********
因为dword_408078=7 dword_40807C=0,所以是+为起点,F为中点,且ad控制左右,ws控制上下。又因为
a --dword_408078
d ++dword_408078
s --dword_40807C
w ++dword_40807C
所以s是上,w是下,a是右,d是左。
若迷宫为正向,则行走路径为wwdddwddwwaaas;若迷宫为反向,则行走路径为ssaaasaassdddw。
先验证一下.
排除正向迷宫,提交一下:flag{ssaaasaassdddw}
4.[GKCTF2020]BabyDriver
工具:IDA
根据题目描述,拖入IDA64中。看来看去没有头绪,搜下字符串,估计是个迷宫题
跟进字符串,发现一共有224个字符,猜测是16*
14 或 14*
16,写个脚本验证一下
list = '****************o.*..*......*..**.**...**.*.*.***.****.**.*.*.***...**....*.*.*****..***.**.*..**.**.***.**.**.**.**.******.**.**.**....***.**.**.*****.***....**...***.**********..***......#****.***************************** '
a = 0
for i in range(len(list)):
a = a + 1
print(list[i], end='')
if a % 16 == 0:
print('\n', end='')
得到
****************
o.*..*......*..*
*.**...**.*.*.**
*.****.**.*.*.**
*...**....*.*.**
***..***.**.*..*
*.**.***.**.**.*
*.**.******.**.*
*.**....***.**.*
*.*****.***....*
*...***.********
**..***......#**
**.*************
****************
跟进a0,并查看交叉引用列表。
跟进sub_140001380进行反编译。得到关键代码,先分析好分析。
v9 = aO[v5];
if ( v9 == '*' )
{
v10 = "failed!\n";
}
else
{
if ( v9 != '#' )
{
LABEL_27:
aO[v5] = 'o'; //更新位置
goto LABEL_28;
}
v10 = "success! flag is flag{md5(input)}\n";
}
dword_1400030E4 = 16;
DbgPrint(v10);
v5 = dword_1400030E4;
goto LABEL_27;
}
}
可知当碰到*时结束。碰到#时,你所输入的值md5加密后就是flag。
再来分析输入判断。由于是驱动文件,所以输入比较的是16进制的键盘码
LABEL_28:
v6 += 6;
if ( !--v7 )
goto LABEL_29;
}
aO[v5] = 46;
v8 = *v6;
if ( *v6 == 0x17 ) // I
{
if ( v5 & 0xFFFFFFF0 ) // 当V5>16,条件成立,因为是向上。所以不用考虑208这倒数第二层的边界
{
v5 -= 16; //上移
goto LABEL_21;
}
v5 += 208; // 当V5<16也就是在第一层,强制碰到最后一层墙壁结束
dword_1400030E4 = v5;
}
if ( v8 == 0x25 ) // K
{
if ( (v5 & 0xFFFFFFF0) != 208 ) // 当V5>16且不等于208,条件成立,因为是向下。所以要考虑208这倒数第二层的边界
{
v5 += 16; //下移
goto LABEL_21;
}
v5 -= 208; // 当v5=208,强制碰到第一层墙壁结束
dword_1400030E4 = v5;
}
if ( v8 == 0x24 ) // J
{
if ( v5 & 15 ) //当v5不等于16的倍数时,即不在每一层的左边界,条件成立
{
--v5; //左移
goto LABEL_21;
}
v5 += 15; //当v5等于16的倍数时,即在某一层的左边界 ,向右移15位,即移动到这一层的右边界
dword_1400030E4 = v5;
}
if ( v8 != 0x26 ) // L
goto LABEL_22;
if ( (v5 & 15) == 15 ) //当v5等于16的倍数-1时,即在某一层的右边界,条件成立
v5 -= 15; //向左移15位,即移动到这一层的左边界
else
++v5; //右移
LABEL_21:
dword_1400030E4 = v5;
所以,根据上面的迷宫和代码的分析:
若为正向迷宫,则行走路径为 LKKKLLKLKKKLLLKKKLLLLLL;
若为反向迷宫,则行走路径为 JIIIJJIJIIIJJJIIIJJJJJJ。
然后进行md5 32位小写加密,进行验证,可得到只有正向才是对的,即flag{403950a6f64f7fc4b655dea696997851} 。