目录
122_[b01lers2020]little_engine
头一回见着这样的题,IDA看了半天没找着有用的东西,试着运行一下发现是个安装程序,运行后会释放出一个32位程序。程序非常简单,直接对输入异或后比较
- BOOL start()
- {
- int v0; // ecx
- HANDLE StdHandle; // [esp+4h] [ebp-Ch]
- HANDLE hFile; // [esp+8h] [ebp-8h]
- DWORD NumberOfBytesWritten; // [esp+Ch] [ebp-4h] BYREF
-
- StdHandle = GetStdHandle(0xFFFFFFF6);
- hFile = GetStdHandle(0xFFFFFFF5);
- WriteFile(hFile, aLetSStartOutEa, 0x2Au, &NumberOfBytesWritten, 0);
- ReadFile(StdHandle, byte_402158, 0x32u, &NumberOfBytesWritten, 0);
- v0 = 0;
- while ( ((unsigned __int8)byte_402158[v0] ^ 0x7D) == byte_402140[v0] )
- {
- if ( ++v0 >= 24 )
- return WriteFile(hFile, aYouAreSuccess, 0x12u, &NumberOfBytesWritten, 0);
- }
- return WriteFile(hFile, aYouAreFailure, 0x12u, &NumberOfBytesWritten, 0);
- }
所以可以直接解密
- #先运行程序,选择输入文件目录后会生成一个32位程序然后退出,处理生成的程序
-
- c = bytes.fromhex('1F08131304220E114D0D183D1B111C0F18501213531E1210')
- print(bytes([i^0x7d for i in c]))
- #bunny_sl0pe@flare-on.com
- #flag{bunny_sl0pe@flare-on.com}
IDA里main里进行加密和检查
- __int64 __fastcall main(__int64 a1, char **a2, char **a3)
- {
- void *vars0[5]; // [rsp+0h] [rbp+0h] BYREF
-
- vars0[3] = (void *)__readfsqword(0x28u);
- sub_16B0();
- sub_1830((__int64)vars0);
- sub_1510((__int64 *)vars0); // 对输入加密
- if ( (unsigned __int8)sub_15A0(vars0) )
- std::__ostream_insert<char,std::char_traits<char>>(
- std::cout,
- "Chugga chugga choo choo you're the little engine that CAN!",
- 58LL);
- else
- std::__ostream_insert<char,std::char_traits<char>>(
- std::cout,
- "I guess you don't know anything about trains...go do some TRAINing you non-conductor :(",
- 87LL);
- std::endl<char,std::char_traits<char>>(std::cout);
- if ( vars0[0] )
- operator delete(vars0[0]);
- return 0LL;
- }
1510里进行加密
- unsigned __int64 __fastcall sub_1510(__int64 *a1)
- {
- ......
- v2 = *a1;
- if ( *a1 != a1[1] )
- {
- v3 = 0LL;
- v4 = -111;
- do
- {
- v5 = (_BYTE *)(v3 + v2);
- ......
- *v5 ^= v4;
- v6 = v4 + v3++;
- v2 = *a1;
- v4 = v6 + v6 / 0xFF;
- }
- while ( v3 < a1[1] - *a1 );
- }
- return __readfsqword(0x28u) ^ v8;
- }
不复杂
- data = open('engine', 'rb').read()
-
- v4 = -111 & 0xff
- t = []
- for i in range(75):
- t.append( v4^data[0x2220+ 4*i] )
- v6 = v4+i
- v4 = (v6 + v6//0xff )&0xff
-
- print(bytes(t))
- #pctf{th3_m0d3rn_st34m_3ng1n3_w45_1nv3nt3d_1n_1698_buT_th3_b3st_0n3_in_1940}
- #flag{th3_m0d3rn_st34m_3ng1n3_w45_1nv3nt3d_1n_1698_buT_th3_b3st_0n3_in_1940}
主程序是2000行,懒得看了,看了WP说是xxtea加密,因为用了SEH,这块也找不着怎么弄的,从搜到的WP里找到一个c程序
- #include <stdio.h>
-
- #include <stdint.h>
-
- void XXTeaDecrypt(int n, uint32_t* v, uint32_t const key[4])
- {
- uint32_t y, z, sum;
- unsigned p, rounds, e;
- uint32_t DELTA = 0x33445566;
- 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] -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
- }
- z = v[n - 1];
- y = v[0] -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
- sum -= DELTA;
- } while (--rounds);
- }
-
- int main()
- {
- uint8_t enc_data[] = { 0x5c, 0xab, 0x3c, 0x99, 0x29, 0xe1, 0x40, 0x3f, 0xde, 0x91, 0x77, 0x77, 0xa6, 0xfe, 0x7d, 0x73, 0xe6, 0x59, 0xcf, 0xec, 0xe3, 0x4c, 0x60, 0xc9, 0xa5, 0xc0, 0x82, 0x96, 0x1e, 0x2a, 0x6f, 0x55, 0};
- uint32_t key[] = { 14000, 79894, 16, 123123 };
-
- XXTeaDecrypt(8, (uint32_t*)enc_data, key);
-
- puts((char*)enc_data); //9b34a61df773acf0e4dec25ea5fb0e29
- return 0;
- }
这个也确实比较复杂,看了半天跟着一点点调
这里用了int3动调,执行到int3时调用函数修改代码
程序里涉及到ini3的地方有3次,第1处在main里,具体内容到int3就完了,根据函数里的数据修改这里的7字节指向下部的程序
- 的v16前7个修改sub_408A40中int3开始的7字节,得到调用加密函数和检查函数
- .text:00408AF5 89 45 F8 mov [ebp+var_8], eax
- .text:00408AF8 CC int 3 ; Trap to Debugger
- .text:00408AF9 90 nop
- .text:00408AFA 90 nop
- .text:00408AFB 90 nop
- .text:00408AFC 90 nop
- .text:00408AFD 90 nop
- .text:00408AFE 90 nop
- .text:00408AFF 68 80 1E 4C 00 push offset aYouAreTooShort ; "you are too short!"
-
-
- .text:00408AF8 90 nop
- .text:00408AF9 83 7D F8 18 cmp [ebp+var_8], 18h
- .text:00408AFD 7D 11 jge short loc_408B10 跑到下方执行加密和检查
- .text:00408AFD
第2块 sub_4087e0 408824开始的30字节
第3块检查 sub_4083c0 408432开始5字节,没有patch的内容,向下40845A开始找到对应比较数据 408635开始为数据
408536这块是密文,然后按原来得程序进行逆向,乘的逆向用逆
- iv=0x1234
- inv=12 #gmpy2.invert(41,491)
- c=[0x3d1,0x2f0,0x52,0x475,0x1d2,0x2f0,0x224,0x51c,0x4e6,0x29f,0x2ee,0x39b,0x3f9,0x32b,0x2f2,0x5b5,0x24c,0x45a,0x34c,0x56d,0xa,0x4e6,0x476,0x2d9]
- key=[2,3,7,14,30,57,120,251] #sub_408A40
- flag=[]
- for i in range(len(c)):
- t=c[i]*inv%491
- p=""
- for i in range(8):
- if key[7-i]>t:
- p+="0"
- else:
- p+="1"
- t-=key[7-i]
- flag.append(int(p[::-1],2))
- print(chr((flag[0]^0x1234)&0xff),end="")
- for i in range(1,len(flag)):
- print(chr((flag[i]^c[i-1])&0xff),end="")
-
- #swpuctf{y0u_@re_s0_coo1}
- #flag{y0u_@re_s0_coo1}
这个是linux下的动调,输入一个数据然后往下跟,第一次处理是每4字符进行一个顺序交换,第二次是每4个分别加上4个数,第3次是每组中字符前几位和后几位互换。然后与密文比较
动调时得到密文
- gdb-peda$ x/32c 0x7ffff781c0a0
- 0x7ffff781c0a0: 0xda 0xd8 0x3d 0x4c 0xe3 0x63 0x97 0x3d
- 0x7ffff781c0a8: 0xc1 0x91 0x97 0xe 0xe3 0x5c 0x8d 0x7e
- 0x7ffff781c0b0: 0x5b 0x91 0x6f 0xfe 0xdb 0xd0 0x17 0xfe
- 0x7ffff781c0b8: 0xd3 0x21 0x99 0x4b 0x73 0xd0 0xab 0xfe
然后向回处理
- a=bytes.fromhex("DAD83D4CE363973DC191970EE35C8D7E5B916FFEDBD017FED321994B73D0ABFE")
- flag = ''
- for i in range(0, len(a), 4):
- d1 = a[i]
- d1 = (d1>>3)|(d1<<5) & 0xff
- d1 = (d1 - 7)& 0xff
-
- d2 = a[i+1]
- d2 = (d2>>6)|(d2<<2) & 0xff
- d2 = (d2 - 18)& 0xff
-
- d3 = a[i+2]
- d3 = (d3>>1)|(d3<<7) & 0xff
- d3 = (d3 - 88)& 0xff
-
- d4 = a[i+3]
- d4 = (d4>>4)|(d4<<4) & 0xff
- d4 = (d4-129)& 0xff
- flag+=chr(d2)+chr(d4)+chr(d1)+chr(d3)
-
- print(flag)
- #QCTF{Rus4_1s_fun4nd_1nt3r3st1ng}
- #flag{Rus4_1s_fun4nd_1nt3r3st1ng}