跳过正文
  1. Writeups/

HGAME2024 Revserse

·1327 字·7 分钟·
CTF Reverse
目录

Week1
#

ezIDA
#

64位IDA打开,Shift+F12打开strings窗口能看到flag。

ezIDA

ezASM
#

汇编语言看得有点头大,找到关键的 check flag 部分

    xor al, 0x22
    cmp al, byte [c + esi]

发现是异或0x22加密,信息在section .data已经给出,用脚本解密。

num=[74, 69, 67, 79, 71, 89, 99, 113, 111, 125, 107, 81, 125, 107, 79, 82, 18, 80, 86, 22, 76, 86, 125, 22, 125, 112, 71, 84, 17, 80, 81, 17, 95, 34]
flag=[]
for i in num:
    i^=0x22
    flag.append(chr(i))
print(''.join(flag))

ezPYC
#

题目给出了一个用python编写但打包成了exe的程序,先用工具 pyinstxtractor.py 把exe文件还原为pyc文件,再找在线反编译工具导出py文件。

程序的逻辑同样是异或加密,复用反编译后的代码再修改一点点,进行解密。

ezPYC1

flag=[87, 75, 71, 69, 83, 121, 83, 125, 117, 106, 108, 106, 94, 80, 48, 114, 100, 112, 112, 55, 94, 51, 112, 91, 48, 108, 119, 97, 115, 49, 112, 112, 48, 108, 100, 37, 124, 2]
c=[1, 2, 3, 4]
result=[]
for i in range(0, 36, 1):
    result.append(chr(c[i % 4] ^ flag[i]))
print(''.join(result))

ezupx
#

简单的upx脱壳。脱壳后在IDA中反编译,可以看出与0x32进行了异或。直接脚本解密。

![ezupx 1](ezupx 1.png)

num= [0x64,0x7B,0x76,0x73,0x60,0x49,0x65,0x5D,0x45,0x13,0x6B,0x02,0x47,0x6D,0x59,0x5C,0x02,0x45,0x6D,0x06,0x6D,0x5E,0x03,0x46,0x46,0x5E,0x01,0x6D,0x02,0x54,0x6D,0x67,0x62,0x6A,0x13,0x4F,0x32]
for i in num:
    i^=0x32
    flag.append(chr(i))

print(''.join(flag))

Week2
#

ezcpp
#

魔改TEA,delta换成了0xDEADBEEF,右移改成左移。

四次TEA加密的代码都挤到主函数里来了,不过还是能看出来结构的。

8个字节分组加密,但是每次加密的明文只比上一组向后移了一个字节,最后只加密了前11个字节。(这里用的是最开始的附件,后面更新的附件修复了)

#include<stdio.h>
void modify_tea_decry(unsigned int *data, unsigned int *key)
{
    unsigned int d1 = data[0], d2 = data[1];
    unsigned int delta = 0xDEADBEEF, number = delta * 32;
    for (int i = 0; i < 32; i++)
    {
        d2 -= ((d1<<4) + key[2]) ^ ((d1<<5) + key[3]) ^ (d1 + number);
        d1 -= ((d2<<4) + key[0]) ^ ((d2<<5) + key[1]) ^ (d2 + number);
        number -= delta;
    }
    data[0] = d1;
    data[1] = d2;
}

int main()
{
    unsigned char data[33] = {
    	0x88, 0x6A, 0xB0, 0xC9, 0xAD, 0xF1, 0x33, 0x33, 
        0x94, 0x74, 0xB5, 0x69, 0x73, 0x5F, 0x30, 0x62, 
    	0x4A, 0x33, 0x63, 0x54, 0x5F, 0x30, 0x72, 0x31, 
        0x65, 0x6E, 0x54, 0x65, 0x44, 0x3F, 0x21, 0x7D};
    unsigned int key[] = {1234,2341,3412,4123};
    for(int i = 3; i >= 0; i--)
        modify_tea_decry((unsigned int*)(data+i),key);
    printf("%s",data);
    return 0;
}

android
#

java层检测用户名,RC4

native层检测密码

babyre
#

可以比较直观的看到开了4个线程,分别对应4种不同的加密方式。每加密一个字节切换到固定顺序的下一个线程,依次加密完输入的flag。

babyre_init

关于key的部分埋了不少坑,先是.init_array中的函数改了 key,之后主函数中异或了0x11进行加密。加密过程中还会进行异常处理,结果只异或了 key的前3个字节。

从F5伪码不能直接看出异常的触发,需要看反汇编。idiv在除数为零时触发 SIGFPE信号作为异常。而除数var_38 = i - 3,在异或第4个字节时等于0。

babyre_div

#include<stdio.h>
int main()
{
    unsigned char key[] = {0x66,0x65,0x69,0x66,0x65,0x69};
    int flag[] = {
        0x2F14, 0x004E, 0x4FF3, 0x006D, 0x32D8, 0x006D, 0x6B4B, -110, 
        0x264F, 0x005B, 0x52FB, -100, 0x2B71, 0x0014, 0x2A6F, -107, 
        0x28FA, 0x001D, 0x2989, -101, 0x28B4, 0x004E, 0x4506, -38, 
        0x177B, -4, 0x40CE, 0x007D, 0x29E3, 0x000F, 0x1F11, 0x00FF, 0};
	flag[32] = 249 + 1;
    for (int i = 0; i < 3; i++)		
        key[i] ^= 17;		// 异常处理  
    
    for (int k = 31; k >= 0; k--)
    {
        switch (k % 4){
        case 0:
            flag[k] -= flag[k+1] * key[(k+1)%6];
            break;
        case 1:
            flag[k] += flag[k+1] ^ key[(k+1)%6];
            break;
        case 2:
            flag[k] /= flag[k+1] + key[(k+1)%6];
            break;
        case 3:
            flag[k] ^= flag[k+1] - key[(k+1)%6];
            break;
        }
    }
    for (int c=0;c<32;c++)
        printf("%c",(char)flag[c]);
    return 0;
}

作为复现,回过头来看一下Linux的多线程和异常处理:

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);

#include <pthread.h>
int pthread_create(pthread_t *thread,
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *),
                   void *arg);
int pthread_exit(void *retval_ptr);
int pthread_join(pthread_t thread, void ** retval);

sem信号量机制实现线程同步

pthread相关的函数实现线程的创建,退出和回收

#include <signal.h>
#include <setjmp.h>

void (*signal(int sig, void (*handler)(int)))(int);   
int sigsetjmp(jmp_buf env, int savemask)
void siglongjmp(jmp_buf env, int value)

C语言通过库函数 setjmplongjmp实现异常处理。setjmp设置跳转点,其他地方调用 longjmp跳转到该点抛出异常。POSIX定义了两个新函数 sigsetjmpsiglongjmp,两者之间的唯一区别是 sigsetjmp增加了一个参数。

setjmp函数保存程序当前的堆栈环境到 env参数中,接下来的其它地方,你可以通过调用 longjmp函数来恢复先前被保存的程序堆栈环境,并且因此程序控制流会返回到先前调用 setjmp时的执行点。当 setjmplongjmp组合一起使用时,能在程序中实现“非本地局部跳转”(“non-local goto”)的机制。这种机制常用于实现把程序的控制流传递到错误处理模块之中;或者程序中不采用正常的返回语句,或函数的正常调用等方法,而使程序能被恢复到先前的一个调用例程(即函数)中。 ——MSDN

signal函数用于信号处理。本题中捕获算术异常的信号,随即调用 handler函数中的 siglongjmp进行异常处理。

arithmetic
#

查壳发现魔改UPX,手动修复节区名之后脱壳。

类似变种迷宫题,从out文件依次加载数据到内存中,按照下三角排列,作为地图。

路径只有两个方向,1和2分别对应向下和右下。程序中用随机数生成路径,不过肯定没有用

最后要求所有路径节点的和大于固定值6752833,一开始想过爆破,但显然很费时费力。而且在动调时还发生内存溢出,整个out文件的内容不能完全读取。

一般迷宫题比较理想的解法都是写算法求解,翻了下官方wp,确实用的是动态规划。奈何不会算法,这里就仅复现一下思路,不写脚本了。

Week3
#

crackme
#

c++异常处理,主动抛出异常并轮流调用三个 catch语句,对应XTEA加密的三步。

这里的XTEA魔改的比较多,改了加密顺序,改了delta,两次左移右移的位数也不相同。

IDA对于异常处理相关代码的识别并不是很好。之前用IDA 7.5版本只能识别出第一个异常的try块;后面改用最新版9.0,三个异常都能识别,而且对应的catch块也直接能对应上。但是F5反编译之后看不到check部分。这一部分只能借助反汇编来看,好在并不复杂。

#include<stdio.h>
void modi_xtea_decry(unsigned int *data, unsigned int *key)
{
    unsigned int d1 = data[0], d2 = data[1];
    unsigned int delta = 0x33221155;
    unsigned int number = 0;
    for (int i = 0; i < 32; i++)
    {   
        number ^= delta;
        d2 -= ( ((d1<<5) ^ (d1>>6)) + d1) ^ (number + key[(number>>11) & 3]);
        d1 -= ( ((d2<<4) ^ (d2>>5)) + d2) ^ (number + key[number & 3]);
    }
    data[0] = d1;
    data[1] = d2;
}
int main()
{
    unsigned int key[] = {1234,2345,3456,4567};
    unsigned int v18[8];
    v18[0] = 0x32FC31EA;
    v18[1] = 0xF0566F42;
    v18[2] = 0xF905B0B2;
    v18[3] = 0x5F4551BE;
    v18[4] = 0xFB3EFCBB;
    v18[5] = 0x6B6ADB30;
    v18[6] = 0x4839879;
    v18[7] = 0x2F4378DF;
    for(int i = 0; i < 8; i += 2)
        modi_xtea_decry(v18+i,key);
    printf("%s",(char*)v18);
    return 0;
}

利用动调来追踪输入会比较方便一些

findme
#

IDA反编译程序,在主函数中看到一个fake_flag,下面是一串乱码。观察乱码最后面是等号,猜测是Base系列编码。解码后发现还是一个fake_flag。

findme01

main函数很短,除了上面两个假flag,只剩下输出Buffer一个语句。查看Buffer变量的内容,发现是字符M,但是紧接着四个字节后又有一个字节'Z'。这里联想到PE文件的MZ头。但是正常它只会出现在文件的最前面部分,中间也没有间隔。于是打开Hex窗口看了后面的数据,又发现了This program cannot be run in DOS mode这一标志性的信息。可以确定在题目的程序里面又藏了一个PE文件。

能够看出内嵌的文件把原来的每个字节都扩充成了4个字节,高位的3个字节(小端序)都补零对齐。在010Editor中把有这种特征的部分提取出来,另存为enc.exe,然后写了几行c代码把它恢复成正常的PE文件。

findme02

#include<stdio.h>
int main()
{
    int buf;
    char byte;
    FILE *enc = fopen("C:\\Users\\LENOVO\\Desktop\\enc.exe","rb+");
    FILE *dec = fopen("C:\\Users\\LENOVO\\Desktop\\dec.exe","wb+");
    while(!feof(enc))
    {
        fread(&buf,sizeof(int),1,enc);
        byte = buf & 0xff;
        fwrite(&byte,sizeof(char),1,dec);
    }
    fclose(enc);
    fclose(dec);
    return 0;
}

反编译提取出来的程序,在IDA中没找到main函数。分析汇编部分,看到了不少花指令,而且形式都相似,如下:

jz   loc_xx
jnz  loc_xx
db   0xC7

flower1

从头开始把所有花指令都patch成nop,重新建立函数,可以正常反编译main函数。除去输入输出,主函数调用了sub_401068sub_40110C两个函数,最后把处理后的输入和数组byte_405148比较判断。分别跟踪这两个函数,发现大部分比较熟悉(算法和Week2的一道题很像,当时没做出来,后面看wp才知道是RC4)结合网络资料,判断出这两个函数是经过小改的RC4算法。

改动主要有三点

  1. S盒初始状态,各元素都是索引值的相反数(从unsigned char的角度来看,溢出成256-i

  2. 密钥流的生成,取S盒中从后向前第s[v1]+s[v3]个元素,同样看成s[256-(s[v1]+s[v3])]

    这部分IDA反编译的代码不好理解,array[-(buffer[v1]+buffer[v3])],最后索引值取负。但是负的索引在C语言没有意义。如果理解为unsigned char,由于array长度只有32,同样也会出现越界的情况。于是分析一下对应的汇编,得出结论是这里负号要看成从array地址减去那个索引值。

    在栈中查看arraybuffer之间的偏移量,发现刚好是256,这两个数组无缝衔接。那么从array地址减去k对应的元素也就是buffer从后向前第k个元素。

  3. 加密没有进行异或,而是原文和对应密钥流相加。

findme2

findme1

最后进行解密。按照原算法的流程推出密钥流,之后反过来从加密数据中逐一减去即可。

array= [0x7D, 0x2B, 0x43, 0xA9, 0xB9, 0x6B, 0x93, 0x2D, 0x9A, 
  0xD0, 0x48, 0xC8, 0xEB, 0x51, 0x59, 0xE9, 0x74, 0x68, 0x8A, 
  0x45, 0x6B, 0xBA, 0xA7, 0x16, 0xF1, 0x10, 0x74, 0xD5, 0x41, 
  0x3C, 0x67, 0x7D]
buffer = [0]+[256-i for i in range(1,256)]
v = b'deadbeef'
v6 = list(v*32)

v2 = 0
for j in range(256):
    v2 = (buffer[j]+v6[j]+v2) % 256
    buffer[v2],buffer[j] = buffer[j],buffer[v2]
v1 = v3 = 0
for i in range(32):
    v1 = (v1+1) % 256
    v3 = (buffer[v1]+v3) % 256
    buffer[v1],buffer[v3] = buffer[v3],buffer[v1]
    index = 256 - (buffer[v1]+buffer[v3]) & 0xff
    array[i] = (array[i]-buffer[index]) & 0xff

print(bytes(array))

encrypt
#

主函数遍布Windows加密API,去MSDN上一查都是<bcrypt.h>这个库中的。

结合文档和程序中的字符串信息,推测出是AES-CBC。

在生成 pbKey的函数附近找到 pbSecret,推测是key。在前面可以找到iv

encrypt_flag

mystery
#

主函数只有ptrace反调试,核心代码放到了initfini部分。

  • init_array预先加密了密钥,先异或后RC4。
  • fini_array实现类似主函数的功能,对输入的flag进行魔改RC4(生成密钥流后的异或加密改成了减法)
#include<stdio.h>

void rc4_crypt(unsigned char *data, unsigned char *key, int data_len, int key_len)
{
    unsigned char S[256], tmp = 0, t = 0;
    int i = 0, j = 0;
    for (int i = 0; i < 256; i++) S[i]= i;  
    for (int i = 0; i < 256; i++)
    {
        j = (j + S[i] + key[i % key_len]) % 256;
        tmp = S[i];
        S[i] = S[j];
        S[j] = tmp;
    }

    i = 0, j = 0;
    for (int l = 0; l < data_len; l++)
    {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        t = (S[i] + S[j]) % 256;
        tmp = S[i];
        S[i] = S[j];
        S[j] = tmp;
        data [l] ^= S[t];
    }
}

void modi_rc4_crypt(unsigned char *data, unsigned char *key, int data_len, int key_len)
{
    unsigned char S[256], tmp = 0, t = 0;
    int i = 0, j = 0;
    for (int i = 0; i < 256; i++) S[i]= i;
    for (int i = 0; i < 256; i++)
    {
        j = (j + S[i] + key[i % key_len]) % 256;
        tmp = S[i];
        S[i] = S[j];
        S[j] = tmp;
    }

    i = 0, j = 0;
    for (int l = 0; l < data_len; l++)
    {
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        t = (S[i] + S[j]) % 256;
        tmp = S[i];
        S[i] = S[j];
        S[j] = tmp;
        data [l] += S[t];
    }
}

int main()
{
    unsigned char key[] = {0x44, 0x4A, 0x56, 0x44, 0x4A, 0x56};
    unsigned char key1[] = {0x4D, 0x4E, 0x41, 0x70, 0x4B, 0x4A, 0x4D, 0x5A, 0x48, 0x0E};
    unsigned char data[] ={
        0x50, 0x42, 0x38, 0x4D, 0x4C, 0x54, 0x90, 0x6F, 0xFE, 0x6F, 
        0xBC, 0x69, 0xB9, 0x22, 0x7C, 0x16, 0x8F, 0x44, 0x38, 0x4A, 
        0xEF, 0x37, 0x43, 0xC0, 0xA2, 0xB6, 0x34, 0x2C};
    
    for (int i = 0; i < 6; i++)
        key[i] ^= 0x2F;
    for (int j = 0; j < 10; j++)
        key1[j] ^= 0x2F;
    
    rc4_crypt(key1,key,10,6);
    modi_rc4_crypt(data,key1,28,10);
    printf("%s",data);
}

Week4
#

crackme2
#

先是异常处理,出题人应该想借异常来隐藏代码。结合上周的经验,IDA9.0能够识别出来这部分异常处理,反编译结果还算清晰。

mov byte ptr ds:0, 1 引发空指针解引用的异常,直接跳转执行__expect(1)块中的代码(查询MSDN可知,try-except 语句是 Microsoft 特定的扩展,支持 C / C++ 中的结构化异常处理SEH

继续分析,NtQueryInformationProcess(···, ProcessDebugPort, &ProcessInformation, ···) 检查是否处于调试状态,结合后面smc进行反调试。先把这里的反调试patch掉。

crackme2_smc

smc大概有两种解决思路

  • IDApython脚本进行patch
  • 解密完成处下断点dump

动调还是方便一点,在第二次调用VirtualPrrotect 的地方下断点,此时SMC已经解密完成。在反汇编窗口中找到sub_14000105C 对应地址,重新反编译看到真正的代码逻辑。这里的反调和smc设计的很巧妙,在解密之前已经是一个正常的函数,只不过最后解出fake flag,如果不仔细看很难发现这里暗藏玄机。

解密后发现是方程,用z3求解。

考虑到方程中用了左移,最开始用z3的Bitvecs(8) 创建变量,不过确实慢,要跑大概两个多小时。而且比较疑惑的是这种方法解出的flag只有部分正确,其余的超过ascii可见字符范围。

hgame{S\xcdC\xdf\xb4n\xe4\xdfs0\xec\xf61ng_equ4t\xb1On\xf3}

参考官方wp,改用Ints 创建变量,效果立竿见影。得到正确flag

from z3 import *

(v5,v28,v11,v10,v24,v41,v22,v40,v7,v18,v29,v27,v9,v31,v30,v20,v23,v39,v15,v21,v26,v2,v42,v6,v16,v1,v17,v25,v8,v4,v19,v3,v12,v13) = Ints("v5 v28 v11 v10 v24 v41 v22 v40 v7 v18 v29 v27 v9 v31 v30 v20 v23 v39 v15 v21 v26 v2 v42 v6 v16 v1 v17 v25 v8 v4 v19 v3 v12 v13")

s = Solver()
s.add(v18+201*v24+194*v10+142*v20+114*v39+103*v11+52*(v17+v31)+((v9+v23)*2**6)+14*(v21+4*v25+v25)+9*(v40+23*v27+v2+3*v1+4*v2+4*v6)+5*(v16+23*v30+2*(v3+2*v19)+5*v5+39*v15+51*v4)+24*(v8+10*v28+4*(v42+v7+2*v26))+62*v22+211*v41+212*v29==296473)

s.add(207*v41+195*v22+151*v40+57*v5+118*v6+222*v42+103*v7+181*v8+229*v9+142*v31+51*v29+122*(v26+v20)+91*(v2+2*v16)+107*(v27+v25)+81*(v17+2*v18+v18)+45*(v19+2*(v11+v24)+v11+v24)+4*(3*(v23+v21+2*v23+5*v4)+v39+29*(v10+v1)+25*v15)+26*v28+101*v30+154*v3==354358)

s.add(177*v40+129*v26+117*v42+143*v28+65*v8+137*v25+215*v21+93*v31+235*v39+203*v11+15*(v7+17*v30)+2*(v24+91*v9+95*v29+51*v41+81*v20+92*v18+112*(v10+v6)+32*(v22+2*(v1+v23))+6*(v2+14*v16+19*v15)+83*v5+53*v4+123*v19)+v17+175*v27+183*v3==448573)

s.add(113*v19+74*v3+238*v6+140*v2+214*v26+242*v8+160*v21+136*v23+209*v9+220*v31+50*v24+125*v10+175*v20+23*v39+137*v22+149*v18+83*(v4+2*v30)+21*(9*v29+v16)+59*(4*v27+v17)+41*(v1+v41)+13*(v7+11*(v40+v15)+6*v42+4*(v28+2*v11)+v28+2*v11+17*v5)+36*v25==384306)

s.add(229*v21+78*v1+v2+v9+133*v27+74*v6+69*v26+243*v7+98*v28+253*v8+142*v25+175*v31+105*v41+221*v10+121*v39+218*(v19+v29)+199*(v24+v30)+33*(v40+7*v17)+4*(27*v20+50*v11+45*v18+19*(v3+v42)+v16+16*v23+52*v4)+195*v22+211*v5+153*v15==424240)

s.add(181*v25+61*v2+65*v21+58*v31+170*v29+143*v24+185*v10+86*v11+97*v22+235*(v23+v27)+3*(53*v41+74*(v8+v3)+13*(v42+6*v9)+11*(v39+7*v20)+15*(v18+4*v17)+v7+35*v1+29*v15)+4*(57*v6+18*(v5+2*v26)+v28+17*v16+55*v30)+151*v40+230*v4+197*v19==421974)

s.add(209*v21+249*v30+195*v2+219*v25+201*v39+85*v18+213*(v17+v31)+119*(v11+2*v41)+29*(8*v24+v40+4*v27+v27)+2*(v8+55*(2*v29+v19)+3*(v10+39*v9+2*(v6+20*v20)+35*v7)+4*(v5+31*v42+28*v3)+26*v28+46*(2*v26+v16)+98*v1)+53*v23+171*v15+123*v4==442074)

s.add(162*v19+74*v5+28*v27+243*v42+123*v28+73*v8+166*v23+94*v24+113*v11+193*v22+122*(v6+2*v7)+211*(v10+v25)+21*(v17+7*v41)+11*(v4+23*(v16+v39)+2*(v40+5*v30+2*(2*v18+v29)+2*v18+v29))+5*(46*v9+26*v20+4*(v31+2*v21)+v15+27*v2+10*v1)+36*(v3+5*v26)==376007)

s.add(63*v19+143*v5+250*v6+136*v2+214*v40+62*v26+221*v42+226*v7+171*v28+178*v8+244*v23+(v9*2**7)+150*v31+109*v29+70*v41+127*v20+204*v39+121*v22+173*v18+69*(v25+v30+v27)+74*(v16+2*v15+v15)+22*(7*v24+v17+10*v11)+40*(v1+4*v21+v21)+81*v10+94*v4+84*v3==411252)

s.add(229*v15+121*v4+28*v30+206*v16+145*v27+41*v1+247*v6+118*v26+241*v28+79*v8+102*v25+124*v23+65*v9+68*v31+239*v17+148*v24+245*v39+115*v11+163*v22+137*v18+53*(v5+2*v29)+126*(v40+2*v10)+38*(v7+v21+4*v7+6*v41)+12*(v2+16*v42)+109*v20+232*v3+47*v19==435012)

s.add(209*v21+233*v40+93*v1+241*v2+137*v8+249*v17+188*v29+86*v24+246*v10+149*v20+99*v11+37*v22+219*v18+17*(v6+10*v25)+49*(v5+3*v3+4*v28+v28)+5*(16*v39+11*(v41+2*v27+v27)+12*v7+v31+30*v16+27*v19)+18*(v23+2*(v4+v26+2*v4)+v4+v26+2*v4)+24*v9+109*v42+183*v30+154*v15==392484)

s.add(155*v15+247*v40+157*v28+119*v23+161*v17+133*v20+85*v22+229*(v7+v24)+123*(2*v31+v42)+21*(v41+12*v30)+55*(v9+v5+v18+2*v5)+15*(v3+16*v10+9*v21)+2*(v2+115*v29+111*v16+26*v6+88*v8+73*v39+71*v11+28*(v26+2*(v25+2*v1))+51*v27+99*v4+125*v19)==437910)

s.add(220*v3+200*v4+139*v15+33*v5+212*v30+191*v16+30*v27+233*v1+246*v6+89*v2+252*v40+223*v42+19*v25+141*v21+163*v9+185*v17+136*v31+46*v24+109*v10+217*v39+75*v22+157*v18+125*(v11+v19)+104*(2*v41+v20)+43*(v28+2*v29+v29)+32*(v8+v7+2*v8+2*(v23+v26))==421905)

s.add(211*v24+63*v15+176*v5+169*v16+129*v27+146*v40+111*v26+68*v42+39*v25+188*v23+130*v9+(v31*2**6)+91*v41+208*v20+145*v39+247*v18+93*(v22+v17)+71*(v6+2*v11)+103*(v8+2*v30)+6*(v21+10*v28+28*v7+9*v29+19*v2+24*v1+22*v3)+81*v10+70*v4+23*v19==356282)

s.add(v12==v10+2*(v31+4*(v29+v17))+v31+4*(v29+v17))

s.add(94*v42+101*v2+152*v40+200*v7+226*v8+211*v23+121*v24+74*v11+166*v18+((v6+3*v28)*2**6)+41*(4*v9+v21)+23*(v39+11*v41)+7*(v20+10*v25+2*v12+v12)+3*(78*v30+81*v16+55*v27+73*v1+4*v26+v15+85*v3+65*v19)+62*v22+88*v5+110*v4==423091)

s.add(133*v22+175*v15+181*v30+199*v16+123*v27+242*v1+75*v6+69*v2+153*v40+33*v26+100*v42+229*v7+177*v8+134*v31+179*v29+129*v41+14*v10+247*v24+228*v20+92*v11+86*(v9+2*v18)+94*(v23+v21)+37*(v17+4*v3)+79*(v25+2*v28)+72*v5+93*v39+152*v4+214*v19==391869)

s.add(211*v24+213*v18+197*v40+159*v25+117*v21+119*v9+98*v17+218*v41+106*v39+69*v11+43*(v2+v29+2*v2)+116*(v4+v10+2*v26)+5*(v42+9*v23+35*v20+37*v31)+11*(v16+13*v27+5*v5+8*v30)+6*(29*v28+25*v8+38*v22+v15+13*v1+10*v3)+136*v7+142*v6+141*v19==376566)

s.add(173*v3+109*v15+61*v30+187*v1+79*v6+53*v40+184*v21+43*v23+41*v9+166*v31+193*v41+58*v24+146*v10+(v20*2**6)+89*v39+121*v11+5*(v17+23*v8)+7*(29*v18+v29+4*v7)+13*(3*v42+v16+7*v26+13*v2)+3*(v4+83*v5+51*v27+33*v22+8*(v19+4*v28)+18*v25)==300934)

s.add(78*v1+131*v5+185*v16+250*v40+90*v26+129*v42+255*v28+206*v8+239*v25+150*v10+253*v39+104*v22+58*(v2+2*v7)+96*(v15+v31)+117*(v9+2*v4)+27*(v17+8*v18+v18)+19*(v23+3*v21+4*v29+v29)+7*(22*v41+3*(v11+11*v24)+v3+29*v6+14*v27)+109*v20+102*v30+100*v19==401351)

s.add(233*v19+71*v5+209*v27+82*v6+58*v26+53*v25+113*v23+206*v31+39*v41+163*v20+222*v11+191*v18+123*(v7+v40)+69*(v9+2*v22+v22)+9*(v3+8*v24+7*(3*v1+v28)+5*v16+19*v30)+4*(v15+26*v17+61*v29+43*v42+49*v2+32*v4)+10*(7*(v8+3*v21)+v39+12*v10)==368427)

s.add(139*v30+53*v5+158*v16+225*v1+119*v6+67*v2+213*v40+188*v28+152*v8+187*v21+129*v23+54*v9+125*v17+170*v24+184*v11+226*v22+253*v18+26*(v29+v41)+97*(v4+2*v25)+39*(5*v26+v27)+21*(v39+8*v42)+12*(17*v10+v31+15*v7+12*v19)+165*v20+88*v15+157*v3==403881)

s.add(114*v3+61*v27+134*v40+62*v42+89*v9+211*v17+163*v41+66*v24+201*(v7+v18)+47*(5*v16+v22)+74*(v4+v31)+142*(v2+v28)+35*(v20+6*v26)+39*(v15+6*v30)+27*(v25+9*v23+8*v6)+4*(v21+63*v19+2*(v1+12*(v10+v5)+8*v11+26*v29))+10*(v8+4*v39+v39)==382979)

s.add(122*v25+225*v21+52*v23+253*v9+197*v17+187*v31+181*v29+183*v41+47*v20+229*v39+88*v22+127*(v10+2*v18)+37*(v7+3*v3)+((v11+2*v30+v30)*2**6)+7*(21*v8+v27+18*(v4+v1+2*v16))+6*(23*v24+v26+17*v2+39*v6)+10*(v5+11*v28+21*v42)+149*v19+165*v40+121*v15==435695)

s.add(165*v20+223*v4+249*v5+199*v1+135*v2+133*v26+254*v42+111*v7+189*v28+221*v25+115*v21+186*v9+79*v41+217*v24+122*v11+38*v18+109*(2*v31+v29)+14*(v8+17*v40+8*(v6+2*v16))+4*(11*(5*v30+v39)+6*(v10+2*v22)+v27+52*v17+50*v23)+229*v15+86*v3+234*v19==453748)

s.add(181*v25+94*v42+125*v1+226*v26+155*v7+95*v21+212*v17+91*v31+194*v29+98*v24+166*v11+120*v22+59*v18+32*(v9+v8)+158*(v6+v5)+101*(v41+v19)+63*(v4+2*v23)+67*(v28+2*v20)+11*(v39+10*v16+11*v10)+39*(v30+4*(v2+v15))+233*v40+56*v27+225*v3==358321)

s.add(229*v21+135*v4+197*v15+118*v5+143*v16+134*v6+204*v40+173*v26+81*v7+60*v28+58*v8+179*v23+142*v9+178*v17+230*v31+148*v29+224*v41+194*v24+223*v10+87*v20+200*v39+233*v11+49*v22+127*(v25+v30)+31*(4*v27+v18)+42*(v1+6*v2)+109*v42+75*v3+165*v19==456073)

s.add(41*v4+253*v3+163*v15+193*v30+155*v16+113*v27+131*v6+55*v2+21*v40+53*v26+13*v8+201*v25+237*v9+223*v31+95*v24+194*v20+62*v39+119*v11+171*v22+135*v18+69*(v10+3*v28)+211*(v1+v29)+4*(43*v7+v42+40*v17)+6*(v5+33*v41+20*(2*v19+v21)+24*v23)==407135)

s.add(v13==v6+v1+8*v6+4*(v8+2*v27))

s.add(111*v19+190*v3+149*v4+173*v28+118*v23+146*v29+179*v10+51*v20+49*v39+61*v11+125*v22+162*v18+214*(v25+v30)+14*(2*v31+v24)+178*(v41+v16)+11*(4*v9+v21+17*v42)+65*(v26+v17+2*v26+2*v5)+4*(v7+38*v15+4*v13+v13+8*v40+43*v2)==369835)

s.add(27*v27+223*v6+147*v26+13*v21+35*(v17+7*v4)+57*(v19+2*v18+3*v11)+11*(v1+17*(v9+v5)+10*v16+3*v31)+2*(53*v23+v25+38*v15+43*v42+115*v29+61*v22+111*(v10+v40)+14*(v20+v7+2*v7+8*v28)+109*v2+100*v41+63*v8)+93*v39+251*v30+131*v3==393303)

s.add(116*v9+152*v29+235*v20+202*v18+85*(v8+3*v11)+221*(v16+v40)+125*(2*v41+v24)+7*(19*v4+9*(v10+2*v25)+v2+33*v3+32*v19)+3*(71*v39+43*v22+32*(v17+v26)+15*(v5+v6+2*v23)+v28+74*v31+48*v42)+10*(v21+11*v30+16*v15)+136*v7+106*v1+41*v27==403661)

s.add(127*v4+106*v15+182*v30+142*v5+159*v16+17*v1+211*v6+134*v2+199*v7+103*v28+247*v23+122*v9+95*v41+62*v10+203*v39+16*v11+41*(6*v42+v25)+9*(22*v24+v20+27*v31+28*v40)+10*(v8+v22+3*v21+8*v17+2*(v22+3*v21+8*v17)+13*v29)+6*(23*v27+v26)+213*v18+179*v3+43*v19==418596)

s.add(149*v19+v1+133*v22+207*v41+182*v26+234*v7+199*v8+168*v21+58*v10+108*v20+142*v18+156*(v9+v25)+16*(v29+6*v31)+126*(v17+2*v39)+127*(v4+2*v27+v40)+49*(v30+4*v16)+11*(v5+22*v11)+5*(v15+v42+45*v24+50*v28)+109*v2+124*v6+123*v3==418697)

flag = []
result = s.model()
vars = (v5,v28,v11,v10,v24,v41,v22,v40,v7,v18,v29,v27,v9,v31,v30,v20,v23,v39,v15,v21,v26,v2,v42,v6,v16,v1,v17,v25,v8,v4,v19,v3)
for var in vars:
    flag.append(result[var].as_long())
print(bytes(flag).decode())

change
#

hook,有点像回调函数

两个加密函数(隔一个字节)交替进行加密。

data = [0x13, 0x0A, 0x5D, 0x1C, 0x0E, 0x08, 0x23, 0x06, 0x0B, 0x4B, 0x38, 0x22, 0x0D, 0x1C, 0x48, 0x0C, 
    0x66, 0x15, 0x48, 0x1B, 0x0D, 0x0E, 0x10, 0x4F]
key=b"am2qasl"
flag = []
for i in range(len(data)):
    if(i % 2 == 0):
        result = (data[i] -10) ^ key[i % len(key)]
    else:
        result = data[i] ^ key[i % len(key)]
    flag.append(result)
print(bytes(flag))

相关文章

0xGame2023 Reverse
·591 字·3 分钟
CTF Reverse
reverse writeup of 0xgame2023
0xGame2023 Crypto
·1338 字·7 分钟
CTF Crypto
crypto writeup of 0xgame2023