分类 强网杯 下的文章 - c4f3b4b3

强网杯2024 Writeup - 星盟安全团队

2024强网杯 web&re 部分wp - 先知社区

2024 强网杯逆向 Writeups - gaoyucan - 博客园

2024 强网杯 部分题解 - S1uM4i

[推荐][原创] 强网杯2024 ez_vm 手撕VM + DFA Attack Whitebox AES-CTF对抗-看雪-安全社区|安全招聘|kanxue.com

太难了,被暴打了

Mips

image-20241129161606662

出题人应该不会好心的给你一个qemu-static,应该是经过魔改的

发现有一个smc

qemu-mips -g 12345 ./mips_bin  #QEMU在12345端口上开启一个GDB服务器,这样GDB可以远程连接并控制QEMU的执行。

#gdb的调试方法
#另外开一个窗口
gdb-multiarch ./mips_bin
set arch mips 
set endian big
target remote localhost:12345 #GDB连接到本地的12345端口

#ida的调试方法
ida remote gdbserver

这道题目的几个好玩的地方

image-20241201225809681
  1. 首先是mmap这个函数,在0x23000地址映射了一片区域,方便了魔改的qemu做手脚

  2. 这里又开了个子线程,调试的方法,先下个断点,然后再开个ida附加,或者直接set ip直接过掉

  3. 直接emu -g 无法开启gdbserver,直接patch一下就可以了

  4. 可以先编译一个qemu-user-linux 然后bindiff恢复大部分符号方便逆向分析

  5. 这里opcodes是个smc,直接动态调试,创建函数

    image-20241201230207341

    发现只是一个简单的异或

    cipher="sxrujtv`labiVzbp`vpg|"
    for i in range(21):
         print(chr(ord(cipher[i])^(0x15-i)),end="")

    得到了一个假flag flag{dynamic_reverse}

    image-20241201230345022

    显然中间有暗改的地方

    动态跟踪一直到最后都没有看到数据暗改的地方,那么只有write函数做了手脚

    这里可以猜到qemu对syscall的调用进行了暗改,我们要先找到qemu-write的系统调用

    通过函数特征可以定位到这里

    image-20241201230816412

    这样就找到了flag效验的地方,找到加密函数

    image-20241201230949346
加密流程分析

1.进入encrypt函数,实际上是 input-> 移位加密 -> xor_keystream -> 标准RC4加密

j=0

for i in range(22):

  j+=sbox[i]

  sbox[i],sbox[j&0xff]=sbox[j&0xff],sbox[i]

  k=((rol(input[i+5],7)<<6)^0xC0| (rol(input[i+5],7) >>2)^0x3B)^0xBE

  h=((rol(k,5)^0x3D) <<4 | (rol(k,5)^0xAD) >>4)^ 0xDE

  key2=[0xDE, 0xAD, 0xBE, 0xEF]

  cipher[i]=sbox[(sbox[j]+sbox[i])&0xff]^key2[i%4]^rol(h,3)

  

2.encrypt函数外部  xor_keystream

3.swap(7,11) swap(12,16)  ->cipher

直接对着写解密脚本

from string import printable
cipher = [
    0x000000C4, 0x000000EE, 0x0000003C, 0x000000BB, 0x000000E7, 0x000000FD, 0x00000067, 0x0000001D, 
    0x000000F8, 0x00000097, 0x00000068, 0x0000009D, 0x0000000B, 0x0000007F, 0x000000C7, 0x00000080, 
    0x000000DF, 0x000000F9, 0x0000004B, 0x000000A0, 0x00000046, 0x00000091, 0x00000000, 0x00000000
]

def swap(a,l,r):
    a[l],a[r] = a[r],a[l]



def rol(x,n):
    return ((x<<n)|(x>>(8-n)))&0xff


#生成RC4密钥流
j=0
key="6105t3"
sbox=[i for i in range(256)]
keybox=[0]*256
k=[ord(key[i%6]) for i in range(256)]
for i in range(256):
    a=sbox[i]
    b=k[i]
    j+=(a+b)
    sbox[i],sbox[j&0xff]=sbox[j&0xff],sbox[i]
j=0
i=0
for _ in range(256):
    i = (i + 1) % 256 
    j= (j+sbox[i])%256
    sbox[i],sbox[j&0xff]=sbox[j&0xff],sbox[i]
    keybox[_]=sbox[(sbox[j&0xff]+sbox[i])&0xff]
#print(keybox)
""" 加密流程分析
1.进入encrypt函数,实际上是 input-> 移位加密 -> xor_keystream -> 标准RC4加密

j=0
for i in range(22):
    j+=sbox[i]
    sbox[i],sbox[j&0xff]=sbox[j&0xff],sbox[i]
    k=((rol(input[i+5],7)<<6)^0xC0| (rol(input[i+5],7) >>2)^0x3B)^0xBE
    h=((rol(k,5)^0x3D) <<4 | (rol(k,5)^0xAD) >>4)^ 0xDE
    key2=[0xDE, 0xAD, 0xBE, 0xEF]
    cipher[i]=sbox[(sbox[j]+sbox[i])&0xff]^key2[i%4]^rol(h,3)

    
2.encrypt函数外部  xor_keystream

3.swap(7,11) swap(12,16)  ->cipher
 """

#解密流程
swap(cipher,12,16)
swap(cipher,7,11)
for i in range(len(cipher)):
    cipher[i] ^=0xa

for i in range(len(cipher)):
    cipher[i]^=keybox[i]

key2=[0xDE, 0xAD, 0xBE, 0xEF]
for i in range(len(cipher)):
    cipher[i]^=key2[i%4]

#移位加密 直接爆破吧  
flag=""
for i in range(len(cipher)):
    for ch in printable:
        k=((rol(ord(ch),7)<<6)^0xC0| (rol(ord(ch),7) >>2)^0x3B)^0xBE
        k&=0xff
        h=((rol(k,5)^0x3D) <<4 | (rol(k,5)^0xAD) >>4)^ 0xDE
        h&=0xff
        if rol(h,3)==cipher[i]:
            print(ch,end="")
            break

斯内克

Hint: 赛题附件已更新,总体算法并未修改。对于解决此赛题,需关注以下内容:

  1. 需要选手的操作序列最短
  2. 赛题内设用于触发验证的轮次减少
  3. 选手可忽略由蛇的身体长度引起的可能

蛇要最优走好每一步;蛇不应该直接调头(如当蛇往右走时,不能直接转变方向为左),否则会咬伤自己。

首先总结一下游戏流程

image-20241203145633914

移动贪吃蛇,每一步会解密一段shellcode,如果吃到食物,并且shellcode的md5等于预设的值就执行这段shellcode,即真正的flagcheck逻辑

image-20241203145931482

opcodes太长了,直接dump出来然后再解密

import idc
start=0x7FF77CF9F000
end=0x7FF77CF9F47F
with open("dump.bin","wb") as f:
    for addr in range(start,end+1):
        val=idc.get_bytes(addr,1)
        f.write(val)
image-20241203145056797

直接根据题目逻辑写贪心

from ctypes import *
from hashlib import *
cdll.msvcrt.srand(0XDEADBEEF)
food_pos=[]
for i in range(20):
    food_pos.append((cdll.msvcrt.rand()%20,cdll.msvcrt.rand()%20))
print(food_pos)
snake_pos=(10,10)
with open(r'C:\Users\Npc\Desktop\CTF\Events\2024qwb\SNAKEEE\dump.bin',"rb") as f:
    opcode=f.read()
opcodes=[i for i in opcode]

""" code的解密观察
        if ( Buffer.Event.KeyEvent.wVirtualKeyCode == 37 )// <-
        {
          ++step_count;
          prev_keycode = 37;
          steps = 3;
          for ( j = 0; j < 1152; ++j )
            opcodes[j] = ((int)(unsigned __int8)opcodes[j] >> 5) | (8 * opcodes[j]);
        }
        else
        {
          switch ( wVirtualKeyCode )
          {
            case 38:                            // ↑
              ++step_count;
              steps = 0;
              prev_keycode = 38;
              j_memcpy(v7, opcodes, 0x480uLL);
              for ( k = 0; k < 1152; ++k )
                opcodes[k] = v7[(k + 6) % 1152];
              break;
            case 39:                            // ->
              ++step_count;
              prev_keycode = 39;
              steps = 2;
              for ( m = 0; m < 1152; ++m )
                opcodes[m] -= 0x66;
              break;
            case 40:                            // ↓
              ++step_count;
              prev_keycode = 40;
              steps = 1;
              for ( n = 0; n < 1152; ++n )
                opcodes[n] += 0x1E;
              break;
"""
'''steps:
0  x-- a
1  x++ d
2  y-- w
3  y++ s    
'''
move=1 #初始向右
snake_x,snake_y=snake_pos
for food_x,food_y in food_pos:
    dx=food_x-snake_x
    dy=food_y-snake_y
    #snake吃到食物
    snake_x,snake_y=food_x,food_y
    while True:
        last=move
        #!如果上次移动方向与这次将要移动的方向相同,就延续上一步操作,避免转弯
        if last==0 and dx<0:
            dx=0
            move=0
        if last==1 and dx>0:
            dx=0
            move=1
        if last==2 and dy<0:
            dy=0
            move=2
        if last==3 and dy>0:
            dy=0
            move=3


        #根据食物的位置,确定下一步的移动方向
        if dx>0 and last!=0:
            dx=0
            move=1
       
        elif dx<0 and last!=1:
            dx=0
            move=0
            
        elif dx==0 and last==2 and dy>0:  #特殊情况的处理
            dx=1
            move=1
        elif dx==0 and last==3 and dy<0:
            dx=1
            move=1

        else:
            if dy>0 and last!=2:
              dy=0
              move=3
            
            elif dy<0 and last!=3:
              dy=0
              move=2
        
            elif dy==0 and last==1 and dx<0:
              dy=1
              move=2
        
            elif dy==0 and last==0 and dx>0:
              dy=1
              move=3

        
        
        if last != move:
            
           # print(f"w,dx={dx},dy={dy}")
            
            match move:
                case 0:
                    print(f"a,dx={dx},dy={dy}")
                    
                    opcodes = opcodes[6:] + opcodes[:6]
                case 1:
                    print(f"d,dx={dx},dy={dy}")
                    
                    for k in range(len(opcodes)):
                        opcodes[k] = (opcodes[k] + 0x1E) & 0xFF
                case 2:
                    print(f"w,dx={dx},dy={dy}")
                    
                    for k in range(len(opcodes)):
                        opcodes[k] = (opcodes[k] - 0x66) & 0xFF
                case 3:
                    print(f"s,dx={dx},dy={dy}")
                    
                    for k in range(len(opcodes)):
                        opcodes[k] = ((opcodes[k] >> 5) | (opcodes[k] << 3)) & 0xFF
        if dx==0 and dy==0:
            break
    tmp_code = b''
    for c in opcodes:
        tmp_code += c.to_bytes(1)
    # print(md5(tmp_code).hexdigest())

    if (md5(tmp_code).hexdigest() == "9c06c08f882d7981e91d663364ce5e2e"):
        print("Found")
        with open("opcodes.bin", "wb") as f:
            f.write(tmp_code)
        exit()
#wasawdsawdsawds

发现是tea解密

cipher=[0]*16
cipher[0]=0x98   
cipher[1]=0xA0 
cipher[2]=0xD9 
cipher[3]=0x98 
cipher[4]=0xBA 
cipher[5]=0x97 
cipher[6]=0x1B 
cipher[7]=0x71 
cipher[8]=0x9B 
cipher[9]=0x81 
cipher[10]=0x44 
cipher[11]=0x2F 
cipher[12]=0x55 
cipher[13]=0xB8 
cipher[14]=0x37 
cipher[15]=0xDF 

cipher=memoryview(bytearray(cipher)).cast('I').tolist()
print(cipher)
key=b"W31c0m3. 2 QWBs8"
key=memoryview(bytearray(key)).cast('I').tolist()
print(key)
cipher[2]^=cipher[1]
cipher[3]^=cipher[0]
cipher[1]^=cipher[3]
cipher[0]^=cipher[2]

delta=0x9E3779B9
sum=(delta*0x40)&0xffffffff
for i in range(0x20):
    cipher[3]-=(key[(sum >> 11) & 3] + sum) ^ (cipher[2] + ((cipher[2] >> 5) ^ (16 * cipher[2])))
    cipher[3]&=0xffffffff
    sum-=delta
    sum&=0xffffffff
    cipher[2]-=(key[sum & 3] + sum) ^ (cipher[3] + ((cipher[3] >> 5) ^ (16 * cipher[3])))
    cipher[2]&=0xffffffff

for i in range(0x20):
    cipher[1]-=(key[(sum >> 11) & 3] + sum) ^ (cipher[0] + ((cipher[0] >> 5) ^ (16 * cipher[0])))
    cipher[1]&=0xffffffff
    sum-=delta
    sum&=0xffffffff
    cipher[0] -= (key[sum & 3] + sum) ^ (cipher[1] + ((cipher[1] >> 5) ^ (16 * cipher[1])))
    cipher[0]&=0xffffffff

for i in cipher:
    print((i.to_bytes(4,'little').decode()),end='')
#[2564399256, 1897633722, 793018779, 3744970837]
#[1664168791, 775122224, 1361064480, 947077719]
#flag{G0@d_Snake}

bbox

人肉做反而比写算法要快

先把地图map给提取出来

可以发现,每个关卡的地图大小是400,行20,宽20

import idc

start = 0x0404040
size = 5600*4
output_file_path = 'output.txt'  # 指定输出文件路径

# 打开文件以进行写入
with open(output_file_path, 'w') as f:
    for i in range(14):
        for j in range(20):
            for k in range(20):
                addr = start + (i * 400 + 20 * j + k)*4
                print(hex(addr))
                val = idc.get_bytes(addr, 1)
                f.write(' '+str(int.from_bytes(val, byteorder='little')))
            f.write('\n')
        f.write(f'----------------level{i}-------------------------\n')

print(f"数据已成功写入到 {output_file_path}")

最后4关是字符画,只有0-8关推箱子 9-12 qwb!

1是墙壁,0是通路,3是箱子,4是终点

人肉做

2 12 13 9 21 13 31 3

flag{qwb!_fec2d316d20dbacbe0cdff8fb6ff07b9}

remem

IDA 打不开 报错 Bad ELF byte sex 2 for the indicated machine

image-20241203162208866

64位小端的elf头,被错误的修改为32位大端,操作系统能识别这种错误,但是ida识别不了

直接修改正确后,用ida打开

发现是vm,case 里的操作里包含 mmap 和unmap ,应该是个jit,所以只能动调,然后判断干了些啥

直接断在每个case的call rax

动调测试得到的opcodes
case F2 0 :   x*x   
case F2 3 :  x*factor[i] i为调用的次数
case F0 0 3: a=pop()+pop()  push(a)
case F1 0 3: a=pop()-pop() push(a)
case F6 3 :  stack.push(pop() % 0x5E2F4391) rdx:存储余数 rax:存储商
case F3 : x=x^check_stack.pop()^enc[i] i为调用的次数
case f8 end

直接进行栈的模拟

opcodes=[0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F0, 0x00000000, 0x00000003, 0x000000F1, 0x00000000, 0x00000003, 0x000000F6, 0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F1, 0x00000000, 0x00000003, 0x000000F6, 0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F1, 0x00000000, 0x00000003, 0x000000F6, 0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F6, 0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F1, 0x00000000, 0x00000003, 0x000000F6, 0x00000003, 0x000000F7, 0x000000F3, 0x00000000, 0x00000003, 0x000000F3, 0x00000000, 0x00000003, 0x000000F3, 0x00000000, 0x00000003, 0x000000F3, 0x00000000, 0x00000003, 0x000000F3, 0x00000000, 0x00000003, 0x000000F8]
opcodes=[hex(i) for i in opcodes]
print(opcodes)

stack=[0]*18
"""     stack[0] = (unsigned int)input_0;
    stack[1] = (unsigned int)input_0;
    stack[2] = (unsigned int)input_1;
    stack[3] = (unsigned int)input_1;
    stack[4] = (unsigned int)input_0;
    stack[5] = (unsigned int)input_1;
    stack[6] = (unsigned int)input_0;
    stack[7] = (unsigned int)input_0;
    stack[8] = (unsigned int)input_2;
    stack[9] = (unsigned int)input_2;
    stack[10] = (unsigned int)input_3;
    stack[11] = (unsigned int)input_2;
    stack[12] = (unsigned int)input_3;
    stack[13] = (unsigned int)input_3;
    stack[14] = (unsigned int)input_4;
    stack[15] = (unsigned int)input_4;
    stack[16] = 0LL;
    stack[17] = 0LL; """
"""
  factor[0] = 3;
  factor[1] = input_1;
  factor[2] = 6;
  factor[3] = 82;
  factor[4] = 6;
  factor[5] = 2;
  factor[6] = 13;
  factor[7] = 17;
  factor[8] = input_2;
  factor[9] = 5;
  factor[10] = 5;
  factor[11] = 88;
  factor[12] = input_2;
  factor[13] = 4;
  factor[14] = 5;
  factor[15] = 232;
  factor[16] = 35;
  factor[17] = 8;
  factor[18] = 16"""
check_stack=[0]*100

enc=[0x42DB9F06, 0x35368926, 0x509A3978, 0x1EBFA92F, 0x555CC98C]
enc=[hex(i) for i in enc]
factor=['3','input_1','6','82','6','2','13','17','input_2','5','5','88','input_2','4','5','232','35','8','16']
stack=['input_0','input_0','input_1','input_1','input_0','input_1','input_0','input_0','input_2','input_2','input_3','input_2','input_3','input_3','input_4','input_4',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]+[0]*100
sp=18
csp=0
ip=0
v20=1
v21="input_0"
f2_count=0
f3_count=0
'''动调测试得到的opcodes
case F2 0 :   x*x   
case F2 3 :  x*factor[i] i为调用的次数
case F0 0 3: a=pop()+pop()  push(a)
case F1 0 3: a=pop()-pop() push(a)
case F6 3 :  stack.push(pop() % 0x5E2F4391) rdx:存储余数 rax:存储商
case F3 : x=x^check_stack.pop()^enc[i] i为调用的次数
case f8 end
'''
while True:
    if ip>=len(opcodes):
        break
    opcode=opcodes[ip]
    match opcode:
        case '0xf2':
            if opcodes[ip+1]=='0x0':
                v21="("+v21+"*"+v21+")"
               # print(f"{v21}={v21}*{v21}")
            elif opcodes[ip+1]=='0x3':
                v21="("+v21+"*"+factor[f2_count]+")"
               # print(f"{v21}={v21}*{factor[f2_count]}")
                f2_count+=1

            ip+=2
        case '0xf0':
            if opcodes[ip+1]=='0x0' and opcodes[ip+2]=='0x3':
                sp-=1
                a=stack[sp]
                sp-=1
                b=stack[sp]
                stack[sp]=str(a)+"+"+str(b)
                sp+=1
            ip+=3
        case '0xf1':
            if opcodes[ip+1]=='0x0' and opcodes[ip+2]=='0x3':
                sp-=1
                a=stack[sp]
               # type(a)
                sp-=1
                b=stack[sp]
               # type(b)
                stack[sp]=str(a)+"-"+str(b)
                sp+=1
            ip+=3
        case '0xf6':
            if opcodes[ip+1]=='0x3':
                sp-=1
                a=stack[sp]
                #print(csp)
                check_stack[csp]="("+a+")"+"%0x5E2F4391"
                csp+=1
            ip+=2
        case '0xf3':
            csp-=1
            a=check_stack[csp]
            v21="("+str(a)+"^"+str(v21)+"^"+enc[f3_count]+")"
            f3_count+=1
            ip+=3
        case '0xf7':
            stack[sp]=v21
            sp+=1
            v14=v20
            v21=stack[v14]
            v20+=1
            ip+=1
        
        case '0xf8':
            print(v21)
            exit(1)

        case _:
            print(opcode)
            continue
((((input_4*input_4)*16)+(input_4*8)-((input_3*input_3)*35))%0x5E2F4391^(((input_3*232)+((input_2*input_2)*5)-((input_3*input_2)*4))%0x5E2F4391^(((input_2*88)+((input_2*input_2)*5)-((input_0*input_2)*5))%0x5E2F4391^(((input_0*17)+(input_1*13)+((input_0*input_0)*2))%0x5E2F4391^(((input_1*6)+(input_1*82)+((input_0*input_1)*6)-((input_0*input_0)*3))%0x5E2F4391^0^0x42db9f06)^0x35368926)^0x509a3978)^0x1ebfa92f)^0x555cc98c)

把异或视为判断,并将表达式转换成⽅程组。化简结果可以得到

(((input_1*6)+(input_1*82)+((input_0*input_1)*6)-((input_0*input_0)*3))%0x5E2F4391^0x42db9f06) ==0
(((input_0*17)+(input_1*13)+((input_0*input_0)*2))%0x5E2F4391^^0x35368926) ==0
(((input_2*88)+((input_2*input_2)*5)-((input_0*input_2)*5))%0x5E2F4391^^0x509a3978)==0
(((input_3*232)+((input_2*input_2)*5)-((input_3*input_2)*4))%0x5E2F4391^^0x1ebfa92f)==0
((((input_4*input_4)*16)+(input_4*8)-((input_3*input_3)*35))%0x5E2F4391^^0x555cc98c)==0 
88* input[1] + 6* input[0]* input[1] + -3* input[0]* input[0] mod 0x5E2F4391 == 0x42DB9F06
17* input[0] + 13* input[1] + 2* input[0]* input[0] mod 0x5E2F4391 == 0x35368926
88* input[2] + 5* input[2]* input[2] + -5* input[0]* input[2] mod 0x5E2F4391 == 0x509A3978
232* input[3] + 5* input[2]* input[2] + -4* input[2]* input[3] mod 0x5E2F4391 == 0x1EBFA92F
16* input[4]* input[4] + 8* input[4] + -35* input[3]* input[3] mod 0x5E2F4391 == 0x555CC98C

由于神秘原因z3无法解出

from z3 import *

# Define modulus
modulus = 0x5E2F4391

# Define input variables as 32-bit bit-vectors
input_vars = [BitVec(f'input[{i}]', 32) for i in range(5)]

# Define equations
eq1 = (88 * input_vars[1] + 6 * input_vars[0] * input_vars[1] - 3 * input_vars[0] * input_vars[0]) % modulus == 0x42DB9F06
eq2 = (17 * input_vars[0] + 13 * input_vars[1] + 2 * input_vars[0] * input_vars[0]) % modulus == 0x35368926
eq3 = (88 * input_vars[2] + 5 * input_vars[2] * input_vars[2] - 5 * input_vars[0] * input_vars[2]) % modulus == 0x509A3978
eq4 = (232 * input_vars[3] + 5 * input_vars[2] * input_vars[2] - 4 * input_vars[2] * input_vars[3]) % modulus == 0x1EBFA92F
eq5 = (16 * input_vars[4] * input_vars[4] + 8 * input_vars[4] - 35 * input_vars[3] * input_vars[3]) % modulus == 0x555CC98C

# Create solver and add equations
solver = Solver()
solver.add(eq1, eq2, eq3, eq4, eq5)

# Check satisfiability and solve
if solver.check() == sat:
    model = solver.model()
    solution = [model[input_vars[i]].as_long() for i in range(5)]
else:
    solution = "No solution found"

print(solution)

看到大佬的解法,观察等式1和等式2 可以构造关于input_1的等式,直接用sagemath开根求解

modulu=0x5E04391
R.<x1> = Zmod(modulu)[]
x2=(0x35368926-0x11*x1-2*x1^2)/0xd
f=-3*x1^2+88*x2+6*x1*x2--0x42DB9F06
x1,_=f.roots()[0]
R.<x2> = Zmod(modulu)[]
f=2*x1*x1+13*x2+17*x1-0x35368926
x2,_=f.roots()[0]

同理去解x3,x4,x5

x1 = 862152290
x2 = 53932501 + 0x5E2F4391
x3 = 962670904
x4 = 859320929
x5 = 50524140 + 0x5E2F4391

3cfbaf5f9a18382aa23}

也可以利用爆破,构造x1的等式之后, 我们假设是由16进制字符的形式 组成

代码留着学习

#include <stdint.h>
#include <stdio.h>

#define P 0x5e2f4391

void any_conv(char *p, uint32_t n) {
    const char *table = "0123456789abcdef"; // v5 }
    const int base = 16;                    // v5 17
    const int outsize = 8;                  // 4

    for (int i = 0; i < outsize; i++) {
        int v = n % base;
        p[outsize - 1 - i] = table[v];
        n = n / base;
    }
}

void solve() {
    char buf[9] = {0};
    // 3cfbaf5f9a18382aa23}
    uint64_t v1 = 0x33636662;
    uint64_t v2 = 0x61663566;
    uint64_t v3 = 0x39613138;
    uint64_t v4 = 0x33383261;
    uint64_t v5 = 0x6132337D;

    for (uint32_t x = 0; ; x++) {
        any_conv(buf, x);

        if (x % 0x100000 == 0) {
            printf("\r%08X", x);
            fflush(stdout);
        }

        v1 = *(uint32_t *)&buf[0];
        v2 = *(uint32_t *)&buf[4];
        //v3 = *(uint32_t *)&buf[0];
        //v4 = *(uint32_t *)&buf[4];
        //v5 = *(uint32_t *)&buf[0];

        uint64_t res1 = (((v2 * 0x58 + v1 * v2 * 0x6 - v1 * v1 * 0x3)) % P); // 0x42DB9F06
        uint64_t res2 = (((v1 * 0x11 + v2 * 0xd + v1 * v1 * 0x2)) % P);       // 0x35368926
        uint64_t res3 = (((v3 * 0x58 + v3 * v3 * 0x5 - v1 * v3 * 0x5)) % P);  // 0x509A3978
        uint64_t res4 = (((v4 * 0xe8 + v3 * v3 * 0x5 - v4 * v3 * 0x4)) % P);  // 0x1EBFA92F
        uint64_t res5 = (((v5 * 0x8 + v5 * v5 * 0x10 - v4 * v4 * 0x23)) % P); // 0x555CC98C

        if (res1 == 0x42DB9F06 && res2 == 0x35368926) {
            printf("\n\n%s: %08X %08X\n", buf, (uint32_t)v1, (uint32_t)v2);
            break;
        }

        // if (res3 == 0x509A3978 && res4 == 0x1EBFA92F && res5 == 0x555CC98C) {
        //     printf("\n\n%s: %08X %08X %08X\n", buf, (uint32_t)v3, (uint32_t)v4, (uint32_t)v5);
        //     break;
        // }
    }
}

int main() {
    solve();
}

ez_vm

工程量太大了,待复现

apk2solve

安卓逆向,待学习