Loading

ACTF2022-dropper re

DROPPER

一个upx壳的程序

总结: 一个用数组表示的大整数 是个int[512] 前500个是数据部分 a[500]是数据长度 用0-128表示ascii码 自定义的加减乘除)

参考:https://ppppz.net/2022/06/29/ACTF2022-dropper/ pz师傅的blog

http://www.ctfiot.com/46538.html 官方wp 似乎

动调dump

先慢慢分析 拖进x64dbg中跑一下 发现运行后输入flag直接提示wrong

停不到相关判断代码

image-20220708111138477

发现api https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject

每次会跑到别的进程里面去 应该是新的进程来进行flag输入和检测部分 我们尝试直接dump

image-20220708112640344

我们跳转到[RBP+68]

image-20220708112856031

去内存布局中选中所在区域

image-20220708112928240

右键将内存转存到文件

010打开 搜索MZ头 把MZ头前面没用的删掉

image-20220708113105872

修复好后可以正常运行

image-20220708113130563

分析dump.exe

简单分析找到main函数 (C480函数)( 字符串搜索或者 函数调用啥的都能简单找到

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 i; // rcx
  __int64 CopyInput; // rax
  void *base_input; // rax
  __int64 key; // rax
  __int64 v8; // rax
  size_t v9; // r8
  __int64 v10; // rax
  __int64 v11; // rax
  int v12; // edi
  char v14[32]; // [rsp+0h] [rbp-20h] BYREF
  char v15; // [rsp+20h] [rbp+0h] BYREF
  char input[72]; // [rsp+28h] [rbp+8h] BYREF
  char Src[2040]; // [rsp+70h] [rbp+50h] BYREF
  void (__fastcall ***v18)(_QWORD, __int64); // [rsp+868h] [rbp+848h]
  __int64 v19; // [rsp+888h] [rbp+868h]
  char v20[3576]; // [rsp+8B0h] [rbp+890h] BYREF
  char Block[64]; // [rsp+16A8h] [rbp+1688h] BYREF
  char v22[64]; // [rsp+16E8h] [rbp+16C8h] BYREF
  char *v23; // [rsp+1728h] [rbp+1708h]
  void (__fastcall ***v24)(_QWORD, _QWORD); // [rsp+1748h] [rbp+1728h]
  void *v25; // [rsp+1768h] [rbp+1748h]
  char v26; // [rsp+1788h] [rbp+1768h] BYREF
  char *v27; // [rsp+1F78h] [rbp+1F58h]
  char v28[64]; // [rsp+1F98h] [rbp+1F78h] BYREF
  __int64 v29; // [rsp+1FD8h] [rbp+1FB8h]
  void *v30; // [rsp+1FF8h] [rbp+1FD8h]
  char v31[64]; // [rsp+2018h] [rbp+1FF8h] BYREF
  char v32[64]; // [rsp+2058h] [rbp+2038h] BYREF
  void (__fastcall ***v33)(_QWORD, _QWORD); // [rsp+2098h] [rbp+2078h]
  int v34; // [rsp+20B4h] [rbp+2094h]
  int v35; // [rsp+20D4h] [rbp+20B4h]
  __int64 t; // [rsp+20E8h] [rbp+20C8h]
  __int64 v37; // [rsp+20F0h] [rbp+20D0h]
  void (__fastcall ***v38)(_QWORD, _QWORD); // [rsp+20F8h] [rbp+20D8h]

  v3 = &v15;
  for ( i = 0x6BEi64; i; --i )
  {
    *v3 = 0xCCCCCCCC;
    v3 += 4;
  }
  v35 = 0;
  thread_what(&unk_7FF7462B60F2);
  t = Reverse(Block, &unk_7FF7462AE168, 5i64);  // Reverse 通过对a2参数取反来隐藏一些数据 动调就可以无视了)
  v37 = t;
  printf(std::cout, t);
  free_(Block);                                 // 似乎是始放内存 栈啊什么的函数 无影响
  sub_7FF7462917DF(input);
  get(std::cin, input);
  t = strlen_0(input);                          // 多次动调发现t和input长度一样
  CopyInput = strcopy(input);                   // 复制字符串
  base_input = Base64encode(CopyInput, t);      // 通过base64码表 和动调知道是base64
  copy_base_to_input(input, base_input);        // 就是把base后的字符串放进input里面
  v23 = v22;
  t = E1(v22, input);                           // 这里t是**input
  E2(Src, t);
  v25 = malloc_0(0x7E0ui64);
  if ( v25 )
  {
    v27 = &v26;
    t = Reverse(v28, dword_7FF7462AF300, 360i64);
    v37 = t;
    v35 |= 1u;
    key = strcopy(t);
    v8 = str2int(v27, key);                     // 如key为'123456789'
                                                // 经过函数转换为[6789,1234](十进制)存到v26中
    v38 = sub_7FF746291433(v25, v8);            // 一个虚表函数 但这里指向的不是真正的虚表
  }
  else
  {
    v38 = 0i64;
  }
  v24 = v38;
  v18 = v38;
  if ( (v35 & 1) != 0 )
  {
    v35 &= ~1u;
    free_(v28);
  }
  sub_7FF746291226(v18);                        // 触发一个异常处理 替换了虚表函数
  v30 = malloc_0(0x7D4ui64);
  if ( v30 )
    t = memcpy_0(v30, Src, v9);
  else
    t = 0i64;
  v29 = t;
  v19 = t;
  (**v18)(v18, t);                              // 触发虚表函数
  sub(v19, v20, (v18 + 1));
  if ( check(v20) )                             // check函数 要求a[0]为0 a[500]为1
  {
    t = Reverse(v31, &unk_7FF7462AF910, 4i64);
    v37 = t;
    LODWORD(v10) = printf(std::cout, t);
    std::ostream::operator<<(v10, sub_7FF74629105A);
    free_(v31);
  }
  else
  {
    t = Reverse(v32, &unk_7FF7462AF920, 5i64);
    v37 = t;
    LODWORD(v11) = printf(std::cout, t);
    std::ostream::operator<<(v11, sub_7FF74629105A);
    free_(v32);
  }
  v33 = v18;
  if ( v18 )
    t = sub_7FF746291316(v33, 1i64);
  else
    t = 0i64;
  v34 = 0;
  free_(input);
  v12 = v34;
  sub_7FF74629169F(v14, &unk_7FF7462A93D0);
  return v12;
}

除0异常时 交给程序处理即可

image-20220708170747057

一路调进虚表函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 i; // rcx
  __int64 CopyInput; // rax
  void *base_input; // rax
  const char *key; // rax
  _DWORD *v8; // rax
  size_t v9; // r8
  __int64 v10; // rax
  __int64 v11; // rax
  int v12; // edi
  char v14[32]; // [rsp+0h] [rbp-20h] BYREF
  char v15; // [rsp+20h] [rbp+0h] BYREF
  char input[72]; // [rsp+28h] [rbp+8h] BYREF
  char Src[2040]; // [rsp+70h] [rbp+50h] BYREF
  void (__fastcall ***v18)(_QWORD, __int64); // [rsp+868h] [rbp+848h]
  __int64 v19; // [rsp+888h] [rbp+868h]
  char v20[3576]; // [rsp+8B0h] [rbp+890h] BYREF
  char Block[64]; // [rsp+16A8h] [rbp+1688h] BYREF
  char v22[64]; // [rsp+16E8h] [rbp+16C8h] BYREF
  char *v23; // [rsp+1728h] [rbp+1708h]
  void (__fastcall ***v24)(_QWORD, _QWORD); // [rsp+1748h] [rbp+1728h]
  void *v25; // [rsp+1768h] [rbp+1748h]
  char v26; // [rsp+1788h] [rbp+1768h] BYREF
  char *v27; // [rsp+1F78h] [rbp+1F58h]
  char v28[64]; // [rsp+1F98h] [rbp+1F78h] BYREF
  __int64 v29; // [rsp+1FD8h] [rbp+1FB8h]
  void *v30; // [rsp+1FF8h] [rbp+1FD8h]
  char v31[64]; // [rsp+2018h] [rbp+1FF8h] BYREF
  char v32[64]; // [rsp+2058h] [rbp+2038h] BYREF
  void (__fastcall ***v33)(_QWORD, _QWORD); // [rsp+2098h] [rbp+2078h]
  int v34; // [rsp+20B4h] [rbp+2094h]
  int v35; // [rsp+20D4h] [rbp+20B4h]
  __int64 t; // [rsp+20E8h] [rbp+20C8h]
  __int64 v37; // [rsp+20F0h] [rbp+20D0h]
  void (__fastcall ***v38)(_QWORD, _QWORD); // [rsp+20F8h] [rbp+20D8h]

  v3 = &v15;
  for ( i = 0x6BEi64; i; --i )
  {
    *v3 = 0xCCCCCCCC;
    v3 += 4;
  }
  v35 = 0;
  thread_what(&unk_7FF7462B60F2);
  t = Reverse(Block, &unk_7FF7462AE168, 5i64);  // Reverse 通过对a2参数取反来隐藏一些数据 动调就可以无视了)
  v37 = t;
  printf(std::cout, t);
  free_(Block);                                 // 似乎是始放内存 栈啊什么的函数 无影响
  sub_7FF7462917DF(input);
  get(std::cin, input);
  t = strlen_0(input);                          // 多次动调发现t和input长度一样
  CopyInput = strcopy(input);                   // 复制字符串
  base_input = Base64encode(CopyInput, t);      // 通过base64码表 和动调知道是base64
  copy_base_to_input(input, base_input);        // 就是把base后的字符串放进input里面
  v23 = v22;
  t = memcpppyy(v22, input);                    // 这里t是**input
  E2(Src, t);
  v25 = malloc_0(0x7E0ui64);
  if ( v25 )
  {
    v27 = &v26;
    t = Reverse(v28, dword_7FF7462AF300, 360i64);
    v37 = t;
    v35 |= 1u;
    key = strcopy(t);
    v8 = str2int(v27, key);                     // 如key为'123456789'
                                                // 经过函数转换为[6789,1234](十进制)存到v26中
    v38 = sub_7FF746291433(v25, v8);            // 一个虚表函数 但这里指向的不是真正的虚表
  }
  else
  {
    v38 = 0i64;
  }
  v24 = v38;
  v18 = v38;
  if ( (v35 & 1) != 0 )
  {
    v35 &= ~1u;
    free_(v28);
  }
  sub_7FF746291226(v18);                        // 触发一个异常处理 替换了虚表函数
  v30 = malloc_0(0x7D4ui64);
  if ( v30 )
    t = memcpy_0(v30, Src, v9);
  else
    t = 0i64;
  v29 = t;
  v19 = t;
  (**v18)(v18, t);                              // 触发虚表函数
  sub(v19, v20, (v18 + 1));
  if ( check(v20) )                             // check函数 要求a[0]为0 a[500]为1
  {
    t = Reverse(v31, &unk_7FF7462AF910, 4i64);
    v37 = t;
    LODWORD(v10) = printf(std::cout, t);
    std::ostream::operator<<(v10, sub_7FF74629105A);
    free_(v31);
  }
  else
  {
    t = Reverse(v32, &unk_7FF7462AF920, 5i64);
    v37 = t;
    LODWORD(v11) = printf(std::cout, t);
    std::ostream::operator<<(v11, sub_7FF74629105A);
    free_(v32);
  }
  v33 = v18;
  if ( v18 )
    t = sub_7FF746291316(v33, 1i64);
  else
    t = 0i64;
  v34 = 0;
  free_(input);
  v12 = v34;
  sub_7FF74629169F(v14, &unk_7FF7462A93D0);
  return v12;
}

image-20220708170932126

在每个str2int函数前断下 并把a2参数拿出来

image-20220708171443663

(在这一步看很多师傅的wp选择 这个函数执行完毕后拿出a1参数再解)))

但其实解完就是这个数)))

keys = [64584540291872516627894939590684951703479643371381420434698676192916126802789388,
        11783410410469738048283152171898507679537812634841032055361622989575562121323526,
        55440851777679184418972581091796582321001517732868509947716453414109025036506793,
        17867047589171477574847737912328753108849304549280205992204587760361310317983607,
        7537302706582391238853817483600228733479333152488218477840149847189049516952787,
        80793226935699295824618519685638809874579343342564712419235587177713165502121664,
        14385283226689171523445844388769467232023411467394422980403729848631619308579599,
        55079029772840138145785005601340325789675668817561045403173659223377346727295749,
        71119332457202863671922045224905384620742912949065190274173724688764272313900465,
        57705573952449699620072104055030025886984180500734382250587152417040141679598894]

下方部分函数简单分析发现就是对数组的加减乘除

这个虚表函数的逻辑即为

t+=key1
t*=key2
t-=key3
t+=key4
t*=key5
t-=key6
t+=key7
t-=key8
t+=key9
t-=key10

回到main函数 还有一个sub 直接dump拿出a3就是最后一个key

(

image-20220708172305775

image-20220708172504728

实际上这个key早就在这被处理了)))) 所以dump上面的字符串也可 还省一步解密)

image-20220708172411928

image-20220708172528942

key11=834572051814337070469744559761199605121805728622619480039894407167152612470842477813941120780374570205930952883661000998715107231695919001238818879944773516507366865633886966330912156402063735306303966193481658066437563587241718036562480496368592194719092339868512773222711600878782903109949779245500098606570248830570792028831133949440164219842871034275938433

keys = [0,
        64584540291872516627894939590684951703479643371381420434698676192916126802789388,
        11783410410469738048283152171898507679537812634841032055361622989575562121323526,
        55440851777679184418972581091796582321001517732868509947716453414109025036506793,
        17867047589171477574847737912328753108849304549280205992204587760361310317983607,
        7537302706582391238853817483600228733479333152488218477840149847189049516952787,
        80793226935699295824618519685638809874579343342564712419235587177713165502121664,
        14385283226689171523445844388769467232023411467394422980403729848631619308579599,
        55079029772840138145785005601340325789675668817561045403173659223377346727295749,
        71119332457202863671922045224905384620742912949065190274173724688764272313900465,
        57705573952449699620072104055030025886984180500734382250587152417040141679598894]
key11 = 834572051814337070469744559761199605121805728622619480039894407167152612470842477813941120780374570205930952883661000998715107231695919001238818879944773516507366865633886966330912156402063735306303966193481658066437563587241718036562480496368592194719092339868512773222711600878782903109949779245500098606570248830570792028831133949440164219842871034275938433
t = 0
t += key11
t += keys[10]
t -= keys[9]
t += keys[8]
t -= keys[7]
t += keys[6]
t //= keys[5]
t -= keys[4]
t += keys[3]
t //= keys[2]
t -= keys[1]
print(t)

解出这个虚表函数加密前的值

image-20220708202326914

最后观测main函数中的E2函数 发现是用128来存储ascii码的

所以

import base64
out = 9396732749587903779982032223783704408363403648379534949744333952324860296757610447170361685759182747971458838316594664099958866750753425972637962724687099230310084662432348188439040968731452657169050321
out = '0' + bin(out)[2:]
flag = b""
for i in range(len(out), 0, -7):
    flag += int(out[i-7:i],2).to_bytes(1,'big')
print(base64.b64decode(flag))

ACTF{dr0pp3r_1s_v3ry_int3r3st1ng_1d7a90a63039831c7fcaa53b766d5b2d!!!!!}

posted @ 2022-07-08 20:40  FW_ltlly  阅读(145)  评论(0编辑  收藏  举报