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!\n");
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...\n\n");
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!\n");
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!!\n");
46         ExitProcess(0);
47       }
48       printf("bye ~~\n");
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位为{\rtf1https://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!

posted @ 2020-03-26 01:51  Hk_Mayfly  阅读(485)  评论(0编辑  收藏  举报