暑假第五周
re5-packed-movement
先脱壳
ida打开全是mov 指令,movfuscator混淆
法一
shift+F12 搜索字符串找到’Wrong Flag!',
交叉引用可以看到有70多处引用,可能就是逐字符比较
随便点一个看看,从每一个’Wrong Flag!'处往上翻可以看到都有一个
mov R2 xxh的指令
直接搜索R2寄存器内容
Alt+B搜索C7 05 68 20 06 08即mov R2对应的16进制编码
得到flagALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
法二
使用idac脚本。
shift+F2打开脚本界面
运行得到flag
flag
ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
reverse-for-the-holy-grail-350
直接看主函数,有点多重点锁定这几个语句
v4 = stringmod(v9),v4小于0就输出auuuuuuugh,大于0才输出tuctf{}
应该就是v4>0时输出flag,跟进一下stringmod
stringmod有三个部分先看第一个
v4要大于0所以if 要不执行,那么v3就要是3的倍数,并且
v12读取的字符必须等于firstchar[v3 / 3]
第二部分是一个异或
第三部分
查看一下thirdchar和masterArray,有点乱不好提取
用idapython导出一下
1 | from idc_bc695 import * |
前三行时对应16进制结果,后三行则是10进制
exp
1 | firstchar = [0x41, 0x69, 0x6e, 0x45, 0x6f, 0x61] |
flag
tuctf{AfricanOrEuropean?}
[XCTF]reverse_box
题目描述
挑战描述
$ ./reverse_box ${FLAG}
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
flag格式:TWCTF{}
分析
主函数很短,简单分析如下
运行一下可以发现,输入相同的flag输出不同,说明v4是动态变化的
跟进sub_804858D函数,可以看到是根据时间生成随机数种子v1
再生成随机数,构建出v4
法一
通过查看汇编代码,知道随机生成的范围不超过0xff,那这个构建v4的函数应该可以模拟出来。现在需要知道的就是__ROR1__的作用
这是一个IDA内置定义的函数,通过查看ida/plugins/defs.h查看__ROR1__定义
可以看到这是一个调用__ROL__的函数,查找__ROL__
有两个参数:(value, int count)
第一个参数为左移的数,第二个参数为左移的位数。
如果第二个参数值为负数,则实际上为循环右移 -count位。
该函数的实现细节为:
先得到value的位数,然后count对位数取模。
如果count值大于0,则先右移-count取模的结果,然后在左移取模的结果,得到的两个数相或,即为循环左移的结果。
如果count值小于0,先左移在右移即可。
举例来说: value = 0110, count = 6
value为4位数, 6 % 4 = 2,
0110先右移4-2=2位,得到0001,然后在左移2位,得到1000,0001 | 1000结果为1001,即循环左移结果为1001。
其实简单理解就是把所有位都向左移。最高位复制到进位标志位和最低位。
知道了__ROR1__的作用,接下来根据提示输入flag会输出
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
而flag格式为TWCTF{}对应的ASCII码是84,87,67,84,70,123,125
可以推出v4[84]=0x95,v4[87]=0xee,v4[67]=0xaf,v4[84]=0x95,v4[70]=0xef,v4[123]=0x94,v4[125]=0x4a,这就是我们模拟爆破出v4的依据
最后根据表反推出flag
exp
1 |
|
1 | list = [ |
法二
参考reverse_box gdb调试
生成随机数后第一个断点
此时rand()随机数生成函数刚生成一个随机数与0xFF后,从eax寄存器取出存放在[ebp+var_C]
查看栈中var_C的地址相对于栈顶是-0000000C所以当时加载到栈中的地址就是ebp-0xc
第二个断点
正是printf函数开始输入的位置,这时候我们判断它输入的第一个16进制值是不是0x95,如果是那么我们就认为找到了正确的解;然后输出找到解的随机值和v4数组的所有值,查看起始地址从esp+0x1c开始的256个16进制字节的内容,在上图的汇编中数组地址是esp+eax+1Ch,但是当输出数组的第一个值时i为0即eax为0,所以此时数组起始地址esp+eax+1Ch=esp+1Ch,之所以输出256字节就够了,实际上也许用不上256字节,因为数组的下标是我们输入的字符的ASCII码,又我们输入的字符只能是可打印的字符,所以范围就是0-255中哪些可打印的字符就行了,这里我们输出完整的256字节是没有具体去区分可打印字符了,因为也不会影响到结果。
exp
采用gdb调试
1 | 设置从0到255的随机值爆破 |
flag
TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}
[SUCTF2019]hardCPP
ida打开注意到流程图比较不一样,是OLLVM混淆控制流平坦化
用脚本deflat.py去控制流平坦化,需要用到angr,版本为8.19.4.5
pip install angr==8.19.4.5
python3 deflat.py hardCpp 0x4007E0
得到去控制流平坦化之后的主函数如下
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
先给了一个提示 md5 值
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");
通过查找得到是#
之后有一对冗余代码,对加密不影响
重点看一下
点进去看看每个操作operator
main::$_0::operator()
直接返回第二个参数
1 | char __fastcall main::$_0::operator()(__int64 a1, char a2) |
main::$_1::operator()
同样是直接返回第二个参数
1 | char __fastcall main::$_1::operator()(__int64 a1, char a2) |
main::$_1::operator() const(char)::{lambda(int)#1}::operator()
返回的是a1%a2
1 | __int64 __fastcall main::$_1::operator() const(char)::{lambda(int)#1}::operator()(char *a1, int a2) |
main::$_0::operator() const(char)::{lambda(char)#1}::operator()
冗余代码挺多,关键就5句
v15 = *(&v5-16) + *v5 = a1 + a2
main::$_2::operator()
其实就是返回第二个参数
main::$_2::operator() const(char)::{lambda(char)#1}::operator()
返回a2 ^ a1
1 | __int64 __fastcall main::$_2::operator() const(char)::{lambda(char)#1}::operator()(_BYTE *a1, char a2) |
main::$_3::operator()
返回第二个参数
main::$_3::operator() const(char)::{lambda(char)#1}::operator()
返回a2 * a1
1 | __int64 __fastcall main::$_3::operator() const(char)::{lambda(char)#1}::operator()(char *a1, char a2) |
最后整理一下
1 | v18 = v21 ^ s[v19]; |
其中v21是time(0LL)即为0
s是输入的字符串
最后v18 = ((0 ^ input[i]) + (input[i-1] % 7)) ^ ((18 ^ input[i-1]) * 3 + 2)
最后判断v18是否等于enc
exp
1 | enc = [0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C, 0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5] |
flag
flag{mY-CurR1ed_Fns}
secret-galaxy-300
先运行看看
显示了五个星系的信息,用IDA打开
主函数是调用填充函数和打印函数
打印函数里面没什么内容,那我们就关注一下填充函数
跟踪一下galaxy_name,总共有6个星系名字
而刚刚运行结果只有前五个,最后一个DARK SECRET GALAXY会不坏就藏有flag信息呢
交叉引用看看,跟踪到一个__libc_csu_gala函数有点像flag的生成函数
应该是截取不同星系名字凭借而成一个byte_40DAXX这个串
手动拼接一下或者在return result处下断点
动态调试F9后跟踪一下byte串
按a将数据转换成字符串,得到flag
flag
aliens_are_around_us
[HCTF2018]seven
函数不多,直接定位到关键函数
1 | __int64 __fastcall sub_1400012F0(__int64 a1, __int64 a2) |
提取一下迷宫手动走一下得到flag
1 | **************** |
flag
hctf{ddddddddddddddssaasasasasasasasasas}
[buu]Youngter-drive
先脱UPX壳,然后看主函数,有多个线程
sub_4110FF里是读入source
先看第一个线程hObject,它执行了startAddress
继续查看 sub_41112C 函数
如果不是大写字母或者小写字母就正常退出
如果是小写字母,则对off_418000的小写字母进行-38的加密操作
反之如果是大写字母,则对off_418000的大写字母进行-96的加密操作.
off_418000如下
看一下第二个线程sub_41119F
和第一个线程一样都调用了dword_418008看了一下是0x1D
两个线程执行一次都会把dword_418008减一
两个线程交替运行,由于sub_41119F是直接让dword_418008减一
意味着只有一半的dword_418008会传入sub_41112C
最后回到主函数有个 sub_411190,点开是比较flag
off_418004如下
已知第一个传入的dword_418008是0x1D(29)是奇数
那么只有奇数位会进行加密,偶数为应该就是off_418004的内容
解出来29位提交上去是错的,
仔细观察可以发现最后的sub_411880只比较了0~28位这29个字符
而根据分析主函数判断dword_418008!=-1,说明是从29~0总共应该有30位
没有更多信息去知道最后一位是什么,按理来说应该就是多解了
但是好像只有填充E才会是对的。
exp
1 | enc ='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm' |
flag
ThisisthreadofwindowshahaIsESE