quite_easy

考点是IAT hook,主函数调试没有结果

2024-07-15T13:55:18.png
动态调试过这个反调试函数的时候失败了,交叉引用反调试检测函数能发现还有一个TLS回调函数,跟进TLS看看
2024-07-15T13:55:36.png
1.TLS函数分析

(1)前面的Check反调试要过掉,把jz修改为jmp(因为后面多次调用反调试,所以建议直接改指令,改标识符比较麻烦)
2024-07-15T13:55:57.png

2024-07-15T13:56:04.png
(2)下面进入两个函数

sub_2E1195()和sub_2E14A6()

两个函数都有爆红

sub_2E1195()

这个主要也是反调试,出现了爆红,(之前尝试nop掉,但是报了异常,索性直接不管了,只是反调试,F7跟进动调过了就行
2024-07-15T13:56:23.png
2024-07-15T13:56:30.png
(2)

第二个函数跟进查看,这是主要的加密函数
2024-07-15T13:56:46.png
如果需要转成函数,F5先是伪代码的话,当程序走过爆红的地方可以把之前爆红的地方nop,再按p创建函数,就可以了,如下
2024-07-15T13:57:03.png
下面就能看到大概逻辑
2024-07-15T13:57:26.png

这里是加密函数(也可以通过Shift+F12在字符串窗口找到可能的字符![image](assets/image-20240714221928-fm2gitk.png)交叉引用找到

进入函数:主要思路就是 根据字符串长度来生成伪随机数,输入的前16个字节和伪随机数异或,后16个字节和原始前16个字节异或,最后0-32字节减去假的flag
2024-07-15T13:58:02.png
2024-07-15T13:58:10.png

#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文件
2024-07-15T13:58:31.png
(2)soEasy函数,汪学长说是CRC64的考点
2024-07-15T13:58:47.png

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:

(1)去混淆
2024-07-15T13:59:08.png
(2)CRC64算法写脚本

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}
后续再补充....