buu逆向刷题(五)

    科技2022-07-13  155

    1.[MRCTF2020]Xor

    试运行一下,错误会提示wrong,IDA打开,shift+f12搜字符串wrong,找到关键函数sub_401090。F5反编译失败,某地址报错:call指令识别不了。

    百度一手资料,两种可能; 1、IDA无法识别出正确的调用约定。 2、IDA无法识别出正确的参数个数。

    看call调用的函数,发现只需要一个参数即可 ,所以是参数问题,选中call调用的函数,按y,修改参数为1个(去调后面的char)。F5一下,简单的异或。

    tmp=[0x4D,0x53, 0x41, 0x57, 0x42, 0x7E, 0x46, 0x58, 0x5A, 0x3A,0x4A,0x3A, 0x60, 0x74, 0x51, 0x4A, 0x22, 0x4E, 0x40, 0x20,0x62, 0x70, 0x64, 0x64, 0x7D, 0x38, 0x67] flag=[0 for i in range(27)] for i in range(27): flag[i]=i^tmp[i] print(''.join(map(chr,flag))) #MRCTF{@_R3@1ly_E2_R3verse!}

    2.[GKCTF2020]BabyDriver

    披着驱动皮的maze 主函数标准的键盘过滤函数。 重点放在IRP读函数,点进去发现一个函数sub_140001380。 再进入sub_140001380,发现是个地图题。上下左右需用键盘扫描码对应的字母代替。 md5加密就是flag。

    3.[FlareOn6]Overlong

    IDA打开,一共三个函数,提示里说flag被藏起来了,函数看着挺正常的,就是一大串数据只传入了一部分,结合弹窗提示I never broke the encoding,考虑将所有数据传入,看最终加密后的结果。 X64dbg打开,修改机器码,传入的参数值。 运行就跑了出来。

    4.[MRCTF2020]hello_world_go

    go的逆向,IDA直接打开,找到main函数,看函数猜逻辑,直接明文比较

    5.[WUSTCTF2020]level3

    main函数base64,base64考换表base64,所以找换表函数 所以找到换表函数,写脚本换表。

    B=[0x41,0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F] for i in range(10): v0=B[i] B[i]=B[19-i] B[19-i]=v0 print(''.join(map(chr,B))) #TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

    换表base64解密。wctf2020{Base64_is_the_start_of_reverse}

    6.[GXYCTF2019]simple CPP

    开始看着很乱,主要是自己心里没有这样的模型。 整体分四部分。

    1.输入字符和v7异或后的值放到v3,v7动调可得。
    2.判断输入长度在0-30
    3.将输入字符异或后的值的每个字符的ascii值8个为一组,分成四组,最后一组只有32位,堆叠成一个64位整型数。字符里的第一个值由于每次左移8位而放到最高位,但由于中国人从左向右读取方法。最后求出整型数,按从左到右,每两个16进制组成一个字符,即可求出输入字符异或后的值
    4.这四个整型数运算,z3可解。

    考点:将字符转成整型数处理:移位堆叠。

    from z3 import * x,y,z,w=BitVecs('x y z w',64) s=Solver() s.add((~x)&z==1176889593874) s.add(((z&~x)|(x&y)|(z&(~y))|(x&(~y)))^w==4483974543195470111) s.add(((z&~y)&x|z&((x&y)|y&~x|~(y|x)))==577031497978884115) s.add(((z&~x)|(x&y)|(z&~y)|(x&~y))==4483974544037412639) s.add(((z&(~x)) | (x&y) | y & z) == (((~x)& z)|864693332579200012)) s.check() m = s.model() for i in m: print("%s = 0x%x"%(i,m[i].as_long())) #w = 0x32310600 #z = 0x8020717153e3013 #y = 0xc00020130082c0c #x = 0x3e3a460533286f0d Dst = 'i_will_check_is_debug_or_noi_wil' flag=[0x3E,0x3A,0x46,0x05,0x33,0x28,0x6F,0x0D,0x8C,0x00,0x8A,0x09,0x78,0x49,0x2C,0xAC,0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13,0x32,0x31,0x06] s='' for i in range(len(flag)): s+=chr(ord(Dst[i]) ^ flag[i]) print(s) #We1l_D0näeéb' _ólgebra_am_i

    求出来的flag有乱码,当时题目提示了e!P0or_a。所以最终We1l_D0ne!P0or_algebra_am_i

    7.[WUSTCTF2020]Cr0ssfun

    每个字符的明文比较,找一张纸,写下来得到flag

    8.[2019红帽杯]xx

    __int64 __fastcall sub_1400011A0(__int64 a1, __int64 a2) { unsigned __int64 v2; // rbx signed __int64 v3; // rax __int128 *v4; // rax __int64 v5; // r11 __int128 *v6; // r14 int v7; // edi __int128 *v8; // rsi char v9; // r10 int v10; // edx __int64 v11; // r8 unsigned __int64 v12; // rcx signed __int64 v13; // rcx unsigned __int64 v14; // rax unsigned __int64 i; // rax _BYTE *v16; // rax size_t v17; // rsi _BYTE *v18; // rbx _BYTE *v19; // r9 signed int v20; // er11 char *v21; // r8 signed __int64 v22; // rcx char v23; // al signed __int64 v24; // r9 signed __int64 v25; // rdx __int64 v26; // rax size_t Size; // [rsp+20h] [rbp-48h] __int128 v29; // [rsp+28h] [rbp-40h] int v30; // [rsp+38h] [rbp-30h] int v31; // [rsp+3Ch] [rbp-2Ch] int input[4]; // [rsp+40h] [rbp-28h] int v33; // [rsp+50h] [rbp-18h] *(_OWORD *)input = 0i64; v33 = 0; sub_1400018C0(std::cin, a2, input); // 获取输入 v2 = -1i64; v3 = -1i64; do ++v3; while ( *((_BYTE *)input + v3) ); // v3为输入的长度 if ( v3 != 19 ) // 输入长度==19 { sub_140001620(std::cout, "error\n"); _exit((unsigned __int64)input); } v4 = (__int128 *)sub_140001E5C(5ui64); // malloc 申请一个长度为5的空间 v5 = *(_QWORD *)&Code; // qwertyuiopasdfghjklzxcvbnm1234567890 v6 = v4; // 申请的新空间地址给v6和v8,给两份,有一份是保留原地址 v7 = 0; v8 = v4; do { v9 = *((_BYTE *)v8 + (char *)input - (char *)v4);// input的每个字符给v9 v10 = 0; *(_BYTE *)v8 = v9; // v8为输入的每个字符 v11 = 0i64; v12 = -1i64; do ++v12; while ( *(_BYTE *)(v5 + v12) ); // v12是Code字符串的长度 if ( v12 ) { do { if ( v9 == *(_BYTE *)(v5 + v11) ) // 结合下面来看,要想让程序不退出,v10<v12,v9(input)里的字符一定在v5(Code)里 break; ++v10; ++v11; } while ( v10 < v12 ); } v13 = -1i64; do ++v13; while ( *(_BYTE *)(v5 + v13) ); // v13是Code的长度 if ( v10 == v13 ) _exit(v5); v8 = (__int128 *)((char *)v8 + 1); // v8的地址+1,推动input的地址+1 } while ( (char *)v8 - (char *)v4 < 4 ); // 前4位被验证 *((_BYTE *)v4 + 4) = 0; // 第5位赋值0 do ++v2; while ( *((_BYTE *)input + v2) ); // v2=input的长度 v14 = 0i64; v29 = *v6; while ( *((_BYTE *)&v29 + v14) ) { if ( !*((_BYTE *)&v29 + v14 + 1) ) { ++v14; break; } if ( !*((_BYTE *)&v29 + v14 + 2) ) { v14 += 2i64; break; } if ( !*((_BYTE *)&v29 + v14 + 3) ) { v14 += 3i64; break; } v14 += 4i64; if ( v14 >= 0x10 ) // v14+4 即v14=4 break; } // i=5 for ( i = v14 + 1; i < 16; ++i ) // v29后面的位置填0,flag+12个0 凑够xxtea的16个字节的秘钥 *((_BYTE *)&v29 + i) = 0; v16 = sub_140001AB0((__int64)input, v2, (unsigned __int8 *)&v29, &Size);// xxtea v17 = Size; v18 = v16; v19 = sub_140001E5C(Size); // 动态分配一块加密后长度的内存 v20 = 1; *v19 = v18[2]; v21 = v19 + 1; v19[1] = *v18; v19[2] = v18[3]; v19[3] = v18[1]; v19[4] = v18[6]; v19[5] = v18[4]; v19[6] = v18[7]; v19[7] = v18[5]; v19[8] = v18[10]; v19[9] = v18[8]; v19[10] = v18[11]; v19[11] = v18[9]; v19[12] = v18[14]; v19[13] = v18[12]; v19[14] = v18[15]; v19[15] = v18[13]; v19[16] = v18[18]; v19[17] = v18[16]; v19[18] = v18[19]; v19[19] = v18[17]; v19[20] = v18[22]; v19[21] = v18[20]; v19[22] = v18[23]; // 返回的结果重新排序 for ( v19[23] = v18[21]; v20 < v17; ++v21 ) //异或运算的逻辑就找一张纸,拿出笔,写几个找规律。 { v22 = 0i64; if ( v20 / 3 > 0 ) { v23 = *v21; do { v23 ^= v19[v22++]; *v21 = v23; } while ( v22 < v20 / 3 ); } ++v20; } *(_QWORD *)&v29 = 0xC0953A7C6B40BCCEi64; v24 = v19 - (_BYTE *)&v29; *((_QWORD *)&v29 + 1) = 0x3502F79120209BEFi64; v25 = 0i64; v30 = 0xC8021823; v31 = 0xFA5656E7; do { if ( *((_BYTE *)&v29 + v25) != *((_BYTE *)&v29 + v25 + v24) ) _exit(v7 * v7); ++v7; ++v25; } // v25推动比较 while ( v25 < 24 ); v26 = sub_140001620(std::cout, "You win!"); std::basic_ostream<char,std::char_traits<char>>::operator<<(v26, sub_1400017F0); return 0i64; }

    流程; 1.取输入字符的前4个(flag)作为秘钥 2.将输入字符和秘钥(flag+12个0)xxtea加密 3.加密后的密文变换位置 4.关系较复杂的异或

    #逆向求xxtea加密后的密文 data0 = "CEBC406B7C3A95C0EF9B202091F70235231802C8E75656FA"#为提取出来的v29,v29+1,v30,v31 data = [] for i in range(0,len(data0),2): data.append(int(data0[i]+data0[i+1],16))#每两位为整体,将16进制转换为10进制 print(data) for i in range(len(data)-1,-1,-1): for j in range(i//3): data[i] ^= data[j] #进行的异或操作 print(data) biao = [2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21]#置换表 shuju = [0 for i in range(24)] for i in range(24): shuju[biao[i]] = data[i]#将其按照一定顺序置换 s=''.join(hex(i)[2:] for i in shuju) print(shuju) print(s) print(len(shuju))

    小端存储 密文:bca5ce40f4b2b2e7a9129d12ae10c85b3dd7061ddc70f8dc key:flag

    xxtea算法识别: tea 2个异或 xtea 2个异或+&3 xxtea 6个异或+&3

    //xxtea脚本解密 #include <stdio.h> #include <stdint.h> #include<windows.h> #define DELTA 0x9e3779b9 #define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z))) void btea(uint32_t *v, int n, uint32_t const key[4]) { uint32_t y, z, sum; unsigned p, rounds, e; if (n > 1) /* Coding Part */ { rounds = 6 + 52 / n; sum = 0; z = v[n - 1]; do { sum += DELTA; e = (sum >> 2) & 3; for (p = 0; p < n - 1; p++) { y = v[p + 1]; z = v[p] += MX; } y = v[0]; z = v[n - 1] += MX; } while (--rounds); } else if (n < -1) /* Decoding Part */ { n = -n; rounds = 6 + 52 / n; sum = rounds * DELTA; y = v[0]; do { e = (sum >> 2) & 3; for (p = n - 1; p > 0; p--) { z = v[p - 1]; y = v[p] -= MX; } z = v[n - 1]; y = v[0] -= MX; sum -= DELTA; } while (--rounds); } } int main() { //03e0164dd30553aa // uint32_t a[2] = { (unsigned int)0xd2cfdad7, 0x9ac8d5d4 }; uint32_t v[6] = { (unsigned int)0x40cea5bc, (unsigned int)0xe7b2b2f4,(unsigned int)0x129d12a9,(unsigned int)0x5bc810ae,(unsigned int)0x1d06d73d,(unsigned int)0xdcf870dc }; uint32_t const k[4] = { (unsigned int)0x67616c66, (unsigned int)0x0, (unsigned int)0X0, (unsigned int)0x0 }; int n = 6; //n的绝对值表示v的长度,取正表示加密,取负表示解密 // v为要加密的数据是两个32位无符号整数 // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 //printf("加密前原始数据:%x %x\n", v[0], v[1]); btea(v, -n, k); printf("加密后的数据:%x %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5]); //btea(v, -n, k); //printf("解密后的数据:%x %x\n", v[0], v[1]); system("pause"); }

    小端存储组合 666c61677b4358585f616e645f2b2b7465617d flag{CXX_and_++tea} 大家都是整数还比较好理解,有整数有字符就易理解混。 字符总要转成数字来运算。 前16位字符有的是ascii值有的是它本身,除前16位的其他字符是ascii码 ascii码还是好转的,前16位字符若想转成本身就需要下面的脚本 字符串里的16进制的字符转成与它长相一样的10进制整数

    result = 'CEBC406B7C3A95C0EF9B202091F70235231802C8E75656FA' res = [] for x in range(0,len(result),2): res.append(int(('0x'+result[x]+result[x+1]), 16)) print(res)
    Processed: 0.011, SQL: 8