quite_easy
考点是IAT hook,主函数调试没有结果
动态调试过这个反调试函数的时候失败了,交叉引用反调试检测函数能发现还有一个TLS回调函数,跟进TLS看看
1.TLS函数分析
(1)前面的Check反调试要过掉,把jz修改为jmp(因为后面多次调用反调试,所以建议直接改指令,改标识符比较麻烦)
sub_2E1195()和sub_2E14A6()
两个函数都有爆红
sub_2E1195()
这个主要也是反调试,出现了爆红,(之前尝试nop掉,但是报了异常,索性直接不管了,只是反调试,F7跟进动调过了就行
(2)
第二个函数跟进查看,这是主要的加密函数
如果需要转成函数,F5先是伪代码的话,当程序走过爆红的地方可以把之前爆红的地方nop,再按p创建函数,就可以了,如下
下面就能看到大概逻辑
这里是加密函数(也可以通过Shift+F12在字符串窗口找到可能的字符交叉引用找到
进入函数:主要思路就是 根据字符串长度来生成伪随机数,输入的前16个字节和伪随机数异或,后16个字节和原始前16个字节异或,最后0-32字节减去假的flag
#include <stdio.h>
#include <stdlib.h>
int main()
{
unsigned char random[16];
srand(121); // 随机数种子是字符串长度+89
for (int i = 0; i < 16; i++)
{
char v3 = rand();
random[i] = v3;
}
unsigned char Str2[] = // 密文
{
128, 211, 111, 255, 21, 3, 152, 140, 180, 91,
150, 192, 89, 172, 24, 223, 45, 206, 63, 251,
196, 237, 216, 210, 168, 45, 248, 35, 159, 34,
37, 206};
char flag[] = "flag{ed1d665e6516a37ab09f0b7a40}";
for (int i = 0; i < 32; i++) // 加回flag
{
Str2[i] += flag[i];
}
for (int j = 0; j < 16; j++) // 前16个字节异或
{
Str2[j] ^= random[j];
}
for (int i = 16; i < 32; i++) // 后16个字节异或
{
Str2[i] ^= Str2[i - 16];
}
for (int i = 0; i < 32; i++)
printf("%c", Str2[i]);
return 0;
}
so_easy
1.题目分析
(1)是一个apk文件
(2)soEasy函数,汪学长说是CRC64的考点
2.CRC64校验
解密脚本
secret = [0x540A95F0C1BA81AE, 0xF8844E52E24A0314, 0x09FD988F98143EC9, 0x3FC00F01B405AD5E]
key = 0x71234EA7D92996F5#异或的数值
flag = ""
# 产生CRC32查表法所用的表
for s in secret:
for i in range(255):#这个是轮数
sign = s & 1 # 数乘以二,必为偶数,再异或上奇数,得奇数 所以最后一位为1的数还原前为负。
if sign == 1:
s ^= key
s //= 2
# 防止负值除2,溢出为正值
if sign == 1:
s |= 0x8000000000000000 # 让数回到负数
j = 0
while j < 8:
flag += chr(s & 0xFF) # &0xff可以将高的24位置为0,低8位保持原样。
s >>= 8
j += 1
print(flag)
3.解密
加密的逻辑不是很难,两种思路:1.爆破 2.CRC64解密脚本(直接秒了...
1.爆破
用z3去爆破
附上z3的几种类型:
Int型:
整型,简单运算都可以用这个,用运算符<.<=,==,>=,>等,比较
x=Int('a')
y=Int('b')
solve(x>2,y<10,x+y*2==7)
#[b = 0, a = 7]
RealVal型:
小数形式,使用的很多
x=Real('x')
y=Real('y')
solve(x**2+y**2==3,x**3==2)
set_optiuon(precision=30)# 设置结果小数点后位数表示
BitVec类型:
现代CPU和主流编程语言使用固定大小的位向量进行算术运算,机器算法在z3py中可以使用Bit-Vecxtors来表示
实现无符号和有符号的二补数算术的精确表示,只有这个类型才可以使用异或等位运算
x=BitVec('x',16)#x是名称,16是位数
y=BitVec('y',16)
print(x+2)
print(x+2).sexpr()
print(simplify(x+y-1))
a=BitVecVal(-1,16)#前面是数值,后面是位数
b=BitVecVal(65535,16)
print(simplify(a==b))
a=BitVecVal(-1,32)
b=BitVecVal(65535,32)
print(simplify(a==b))
from z3 import *
enc = [0x540A95F0C1BA81AE, 0xF8844E52E24A0314, 0x09FD988F98143EC9, 0x3FC00F01B405AD5E]
xor_data = BitVecVal(0x71234EA7D92996F5, 64)
for i in range(len(enc)):
v10 = BitVec('v10', 64)
count = 255
s = Solver()
for _ in range(count, 0, -5):
v12 = (2 * v10) ^ xor_data
v12 = If(v10 >= 0, 2 * v10, v12)
v13 = (2 * v12) ^ xor_data
v13 = If(v12 >= 0, 2 * v12, v13)
v14 = (2 * v13) ^ xor_data
v14 = If(v13 >= 0, 2 * v13, v14)
v15 = (2 * v14) ^ xor_data
v15 = If(v14 >= 0, 2 * v14, v15)
v10 = (2 * v15) ^ xor_data
v10 = If(v15 >= 0, 2 * v15, v10)
s.add(v10 == enc[i])
if s.check() == sat:
print(s.model())
else:
print("No find")
出来的结果转成十六进制,再转成字符串倒序即可
2.直接逆
用CRC64的解密脚本写
secret = [0x540A95F0C1BA81AE, 0xF8844E52E24A0314, 0x09FD988F98143EC9, 0x3FC00F01B405AD5E]
key = 0x71234EA7D92996F5#异或的数值
flag = ""
# 产生CRC32查表法所用的表
for s in secret:
for i in range(255):#这个是轮数
sign = s & 1 # 数乘以二,必为偶数,再异或上奇数,得奇数 所以最后一位为1的数还原前为负。
if sign == 1:
s ^= key
s //= 2
# 防止负值除2,溢出为正值
if sign == 1:
s |= 0x8000000000000000 # 让数回到负数
j = 0
while j < 8:
flag += chr(s & 0xFF) # &0xff可以将高的24位置为0,低8位保持原样。
s >>= 8
j += 1
print(flag)
WKCTF{2366064af80f669c2cb9519ab}
BUU上有同样考点的题目:
polyre:
secret = [0xBC8FF26D43536296, 0x520100780530EE16,
0x4DC0B5EA935F08EC,
0x342B90AFD853F450,
0x8B250EBCAA2C3681,
0x55759F81A2C68AE4, 0xB0004B7679FA26B3,
]
key = 0xB0004B7679FA26B3 # 异或的数值
flag = ""
# 产生CRC32查表法所用的表
for s in secret:
for i in range(64): # 这个是轮数
sign = s & 1 # 数乘以二,必为偶数,再异或上奇数,得奇数 所以最后一位为1的数还原前为负。
if sign == 1:
s ^= key
s //= 2
# 防止负值除2,溢出为正值
if sign == 1:
s |= 0x8000000000000000 # 让数回到负数
j = 0
while j < 8:
flag += chr(s & 0xFF) # &0xff可以将高的24位置为0,低8位保持原样。
s >>= 8
j += 1
print(flag)
拿到flag
flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}
后续再补充....