【CTF-Reverse】刮开有奖

今天做了一道reverse,根本不会

看完wp(re学习笔记(9)BUUCTF-re-刮开有奖_re 刮开有奖_Forgo7ten的博客-CSDN博客),感觉茅塞顿开,遂写之

可以说我是抄他的,事实上是看了一遍他的然后自己做了一遍再写了一下wp巩固!

0x1题目描述

给了一个32位exe文件,打开后并不能得到有效的信息。

如下图:(跟没没啥按钮,我咋刮,这不扯犊子)

0x2  ida分析

丢入ida,映入眼帘的是一坨啥也不知道的代码。但是我知道WinMain可是一个exe文件的开始函数啊,随看向WinMain

 笑了,这是什么啊!

Microsoft的文档:

从对话框模板资源创建模式对话框。 在显示对话框之前,函数会将应用程序定义的值作为WM_INITDIALOG消息的 lParam 参数传递给对话框过程。 应用程序可以使用此值初始化对话框控件。

INT_PTR DialogBoxParamA(
  [in, optional] HINSTANCE hInstance,
  [in]           LPCSTR    lpTemplateName,
  [in, optional] HWND      hWndParent,
  [in, optional] DLGPROC   lpDialogFunc,
  [in]           LPARAM    dwInitParam
);

hInstance:包含对话框模板的模块的句柄。 如果此参数为 NULL,则使用当前可执行文件。

lpTemplateName:对话框模板。

hWndParent:拥有对话框的窗口的句柄。

lpDialogFunc:指向对话框过程的指针。

dwInitParam:要传递给WM_INITDIALOG消息的 lParam 参数中的对话框的值。

不管咋样吧,前两个参数是模板,第三个是窗口句柄,第四个是指针,第五个是值。

不可能在模板中嵌入flag吧,那就只能去指针里头转转。

里头是这个鬼样子:

INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
  const char *v4; // esi
  const char *v5; // edi
  int v7[2]; // [esp+8h] [ebp-20030h] BYREF
  int v8; // [esp+10h] [ebp-20028h]
  int v9; // [esp+14h] [ebp-20024h]
  int v10; // [esp+18h] [ebp-20020h]
  int v11; // [esp+1Ch] [ebp-2001Ch]
  int v12; // [esp+20h] [ebp-20018h]
  int v13; // [esp+24h] [ebp-20014h]
  int v14; // [esp+28h] [ebp-20010h]
  int v15; // [esp+2Ch] [ebp-2000Ch]
  int v16; // [esp+30h] [ebp-20008h]
  CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
  char v18[65536]; // [esp+10034h] [ebp-10004h] BYREF

  if ( a2 == 272 )
    return 1;
  if ( a2 != 273 )
    return 0;
  if ( (_WORD)a3 == 1001 )
  {
    memset(String, 0, 0xFFFFu);
    GetDlgItemTextA(hDlg, 1000, String, 0xFFFF);
    if ( strlen(String) == 8 )
    {
      v7[0] = 90;
      v7[1] = 74;
      v8 = 83;
      v9 = 69;
      v10 = 67;
      v11 = 97;
      v12 = 78;
      v13 = 72;
      v14 = 51;
      v15 = 110;
      v16 = 103;
      sub_4010F0(v7, 0, 10);
      memset(v18, 0, 0xFFFFu);
      v18[0] = String[5];
      v18[2] = String[7];
      v18[1] = String[6];
      v4 = (const char *)sub_401000(v18, strlen(v18));
      memset(v18, 0, 0xFFFFu);
      v18[1] = String[3];
      v18[0] = String[2];
      v18[2] = String[4];
      v5 = (const char *)sub_401000(v18, strlen(v18));
      if ( String[0] == v7[0] + 34              // 90+34=124
        && String[1] == v10
        && 4 * String[2] - 141 == 3 * v8
        && String[3] / 4 == 2 * (v13 / 9)
        && !strcmp(v4, "ak1w")
        && !strcmp(
              v5,
              "V1Ax") )
      {
        MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
      }
    }
    return 0;
  }
  if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
    return 0;
  EndDialog(hDlg, (unsigned __int16)a3);
  return 1;
}

 

 长度是8

 进入红框中的函数看看

1.  sub_4010F0:

从a2到a3的循环,清一色去(4*i+a1),a1应当是数组的首地址,a2,a3是数组的边界索引。

 将其变为可执行的C代码(怎么变的??就复制到vs里头,然后哪里爆红改哪里)

注意:所有的4*i全改为i,因为他之前是汇编dword,访问下一个元素,地址需要+4,在c语言orc++之中,直接索引+1就可以解决了

int  sub_4010F0(int* a1, int a2, int a3)
{
    int result; // eax
    int i; // esi
    int v5; // ecx
    int v6; // edx

    result = a3;
    for (i = a2; i <= a3; a2 = i)
    {
        v5 = i;
        v6 =a1[i];
        if (a2 < result && i < result)
        {
            do
            {
                if (v6 > a1[result])
                {
                    if (i >= result)
                        break;
                    ++i;
                    a1[v5] =a1[result];
                    if (i >= result)
                        break;
                    while (a1[i] <= v6)
                    {
                        if (++i >= result)
                            goto LABEL_13;
                    }
                    if (i >= result)
                        break;
                    v5 = 4 * i;
                    a1[result] =a1[i];
                }
                --result;
            } while (i < result);
        }
    LABEL_13:
       a1[result] = v6;
        sub_4010F0(a1, a2, i - 1);
        result = a3;
        ++i;
    }
    return result;
}

 将v7中的元素全都弄进去。得到:

变成char

 2.第二个函数

// a1 为 v9的地址
// a2 为 v9的长度
_BYTE *__cdecl sub_401000(int *a1, int a2)
{
  int v2; // eax
  int v3; // esi
  size_t v4; // ebx
  _BYTE *v5; // eax
  _BYTE *v6; // edi
  int v7; // eax
  _BYTE *v8; // ebx
  int v9; // edi
  signed int v10; // edx
  int v11; // edi
  signed int v12; // eax
  signed int v13; // esi
  _BYTE *result; // eax
  _BYTE *v15; // [esp+Ch] [ebp-10h]
  _BYTE *v16; // [esp+10h] [ebp-Ch]
  int v17; // [esp+14h] [ebp-8h]
  int v18[4]; // [esp+18h] [ebp-4h]

  v2 = a2 / 3;
  v3 = 0;
  if ( a2 % 3 > 0 )
    ++v2;
  v4 = 4 * v2 + 1;                              // v4为长度
                                                // 
                                                // 当进行编码的数据长度是3的倍数时,len=strlen(str_in)/3*4;
                                                // 当进行编码的数据长度不是3的倍数时,len=(strlen(str_in)/3+1)*4;
                                                // 为Base64加密
  v5 = malloc(v4);
  v6 = v5;                                      // v6指向分配的动态内存空间
  v15 = v5;                                     // v15指向分配的动态内存空间
  if ( !v5 )
    exit(0);                                    // 动态分配内存失败,退出
  memset(v5, 0, v4);                            // 给分配的v5清零
  v7 = a2;
  v8 = v6;                                      // v8指向分配的动态内存空间
  v16 = v6;                                     // v16指向分配的动态内存空间
  if ( a2 > 0 )
  {
    while ( 1 )
    {
      v9 = 0;
      v10 = 0;
      v18[0] = 0;
      do
      {
        if ( v3 >= v7 )
          break;
        ++v10;
        v9 = *(a1 + v3++) | (v9 << 8);          // 将v9左移8位,然后与v9各个位二进制求或
      }
      while ( v10 < 3 );
      v11 = v9 << 8 * (3 - v10);                // v9向左移8的倍数位
      v12 = 0;
      v17 = v3;
      v13 = 18;
      do
      {
        if ( v10 >= v12 )
        {
          *(v18 + v12) = (v11 >> v13) & 63;
          v8 = v16;
        }
        else
        {
          *(v18 + v12) = 64;
        }
        *v8++ = byte_407830[*(v18 + v12)];      // byte_407830 存放 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
                                                // 此处更证明了为Base64加密
        v13 -= 6;
        ++v12;
        v16 = v8;
      }
      while ( v13 > -6 );
      v3 = v17;
      if ( v17 >= a2 )
        break;
      v7 = a2;
    }
    v6 = v15;
  }
  result = v6;
  *v8 = 0;
  return result;
}

4.在原函数中,有写成功条件:

string[0]==v7[0]+34=='3'+34= 'U'

string[1]=v10='J'

string[2]=(3*'E'+141)/4='W'

string[3]='Z'/9*2*4='P'

v4="ak1w"

v5="V1Ax"

在sub_401000函数中,可以看出是Base64编码,所以我们可以将v4带入base64解码里头,可以得到

string[5]='j'

string[6]='M'

string[7]='p'

同理可以得到:string[4]='1'

string="UJWP1jMP"

所以flag就是flag{UJWP1jMP}

0x3 总结

     .base64加密的特征:

        存有“A~Za~z0~9+/=”

        数组长度是3的倍数-》直接长度÷3*4,若不是,则÷③后+1再×4

        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LeeOrange_13

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

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

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

打赏作者

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

抵扣说明:

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

余额充值