[SWPU2019]ReverseMe
找到关键函数,但是很长,也只能分析出一些零碎的东西,看 wp 是通过动调来缕清程序的逻辑的。
一个是用ida,还有一个OD都试试吧
还看到一个大佬是用的 ponce 解的,这个先放放。
调的太难受了 0.0
还是要学会,他有个内存断点
[WUSTCTF2020]funnyre
花指令的去除
有很多标红的地址,按 G 跳转
最简单方法就是使用 angr
import angr
import claripy
p=angr.Project('./attachment',load_options={"auto_load_libs":False})
f=p.factory
state=f.entry_state(addr=0x400605)
flag=claripy.BVS('flag',32*8) # request 32 part
state.memory.store(0x603055+0x300+5,flag) # don't have scanf,so set string into memory directly
state.regs.rdx=0x603055+0x300
state.regs.rdi=0x603055+0x300+5 # set 寄存器
sm=f.simulation_manger(state)
print('ready')
sm.explore(find=0x401dae)
if sm.found:
print("[+] found!")
x=sm.found[0].solver.eval(flag,cast_to=bytes)
print(x)
但也是不好写,有时候还跑不出来
然后可以用暴力破解,tql
dt = [0xd9, 0x2c, 0x27, 0xd6, 0xd8, 0x2a, 0xda, 0x2d, 0xd7, 0x2c, 0xdc, 0xe1, 0xdb, 0x2c, 0xd9, 0xdd, 0x27, 0x2d, 0x2a, 0xdc, 0xdb, 0x2c, 0xe1, 0x29, 0xda, 0xda, 0x2c, 0xda, 0x2a, 0xd9, 0x29, 0x2a]
def kaisa(xx, kk):
return [(x+kk) & 0xFF for x in xx]
def xor(xx, kk):
return [x ^ kk for x in xx]
def check(xx):
for x in xx:
if x < ord('0') or (x > ord('9') and x < ord('a')) or x > ord('f'):
return False
return True
if __name__ == '__main__':
for k1 in range(0x100):
tt = kaisa(dt, k1)
for k2 in range(0x100):
tt2 = xor(tt, k2)
if check(tt2):
print(bytes(tt2))
print(k1, k2)
也还有一种最稳妥的办法:
就是 copy 代码
还有idapython提取指令的,额,算了。
[GKCTF 2021]QQQQT
有导入表和导出表改变好像
Enigma Virtual Box打包的QT程序,先解一下包
解码,得到一个名为%DEFAULT FOLDER%的文件
里面的untitled2.exe为解码后的文件
不是外面那个 unpacked
然后看出 base58
辗转相除法,每次除以58,商作下次被除数,余数去查表
位数:log2(58)(每一个字母代表的信息量是log2(58))
[CISCN2018]2ex
__MACOSX 是 Mac Os 系统应用解压所带
mips-32架构
有专门的工具好像,不过不好弄应该
这就是个 base64 变表
[ACTF新生赛2020]fungame
很像 flag 啊,但是提交显示错误,运行,有一会sleep感觉,但是没有回显
难道是 hook ?
main上面函数翻了一下,又发现一小部分
猜测是要其直接打印 flag ,但不知道怎么联系起来
这还有一个
唉 0.0
这是之前遇到的那个栈溢出的题
也大致了解了一点
[RoarCTF2019]polyre
都在switch-case里面
前面一堆if,while啊
关于平坦化和虚假控制流程的题目
之前听过还没学呢
符号执行去除控制流平坦化
看废了
python3 deflat.py attachment 0x400620 #python版本+脚本名+文件名+起始地址
需要有 angr 库,跑的有几分钟
但可能还是有一点 while if ,有些是永真或者永假
需要自己再写脚本 patch 一下
这个表达式实际上是永远为假的,它因为dword_603058这个全局变量是在bss段上的,bss段上为未初始化的全局变量,所以它就是0。而且查一下它的交叉引用表也会发现与它相关的指令都是作为源操作数而非目的操作数,而且也没有取它地址做某些操作,在此基础之上我们基本可以认为它就是不变的。
还有永真
if ( dword_603058 < 10 || ((((_BYTE)dword_603054 - 1) * (_BYTE)dword_603054) & 1) == 0 )
break;
也删了
但是 ida python 一些函数还是报错
st = 0x401121
end = 0x402144
def patch_nop(start, end):
for i in range(start, end):
patch_byte(i, 0x90) # 修改指令地址处 0x90 是最简单的1字节nop
def next_instr(addr):
return addr+ItemSize(addr) # ItemSize获取指令或数据长度,这个函数的作用就是去往下一条指令
addr = st
while(addr < end):
next = next_instr(addr)
if "dword_603058" in GetDisasm(addr): # GetDisasm(addr)得到addr的反汇编语句
while(True):
addr = next
next = next_instr(addr)
if "jnz" in GetDisasm(addr):
dest = GetOperandValue(addr, 0) # 得到操作数,就是指令后的数
patch_byte(addr, 0xe9)
patch_byte(addr+5, 0x90)
offset = dest - (addr + 5)
patch_dword(addr + 1, offset)
print("patch bcf: 0x%x" % addr)
addr = next
break
else:
addr = next
只会就是一个新的加密
实际上就是CRC32算法—输入一组长度48的字符串,每8个字节分为1组,共6组。对每一组取首位,判断正负。正值,左移一位;负值,左移一位,再异或0xB0004B7679FA26B3。重复判断操作64次,得到查表法所用的表。
__int64 __fastcall main(int a1, char **a2, char **a3)
{
signed __int64 v4; // [rsp+1E0h] [rbp-110h]
int i; // [rsp+1E8h] [rbp-108h]
int v6; // [rsp+1ECh] [rbp-104h]
int v7; // [rsp+1ECh] [rbp-104h]
char s1[48]; // [rsp+1F0h] [rbp-100h] BYREF
char s[60]; // [rsp+220h] [rbp-D0h] BYREF
unsigned int v10; // [rsp+25Ch] [rbp-94h]
char *v11; // [rsp+260h] [rbp-90h]
int v12; // [rsp+26Ch] [rbp-84h]
bool v13; // [rsp+272h] [rbp-7Eh]
unsigned __int8 v14; // [rsp+273h] [rbp-7Dh]
int v15; // [rsp+274h] [rbp-7Ch]
char *v16; // [rsp+278h] [rbp-78h]
int v17; // [rsp+284h] [rbp-6Ch]
int v18; // [rsp+288h] [rbp-68h]
bool v19; // [rsp+28Fh] [rbp-61h]
char *v20; // [rsp+290h] [rbp-60h]
int v21; // [rsp+298h] [rbp-58h]
bool v22; // [rsp+29Fh] [rbp-51h]
__int64 v23; // [rsp+2A0h] [rbp-50h]
bool v24; // [rsp+2AFh] [rbp-41h]
__int64 v25; // [rsp+2B0h] [rbp-40h]
__int64 v26; // [rsp+2B8h] [rbp-38h]
__int64 v27; // [rsp+2C0h] [rbp-30h]
__int64 v28; // [rsp+2C8h] [rbp-28h]
int v29; // [rsp+2D0h] [rbp-20h]
int v30; // [rsp+2D4h] [rbp-1Ch]
char *v31; // [rsp+2D8h] [rbp-18h]
int v32; // [rsp+2E0h] [rbp-10h]
int v33; // [rsp+2E4h] [rbp-Ch]
bool v34; // [rsp+2EBh] [rbp-5h]
v10 = 0;
memset(s, 0, 0x30uLL);
memset(s1, 0, sizeof(s1));
printf("Input:");
v11 = s;
__isoc99_scanf("%s", v11);
v6 = 0;
//输入部分,碰到\n截止
while ( 1 )
{
v12 = v6;
v13 = v12 < 64;
if ( !v13 )
break;
v14 = s[v6];
v15 = v14;
if ( v15 == 10 )
{
v16 = &s[v6];
*v16 = 0;
break;
}
v17 = v6 + 1;
v6 = v17;
}
for ( i = 0; ; ++i )
{
v18 = i;
v19 = v18 < 6;//这里可以看出只有6组
if ( !v19 )
break;
v20 = s;
v4 = *(_QWORD *)&v20[8 * i];//转为了QWORD,每组八个字节
v7 = 0;
while ( 1 )
{
v21 = v7;
v22 = v21 < 64;
if ( !v22 )
break;
v23 = v4;
v24 = v4 < 0;
if ( v4 >= 0 )
{
v27 = v4;
v28 = 2 * v27;
v4 = v28;
}
else
{
v25 = 2 * v4;
v26 = v25;
v4 = v26 ^ 0xB0004B7679FA26B3LL;
}
v29 = v7;
v7 = v29 + 1;
}
v30 = 8 * i;
v31 = &s1[8 * i];
*(_QWORD *)v31 = v4;
v32 = i + 1;
}
v33 = memcmp(s1, &unk_402170, 0x30uLL);
v34 = v33 != 0;
if ( v34 )
puts("Wrong!");
else
puts("Correct!");
return v10;
}