• BUUCTF--CrackRTF


    测试文件:https://www.lanzous.com/iane9hi

    代码分析

    直接IDA打开,找到主函数之后

     1 int __cdecl main_0()
     2 {
     3   DWORD v0; // eax
     4   DWORD v1; // eax
     5   CHAR String; // [esp+4Ch] [ebp-310h]
     6   int v4; // [esp+150h] [ebp-20Ch]
     7   CHAR String1; // [esp+154h] [ebp-208h]
     8   BYTE pbData; // [esp+258h] [ebp-104h]
     9 
    10   memset(&pbData, 0, 0x104u);
    11   memset(&String1, 0, 0x104u);
    12   v4 = 0;
    13   printf("pls input the first passwd(1): ");
    14   scanf("%s", &pbData);                         // 输入
    15   if ( strlen((const char *)&pbData) != 6 )
    16   {
    17     printf("Must be 6 characters!
    ");
    18     ExitProcess(0);
    19   }
    20   v4 = atoi((const char *)&pbData);             // 将数字字符串转换为整型
    21   if ( v4 < 100000 )
    22     ExitProcess(0);
    23   strcat((char *)&pbData, "@DBApp");            // 末尾加上 @DBApp
    24   v0 = strlen((const char *)&pbData);
    25   sub_40100A(&pbData, v0, &String1);
    26   if ( !_strcmpi(&String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )// MD5解密得到123321@DBApp
    27   {
    28     printf("continue...
    
    ");
    29     printf("pls input the first passwd(2): ");
    30     memset(&String, 0, 0x104u);
    31     scanf("%s", &String);
    32     if ( strlen(&String) != 6 )
    33     {
    34       printf("Must be 6 characters!
    ");
    35       ExitProcess(0);
    36     }
    37     strcat(&String, (const char *)&pbData);     // 将123321@DBApp放到第二次输入字符串后面
    38     memset(&String1, 0, 0x104u);
    39     v1 = strlen(&String);
    40     sub_401019((BYTE *)&String, v1, &String1);
    41     if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", &String1) )
    42     {
    43       if ( !sub_40100F(&String) )
    44       {
    45         printf("Error!!
    ");
    46         ExitProcess(0);
    47       }
    48       printf("bye ~~
    ");
    49     }
    50   }
    51   return 0;
    52 }

    需要我们输入两次密码。

    第一次密码

    首先我们知道密码长度为6,并且通过atoi的操作,可以看出应该是纯数字的字符串(后面使用星号*表示)。

    第一部分将******@DBApp经过sub_40100A函数加密之后,得到6E32D0943418C2C33385BC35A1470250DD8923A9。因此我们只需要将密文解密,就能得到第一段的密码。

    打开sub_40100A函数后

    猜测是hash类的函数,测试后发现是MD5加密(https://www.somd5.com/),解密得到123321@DBApp

    第二次密码

    这次将上次的结果放到这次输入之后,即******123321@DBApp,用的是同一种加密方法得到27019e688a4e62a649fd99cadaafdb4e,但是解密不出来。

    因此我们向下看sub_40100F函数

     1 char __cdecl sub_4014D0(LPCSTR lpString)
     2 {
     3   LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
     4   DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h]
     5   DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
     6   HGLOBAL hResData; // [esp+60h] [ebp-Ch]
     7   HRSRC hResInfo; // [esp+64h] [ebp-8h]
     8   HANDLE hFile; // [esp+68h] [ebp-4h]
     9 
    10   hFile = 0;
    11   hResData = 0;
    12   nNumberOfBytesToWrite = 0;
    13   NumberOfBytesWritten = 0;
    14   hResInfo = FindResourceA(0, (LPCSTR)0x65, "AAA");
    15   if ( !hResInfo )
    16     return 0;
    17   nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
    18   hResData = LoadResource(0, hResInfo);
    19   if ( !hResData )
    20     return 0;
    21   lpBuffer = LockResource(hResData);
    22   sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
    23   hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
    24   if ( hFile == (HANDLE)-1 )
    25     return 0;
    26   if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
    27     return 0;
    28   CloseHandle(hFile);
    29   return 1;
    30 }
    • FindResourceA:该函数确定指定模块中指定类型和名称的资源所在位置。(可以看看这篇:https://blog.csdn.net/singleyellow/article/details/80308789
    • SizeofResource:表示该函数返回指定资源的字节数大小。 
    • LoadResource 以前返回值类似于记录可移动的指针的 HANDLE,需要传递给 LockResource 得到一个固定的指针地址以供读写。而现在,LoadResource 直接返回固定的可读写内存地址,不再需要 LockResource。

    这个函数我看成了三个部分:

    • 第1~21行:查找类型“AAA”,名称0x65的资源。
    • 第22行:将第二段密码与查找到的资源进行某种变换。
    • 第23行~最后:将变换得到的信息写入dbapp.rtf文件。

    第一部分中的信息,可以使用Resource Hacker查看

    我们重点看第二部分的函数

    unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, int a3)
    {
      unsigned int result; // eax
      unsigned int i; // [esp+4Ch] [ebp-Ch]
      unsigned int v5; // [esp+54h] [ebp-4h]
    
      v5 = lstrlenA(lpString);
      for ( i = 0; ; ++i )
      {
        result = i;
        if ( i >= a3 )
          break;
        *(_BYTE *)(i + a2) ^= lpString[i % v5];
      }
      return result;
    }

    这段实际就是将资源文件的信息与输入的密码进行异或操作。因为密码的后12位我们都知道了,因此我们只需要知道密码的前6位。

    这里需要一点脑洞,因为生成的是.rtf文件,我们可以了解到它的标识符前6位为{ tf1https://blog.csdn.net/dream_dt/article/details/79215798)。有结果,有异或值,不难解出原值。

    脚本

    # -*- coding:utf-8 -*-
    
    List = [0x05,0x7D,0x41,0x15,0x26,0x01]
    
    enc = '{\rtf1'
    flag = ''
    
    for i,val in enumerate(List):
        flag += chr(ord(enc[i]) ^ val)
    print (flag)

    ~!3a@0

    输入密码~!3a@0得到dbapp.rtf文件

    get flag!

  • 相关阅读:
    数据预处理
    机器学习--有监督学习和无监督学习
    weka简介
    第10章 接口、继承与多态----类的继承3
    html5 canvas实现梦幻的3D刺猬球
    html5 canvas实现图片玻璃碎片特效
    css3实现的鼠标经过按钮特效
    CSS3 Transitions属性打造动画的下载按钮特效
    纯css3实现的幽灵按钮导航
    几行css3代码实现超炫加载动画
  • 原文地址:https://www.cnblogs.com/Mayfly-nymph/p/12571918.html
Copyright © 2020-2023  润新知