VNCTF2024 RE yun WP

动态调试血的教训

不过这题比较麻烦,native层没有x86架构,不能用虚拟机跑,得用真机,而且有的真机还装不上,动调贼麻烦。

那就静态分析

jadx可以看到一些简单的字符串判断逻辑

IDA逆一下so文件(注意用7.7打开,8.3打开没有生成ARM的伪代码的插件

JNI_OnLoad 是 Java Native Interface (JNI) 中的一个特定函数,它允许开发者在加载本地库(如 .so 或 .dll 文件)时执行一些初始化操作。这个函数在 Java 虚拟机(JVM)加载本地库时自动调用。

在 JNI_OnLoad 函数中,你可以执行一些一次性初始化任务,比如注册本地方法、设置本地引用等。这对于确保本地库的正确运行至关重要。

需要注意的是,JNI_OnLoad 函数是可选的,如果你的本地库不需要任何特殊的初始化操作,你可以省略这个函数。但是,如果你的本地库需要注册本地方法或执行其他初始化任务,那么实现 JNI_OnLoad 函数是很有用的。

查看JNI_OnLoad初始化函数

似乎是一些初始化的内容,最后返回一个1FB44的地址,看一下汇编

进行了一些地址的加载并调用了sub_1E9C0函数,点进去看一下

是异或加密,修改了类似密文的数据

c0 = [0x5D, 0x55, 0x75, 0x46, 0x5B, 0x67, 0x66, 0x0B, 0x73, 0x67, 
  0x7D, 0x09, 0x5C, 0x51, 0x76, 0x46, 0x65, 0x55, 0x75, 0x47, 
  0x5E, 0x52, 0x0F, 0x46, 0x5B, 0x78, 0x71, 0x49, 0x72, 0x0C, 
  0x6D, 0x4E, 0x70, 0x78, 0x6E, 0x0F, 0x5A, 0x6B, 0x7D, 0x4F, 
  0x5B, 0x77, 0x4F, 0x50, 0x72, 0x67, 0x6A, 0x4B, 0x3F]
c1 = ''
for i in range(0x31):
    c0[i] ^= c0[0x30]
    c1 += chr(c0[i]&0xff)
print("crypto: ",c1)
#crypto:  bjJydXY4LXB6cnIyZjJxam0ydGNvM3RqOGQ0eTBpdHpoMXUt

查询函数引用定位到了sub_1FA24

给出了地址和密钥两个参数

unk_4D0DE = [0x48, 0x42, 0x4F, 0x49, 0x1E, 0x71, 0x41, 0x2E]
name = ''
for i in range(8):
    unk_4D0DE[i] ^= unk_4D0DE[7]
    name += chr(unk_4D0DE[i]&0xff)
print("name: ",name)
#name:  flag0_o

unk_4D0EC = [0x47, 0x23, 0x05, 0x0E, 0x19, 0x0E, 0x40, 0x03, 0x0E, 0x01, 
  0x08, 0x40, 0x3C, 0x1B, 0x1D, 0x06, 0x01, 0x08, 0x54, 0x46, 
  0x35, 0x6F]
sign = ''
for i in range(22):
    unk_4D0EC[i] ^= unk_4D0EC[21]
    sign += chr(unk_4D0EC[i]&0xff)
print("sign: ",sign)
#sign:  (Ljava/lang/String;)Z

一个类似于函数名的东西,另一个是某种签名

xor还有另一个引用,似乎是修改了aVnvn字符串

原本字符串内容是VNVN,不过这里经过了xor修改

这里的伪代码看起来令人疑惑,看一下汇编就很清晰了

W8寄存器里依次入栈了0x4F, 0x3C, 0x7F, 0x3D, 0x36, 然后与索引为5-1=4的那个位置的值异或,得到修改后的字符串(考虑到大小端序,脚本是反过来写的)

avn = [0x4F, 0x3C, 0x7F, 0x3D, 0x36]
avnvn = ''
for i in range(1,5):
    avn[i] ^= avn[0]
    avnvn += chr(avn[i]&0xff)
print("avnvn: ",avnvn)
#avnvn:  s0ry

由此该地址前两个参数就分析出来了,看看第三个函数指针

16行 显然是一个字符串的比较

14行 的函数里呈现了复杂的加密过程

末尾有base64的迹象

重点就是中间的主要加密部分,实际上是希尔加密

希尔密码(加密、解密、破解)-CSDN博客文章浏览阅读2w次,点赞6次,收藏58次。希尔密码的加密、解密与破解简介希尔密码是运用基本矩阵论原理的替换密码,由Lester S. Hill在1929年发明。每个字母当作26进制数字:A=0, B=1, C=2… 一串字母当成n维向量,跟一个n×n的矩阵相乘,再将得出的结果模26。(注意用作加密的矩阵(即密匙)在 必须是可逆的,否则就不可能解码。只有矩阵的行列式和26互质,才是可逆的。)例子用希尔密码对明文串 x = ecnu 进行加密,密钥矩阵:加密密文向量 = 明文向量 * 密钥矩阵 (mod 26)1.先将明文串对应英文字_希尔密码https://blog.csdn.net/Secret_1943/article/details/110674955

先把输入的字母数字变成索引保存成一系列数字

然后每四个数一组,组成一系列2*2的明文矩阵

然后生成aVnvn密钥矩阵,正好里面有四个字符(s0ry)

最后将明文矩阵与密钥矩阵做乘法,得到密文矩阵(由于索引长度是37个字符,所以最后取mod37)

然后再把得到的索引转化为字符n2ruv8-pzrr2f2qjm2tco3tj8d4y0itzh1u-

最后base64加密成bjJydXY4LXB6cnIyZjJxam0ydGNvM3RqOGQ0eTBpdHpoMXUt

所以需要求个逆矩阵,不过直接z3爆也可以

#yun wp
unk_4D0DE = [0x48, 0x42, 0x4F, 0x49, 0x1E, 0x71, 0x41, 0x2E]
name = ''
for i in range(8):
    unk_4D0DE[i] ^= unk_4D0DE[7]
    name += chr(unk_4D0DE[i]&0xff)
print("name: ",name)
#name:  flag0_o

unk_4D0EC = [0x47, 0x23, 0x05, 0x0E, 0x19, 0x0E, 0x40, 0x03, 0x0E, 0x01, 
  0x08, 0x40, 0x3C, 0x1B, 0x1D, 0x06, 0x01, 0x08, 0x54, 0x46, 
  0x35, 0x6F]
sign = ''
for i in range(22):
    unk_4D0EC[i] ^= unk_4D0EC[21]
    sign += chr(unk_4D0EC[i]&0xff)
print("sign: ",sign)
#sign:  (Ljava/lang/String;)Z

avn = [0x4F, 0x3C, 0x7F, 0x3D, 0x36]
avnvn = ''
for i in range(1,5):
    avn[i] ^= avn[0]
    avnvn += chr(avn[i]&0xff)
print("avnvn: ",avnvn)
#avnvn:  s0ry

c0 = [0x5D, 0x55, 0x75, 0x46, 0x5B, 0x67, 0x66, 0x0B, 0x73, 0x67, 
  0x7D, 0x09, 0x5C, 0x51, 0x76, 0x46, 0x65, 0x55, 0x75, 0x47, 
  0x5E, 0x52, 0x0F, 0x46, 0x5B, 0x78, 0x71, 0x49, 0x72, 0x0C, 
  0x6D, 0x4E, 0x70, 0x78, 0x6E, 0x0F, 0x5A, 0x6B, 0x7D, 0x4F, 
  0x5B, 0x77, 0x4F, 0x50, 0x72, 0x67, 0x6A, 0x4B, 0x3F]
c1 = ''
for i in range(0x31):
    c0[i] ^= c0[0x30]
    c1 += chr(c0[i]&0xff)
print("crypto: ",c1)
#crypto:  bjJydXY4LXB6cnIyZjJxam0ydGNvM3RqOGQ0eTBpdHpoMXUt

#解base64: n2ruv8-pzrr2f2qjm2tco3tj8d4y0itzh1u- (36位)
#0x6e,0x32,0x72,0x75,0x76,0x38,0x2d,0x70,0x7a,0x72,0x72,0x32,0x66,0x32,0x71,0x6a,0x6d,0x32,0x74,0x63,0x6f,0x33,0x74,0x6a,0x38,0x64,0x34,0x79,0x30,0x69,0x74,0x7a,0x68,0x31,0x75,0x2d

#参考了sln_1550师傅的代码
from z3 import *
s = Solver()
flag = [BitVec('x%d' % i, 16) for i in range(36)] #设个flag用z3爆
tmp=[0]*4 #每个矩阵2*2
key='s0ry'
table='abcdefghijklmnopqrstuvwxyz1234567890-'
res='n2ruv8-pzrr2f2qjm2tco3tj8d4y0itzh1u-'
for i in range(2):
    for j in range(2):
        tmp[2*i+j]=table.index(key[2*i+j]) #生成密钥矩阵
ret=[0]*36
for i in range(len(flag)//2):
    for j in range(2):
        for k in range(2):
            ret[i*2+j]+=flag[i*2+k]*tmp[k*2+j] #flag矩阵乘密钥矩阵=密文矩阵
for i in range(36):
    s.add(And(flag[i]>=0,flag[i]<37)) #索引范围
    s.add(ret[i]%37==table.index(res[i]))
if s.check() != sat:
    print ("error!")
if s.check() == sat: #z3求解
    m = s.model()
print(''. join(table[m[flag[i]].as_long()%37] for i in range(36))) #索引再转字符
#75531c14-8825-44ed-a9ec-74d47d5cb76b

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-11 22:36:06       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-11 22:36:06       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-11 22:36:06       87 阅读
  4. Python语言-面向对象

    2024-04-11 22:36:06       96 阅读

热门阅读

  1. 蓝桥杯赛前模拟

    2024-04-11 22:36:06       40 阅读
  2. Bonnie++ 工具学习记录

    2024-04-11 22:36:06       33 阅读
  3. vue中404解决方法

    2024-04-11 22:36:06       31 阅读
  4. 力扣日记4.10-【动态规划篇】343. 整数拆分

    2024-04-11 22:36:06       37 阅读
  5. php调用SQL的增改查

    2024-04-11 22:36:06       36 阅读
  6. 数据结构面试

    2024-04-11 22:36:06       41 阅读
  7. SVN客户端异常问题处理

    2024-04-11 22:36:06       33 阅读
  8. leetcode209--长度最小的子数组

    2024-04-11 22:36:06       41 阅读
  9. spring

    spring

    2024-04-11 22:36:06      41 阅读