REVERSE-PRACTICE-BUUCTF-15

[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_; // rbx
  signed __int64 input_len; // rax
  __int128 *v4; // rax
  __int64 Code; // r11
  __int128 *v6; // r14
  int v7; // edi
  __int128 *v8; // rsi
  char v9; // r10
  int v10; // edx
  __int64 v11; // r8
  unsigned __int64 Code_len; // rcx
  signed __int64 Code_len_; // rcx
  unsigned __int64 v14; // rax
  unsigned __int64 i; // rax
  _BYTE *cipher; // rax
  size_t v17; // rsi
  _BYTE *cipher_; // 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);           // 读取输入
  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==v8
  do
  {
    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,变换位置,存储到v19
  v21 = 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加密的密文脚本
xx-script
网上找的xxtea的python代码,参考:XXTEA 加解密 as3 和 Python 分别实现
密钥为“flag”需要加长到16,写解xxtea脚本即可得到flag

# encoding: utf-8
import struct

_DELTA = 0x9E3779B9

def _long2str(v, w):
    n = (len(v) - 1) << 2
    if w:
        m = v[-1]
        if (m < n - 3) or (m > n): return ''
        n = m
    s = struct.pack('<%iL' % len(v), *v)
    return s[0:n] if w else s

def _str2long(s, w):
    n = len(s)
    m = (4 - (n & 3) & 3) + n
    s = s.ljust(m, "\0")
    v = list(struct.unpack('<%iL' % (m >> 2), s))
    if w: v.append(n)
    return v

def encrypt(str, key):
    if str == '': return str
    v = _str2long(str, True)
    k = _str2long(key.ljust(16, "\0"), False)
    n = len(v) - 1
    z = v[n]
    y = v[0]
    sum = 0
    q = 6 + 52 // (n + 1)
    while q > 0:
        sum = (sum + _DELTA) & 0xffffffff
        e = sum >> 2 & 3
        for 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))) & 0xffffffff
            z = v[p]
        y = v[0]
        v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffff
        z = v[n]
        q -= 1
    return _long2str(v, False)

def decrypt(str, key):
    if str == '': return str
    v = _str2long(str, False)
    k = _str2long(key.ljust(16, "\0"), False)
    n = len(v) - 1
    z = v[n]
    y = v[0]
    q = 6 + 52 // (n + 1)
    sum = (q * _DELTA) & 0xffffffff
    while (sum != 0):
        e = sum >> 2 & 3
        for 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))) & 0xffffffff
            y = v[p]
        z = v[n]
        v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
        y = v[0]
        sum = (sum - _DELTA) & 0xffffffff
    return _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
universe-logic
重要的是check函数,分析可知,验证input的字符,就是验证方程组是否成立
universe-check
利用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())

运行结果
universe-flag
转成字符串,运行elf文件,输入,得到flag
universe-flag

[WUSTCTF2020]level4

elf文件,运行后输出了几段字符串,可知是二叉树的遍历,无壳,ida分析
main函数,程序输出的type1为中序遍历,type2为后序遍历,type3被注释掉,应该是先序遍历
level4-logic
中序遍历: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
level4-flag

findKey

exe程序,无壳,ida分析
交叉引用字符串“flag{}”,发现从地址0x00401640开始,代码就没有被ida识别,应该是加了花指令
在地址0x00401918和地址0x0040191D处,两条一样的指令,放在了一起,nop掉第二条push指令
findkey-fakeorder
在地址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; // eax
  size_t v6; // eax
  DWORD v7; // eax
  int v8; // eax
  int v9; // eax
  const 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拷贝到v24
        v10 = (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^=v16
        if ( _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已知,拷贝到v15
        v9 = strlen(&v15);
        sub_401005(&v24, (int)&v15, v9);        // v24和md5散列前的pbData相同,v24^=v15
        MessageBoxA(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散列值,通过在线网站求逆
findkey-script
findkey-md5
再写md5散列前的pbData(v24)和unk_unk_423030(v15)循环异或的脚本,得到flag
findkey-script

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

P1umH0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值