REVERSE-PRACTICE-BUUCTF-15
- [2019红帽杯]xx
- [ACTF新生赛2020]Universe_final_answer
- [WUSTCTF2020]level4
- findKey
[2019红帽杯]xx
exe程序,运行后直接输入,无壳,ida分析
交叉引用字符串“You win!”来到sub_1400011A0函数
主要的逻辑为
读取输入,验证输入长度是否为19,且均为小写字母或0~9的数字
对输入进行TEA加密,加密密钥通过调试可知是输入的前4个字符,即“flag”
TEA加密的密文变换位置
变换位置后的密文循环异或
循环异或后的密文与已知字节比较
__int64 __fastcall sub_1400011A0(__int64 a1, __int64 a2)
{unsigned __int64 input_len_; // rbxsigned __int64 input_len; // rax__int128 *v4; // rax__int64 Code; // r11__int128 *v6; // r14int v7; // edi__int128 *v8; // rsichar v9; // r10int v10; // edx__int64 v11; // r8unsigned __int64 Code_len; // rcxsigned __int64 Code_len_; // rcxunsigned __int64 v14; // raxunsigned __int64 i; // rax_BYTE *cipher; // raxsize_t v17; // rsi_BYTE *cipher_; // rbx_BYTE *v19; // r9signed int v20; // er11char *v21; // r8signed __int64 v22; // rcxchar v23; // alsigned __int64 v24; // r9signed __int64 v25; // rdx__int64 v26; // raxsize_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); // 读取输入input_len_ = -1i64;input_len = -1i64;do // do循环计算输入的长度++input_len;while ( *((_BYTE *)input + input_len) );if ( input_len != 19 ) // 验证输入的长度是否为19{sub_140001620(std::cout, "error\n");_exit((unsigned __int64)input);}v4 = (__int128 *)sub_140001E5C(5ui64);Code = *(_QWORD *)&::Code; // Code=="qwertyuiopasdfghjklzxcvbnm1234567890"v6 = v4;v7 = 0;v8 = v4; // v4==v6==v8do{v9 = *((_BYTE *)v8 + (char *)input - (char *)v4);v10 = 0;*(_BYTE *)v8 = v9;v11 = 0i64;Code_len = -1i64;do++Code_len;while ( *(_BYTE *)(Code + Code_len) );if ( Code_len ){do{if ( v9 == *(_BYTE *)(Code + v11) )break;++v10;++v11;}while ( v10 < Code_len );}Code_len_ = -1i64;do++Code_len_;while ( *(_BYTE *)(Code + Code_len_) );if ( v10 == Code_len_ ) // 验证输入的内容是否在Code范围内,即输入的内容均为小写字母或0~9的数字_exit(Code);v8 = (__int128 *)((char *)v8 + 1);}while ( (char *)v8 - (char *)v4 < 4 );*((_BYTE *)v4 + 4) = 0;do++input_len_;while ( *((_BYTE *)input + input_len_) );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 )break;}for ( i = v14 + 1; i < 16; ++i )*((_BYTE *)&v29 + i) = 0;cipher = sub_140001AB0((__int64)input, input_len_, (unsigned __int8 *)&v29, &Size);// 在sub_140001Ab0函数中发现一常数0x61C88647,判断为TEA加密算法// v29为密钥,调试可知为输入的前4个字符,即“flag”v17 = Size;cipher_ = cipher;v19 = sub_140001E5C(Size);v20 = 1;*v19 = cipher_[2]; // 输入经TEA加密后的密文cipher,变换位置,存储到v19v21 = v19 + 1;v19[1] = *cipher_;v19[2] = cipher_[3];v19[3] = cipher_[1];v19[4] = cipher_[6];v19[5] = cipher_[4];v19[6] = cipher_[7];v19[7] = cipher_[5];v19[8] = cipher_[10];v19[9] = cipher_[8];v19[10] = cipher_[11];v19[11] = cipher_[9];v19[12] = cipher_[14];v19[13] = cipher_[12];v19[14] = cipher_[15];v19[15] = cipher_[13];v19[16] = cipher_[18];v19[17] = cipher_[16];v19[18] = cipher_[19];v19[19] = cipher_[17];v19[20] = cipher_[22];v19[21] = cipher_[20];v19[22] = cipher_[23];for ( v19[23] = cipher_[21]; v20 < v17; ++v21 )// v20初始值为1,v17==Size==24,v21初始值为&(v19[1]),v20和v21是同时加1的{v22 = 0i64;if ( v20 / 3 > 0 ) // 决定下面异或运算的次数{v23 = *v21; // 取指针v21指向的值do{v23 ^= v19[v22++]; // 实际上为 *v21^=v19[v22++],而v21是指向v19的,意味着当v20大于等于3时,v19[v20]从v19[0]异或到v19[v20/3-1]// 即v19会变化,不再是简单的TEA加密的密文*v21 = v23;}while ( v22 < v20 / 3 );}++v20;}*(_QWORD *)&v29 = 0xC0953A7C6B40BCCEi64; // v29是最后要去比较的结果,提取时注意小端序v24 = v19 - (_BYTE *)&v29;*((_QWORD *)&v29 + 1) = 0x3502F79120209BEFi64;v25 = 0i64;v30 = 0xC8021823;v31 = 0xFA5656E7;do{if ( *((_BYTE *)&v29 + v25) != *((_BYTE *)&v29 + v25 + v24) )// v19和v29比较验证_exit(v7 * v7);++v7;++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;
}
先写逆循环异或和逆位置变换得到TEA加密的密文脚本
网上找的xxtea的python代码,参考:XXTEA 加解密 as3 和 Python 分别实现
密钥为“flag”需要加长到16,写解xxtea脚本即可得到flag
# encoding: utf-8
import struct_DELTA = 0x9E3779B9def _long2str(v, w):n = (len(v) - 1) << 2if w:m = v[-1]if (m < n - 3) or (m > n): return ''n = ms = struct.pack('<%iL' % len(v), *v)return s[0:n] if w else sdef _str2long(s, w):n = len(s)m = (4 - (n & 3) & 3) + ns = s.ljust(m, "\0")v = list(struct.unpack('<%iL' % (m >> 2), s))if w: v.append(n)return vdef encrypt(str, key):if str == '': return strv = _str2long(str, True)k = _str2long(key.ljust(16, "\0"), False)n = len(v) - 1z = v[n]y = v[0]sum = 0q = 6 + 52 // (n + 1)while q > 0:sum = (sum + _DELTA) & 0xffffffffe = sum >> 2 & 3for p in range(n):y = v[p + 1]v[p] = (v[p] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffffz = v[p]y = v[0]v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffffz = v[n]q -= 1return _long2str(v, False)def decrypt(str, key):if str == '': return strv = _str2long(str, False)k = _str2long(key.ljust(16, "\0"), False)n = len(v) - 1z = v[n]y = v[0]q = 6 + 52 // (n + 1)sum = (q * _DELTA) & 0xffffffffwhile (sum != 0):e = sum >> 2 & 3for p in range(n, 0, -1):z = v[p - 1]v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffffy = v[p]z = v[n]v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffffy = v[0]sum = (sum - _DELTA) & 0xffffffffreturn _long2str(v, True)def xor(x ,y):return ord(x) ^ ord(y)key="flag"+'\x00'*12
cipher_data=['\xbc','\xa5','\xce','\x40','\xf4','\xb2','\xb2','\xe7','\xa9','\x12','\x9d','\x12','\xae','\x10','\xc8','\x5b','\x3d','\xd7','\x06','\x1d','\xdc','\x70','\xf8','\xdc']
cipher=''.join(cipher_data)
print(decrypt(cipher,key))
#flag{CXX_and_++tea}
[ACTF新生赛2020]Universe_final_answer
elf文件,无壳,ida分析
main函数,读取输入,check函数验证输入的字符,输入验证成功,程序调用sub_C50,最后输出flag
重要的是check函数,分析可知,验证input的字符,就是验证方程组是否成立
利用python的z3库写脚本得到input
from z3 import *
flag=[Int('flag[%d]' %i ) for i in range(10)]
s=Solver()
s.add(-85 * flag[8] + 58 * flag[7] + 97 * flag[6] + flag[5] + -45 * flag[4] + 84 * flag[3] + 95 * flag[0] - 20 * flag[1] + 12 * flag[2] == 12613)
s.add(30 * flag[9] + -70 * flag[8] + -122 * flag[6] + -81 * flag[5] + -66 * flag[4] + -115 * flag[3] + -41 * flag[2] + -86 * flag[1] - 15 * flag[0] - 30 * flag[7] == -54400)
s.add(-103 * flag[9] + 120 * flag[7] + 108 * flag[5] + 48 * flag[3] + -89 * flag[2] + 78 * flag[1] - 41 * flag[0] + 31 * flag[4] - (flag[6]*64) - 120 * flag[8] == -10283)
s.add(71 * flag[6] + (flag[5] *128) + 99 * flag[4] + -111 * flag[2] + 85 * flag[1] + 79 * flag[0] - 30 * flag[3] - 119 * flag[7] + 48 * flag[8] - 16 * flag[9] == 22855)
s.add(5 * flag[9] + 23 * flag[8] + 122 * flag[7] + -19 * flag[6] + 99 * flag[5] + -117 * flag[4] + -69 * flag[2] + 22 * flag[1] - 98 * flag[0] + 10 * flag[3] == -2944)
s.add(-54 * flag[9] + -23 * flag[7] + -82 * flag[2] + -85 * flag[0] + 124 * flag[1] - 11 * flag[3] - 8 * flag[4] - 60 * flag[5] + 95 * flag[6] + 100 * flag[8] == -2222)
s.add(-83 * flag[9] + -111 * flag[5] + -57 * flag[0] + 41 * flag[1] + 73 * flag[2] - 18 * flag[3] + 26 * flag[4] + 16 * flag[6] + 77 * flag[7] - 63 * flag[8] == -13258)
s.add(81 * flag[9] + -48 * flag[8] + 66 * flag[7] + -104 * flag[6] + -121 * flag[5] + 95 * flag[4] + 85 * flag[3] + 60 * flag[2] + -85 * flag[0] + 80 * flag[1] == -1559)
s.add(101 * flag[9] + -85 * flag[8] + 7 * flag[6] + 117 * flag[5] + -83 * flag[4] + -101 * flag[3] + 90 * flag[2] + -28 * flag[1] + 18 * flag[0] - flag[7] == 6308 )
s.add(99 * flag[9] + -28 * flag[8] + 5 * flag[7] + 93 * flag[6] + -18 * flag[5] + -127 * flag[4] + 6 * flag[3] + -9 * flag[2] + -93 * flag[1] + 58 * flag[0] == -1697)
if s.check()==sat:print(s.model())
运行结果
转成字符串,运行elf文件,输入,得到flag
[WUSTCTF2020]level4
elf文件,运行后输出了几段字符串,可知是二叉树的遍历,无壳,ida分析
main函数,程序输出的type1为中序遍历,type2为后序遍历,type3被注释掉,应该是先序遍历
中序遍历:2f0t02T{hcsiI_SwA__r7Ee}
后序遍历:20f0Th{2tsIS_icArE}e7__w
由中序遍历和后序遍历的字符串确定二叉树,再先序遍历二叉树,即为flag
代码参考:已知后序序遍历序列和中序遍历序列建立二叉树
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char ElementType;
typedef struct BiTNode {ElementType data;struct BiTNode* lchild;struct BiTNode* rchild;
}BiTNode, *BiTree;BiTree CreatBinTree(char* post, char* in, int n);
void preorder(BiTree T);int main()
{BiTree T;char postlist[100] = "20f0Th{2tsIS_icArE}e7__w";char inlist[100] = "2f0t02T{hcsiI_SwA__r7Ee}";int length;length = strlen(postlist);T = CreatBinTree(postlist, inlist, length);preorder(T);return 0;
}
void preorder(BiTree T)
{if (T){printf("%c", T->data);preorder(T->lchild);preorder(T->rchild);}
}
BiTree CreatBinTree(char* post, char* in, int n)
{BiTree T;int i;if (n <= 0) return NULL;T = (BiTree)malloc(sizeof(BiTNode));T->data = post[n - 1];for (i = 0; in[i] != post[n - 1]; i++);T->lchild = CreatBinTree(post, in, i);T->rchild = CreatBinTree(post + i, in + i + 1, n - 1 - i);return T;
}
运行结果即为flag
findKey
exe程序,无壳,ida分析
交叉引用字符串“flag{}”,发现从地址0x00401640开始,代码就没有被ida识别,应该是加了花指令
在地址0x00401918和地址0x0040191D处,两条一样的指令,放在了一起,nop掉第二条push指令
在地址0x00401640处右键->create function,或者选中全部的红色地址代码,按p创建函数,F5反汇编
主要的逻辑为
v20的字符串和v16的字符串循环异或,结果和pbData的md5散列值比较
比较相等时,md5散列前的pbData和unk_423030循环异或,结果即为flag
LRESULT __stdcall sub_401640(HWND hWndParent, UINT Msg, WPARAM wParam, LPARAM lParam)
{int v5; // eaxsize_t v6; // eaxDWORD v7; // eaxint v8; // eaxint v9; // eaxconst char *v10; // [esp-4h] [ebp-450h]CHAR *v11; // [esp+0h] [ebp-44Ch]int v12; // [esp+4h] [ebp-448h]int v13; // [esp+4Ch] [ebp-400h]UINT v14; // [esp+50h] [ebp-3FCh]CHAR v15; // [esp+54h] [ebp-3F8h]CHAR v16[2]; // [esp+154h] [ebp-2F8h]int v17; // [esp+157h] [ebp-2F5h]__int16 v18; // [esp+15Bh] [ebp-2F1h]char v19; // [esp+15Dh] [ebp-2EFh]char v20; // [esp+160h] [ebp-2ECh]char v21; // [esp+181h] [ebp-2CBh]__int16 v22; // [esp+25Dh] [ebp-1EFh]char v23; // [esp+25Fh] [ebp-1EDh]CHAR v24; // [esp+260h] [ebp-1ECh]CHAR String[4]; // [esp+360h] [ebp-ECh]int v26; // [esp+364h] [ebp-E8h]__int16 v27; // [esp+368h] [ebp-E4h]CHAR Text; // [esp+36Ch] [ebp-E0h]struct tagRECT Rect; // [esp+38Ch] [ebp-C0h]CHAR Buffer; // [esp+39Ch] [ebp-B0h]HDC hdc; // [esp+400h] [ebp-4Ch]struct tagPAINTSTRUCT Paint; // [esp+404h] [ebp-48h]WPARAM v33; // [esp+444h] [ebp-8h]int v34; // [esp+448h] [ebp-4h]LoadStringA(hInstance, 0x6Au, &Buffer, 100);v14 = Msg;if ( Msg > 0x111 ){if ( v14 == 517 ){if ( strlen((const char *)&pbData) > 6 )ExitProcess(0);if ( strlen((const char *)&pbData) ){memset(&v24, 0, 0x100u);v6 = strlen((const char *)&pbData);memcpy(&v24, &pbData, v6); // pbData拷贝到v24v10 = (const char *)&pbData;do{v7 = strlen(v10);sub_40101E(&pbData, v7, v11); // CryptCreateHash函数,第二个参数为0x8003u,对pbData进行md5散列}while ( &v12 && !&v12 );strcpy(&v20, "0kk`d1a`55k222k2a776jbfgd`06cjjb");// v20="0kk`d1a`55k222k2a776jbfgd`06cjjb"memset(&v21, 0, 0xDCu);v22 = 0;v23 = 0;strcpy(v16, "SS"); // v16="SS"v17 = 0;v18 = 0;v19 = 0;v8 = strlen(&v20);sub_401005(v16, (int)&v20, v8); // v20^=v16if ( _strcmpi((const char *)&pbData, &v20) )// v20和md5散列后的pbData比较,求出v20,解md5散列,可得到散列前的pbData{SetWindowTextA(hWndParent, "flag{}");MessageBoxA(hWndParent, "Are you kidding me?", "^_^", 0);ExitProcess(0);}memcpy(&v15, &unk_423030, 0x32u); // unk_423030已知,拷贝到v15v9 = strlen(&v15);sub_401005(&v24, (int)&v15, v9); // v24和md5散列前的pbData相同,v24^=v15MessageBoxA(hWndParent, &v15, 0, 0x32u);}++dword_428D54;}else{if ( v14 != 520 )return DefWindowProcA(hWndParent, Msg, wParam, lParam);if ( dword_428D54 == 16 ){strcpy(String, "ctf");v26 = 0;v27 = 0;SetWindowTextA(hWndParent, String);strcpy(&Text, "Are you kidding me?");MessageBoxA(hWndParent, &Text, &Buffer, 0);}++dword_428D54;}}else{switch ( v14 ){case 0x111u:v34 = (unsigned __int16)wParam;v33 = wParam >> 16;v13 = (unsigned __int16)wParam;if ( (unsigned __int16)wParam == 104 ){DialogBoxParamA(hInstance, (LPCSTR)0x67, hWndParent, (DLGPROC)DialogFunc, 0);}else{if ( v13 != 105 )return DefWindowProcA(hWndParent, Msg, wParam, lParam);DestroyWindow(hWndParent);}break;case 2u:PostQuitMessage(0);break;case 0xFu:hdc = BeginPaint(hWndParent, &Paint);GetClientRect(hWndParent, &Rect);v5 = strlen(&Buffer);DrawTextA(hdc, &Buffer, v5, &Rect, 1u);EndPaint(hWndParent, &Paint);break;default:return DefWindowProcA(hWndParent, Msg, wParam, lParam);}}return 0;
}
先写脚本得到pbData的md5散列值,通过在线网站求逆
再写md5散列前的pbData(v24)和unk_unk_423030(v15)循环异或的脚本,得到flag