BUUCTF CrackRtf题解

一.总结

  1. 句柄的概念:逻辑上是二级指针,数值上是32位无符号整数
  2. CSP(Cryptographic Service Providers):加密服务提供程序
  3. ALGID:用于指示使用的哈希算法,0x8003指示
    MD5,0x8004指示SHA1
  4. md5在线解密网站(其他网站亲测没用)

二.基本步骤

32位程序,打开后找到main->main0,即可查看关键代码
程序大概步骤是:

  1. 输入第一个6字符长的字符串destion,假设是123321,将字符串和"@DBApp"拼接构成"123321@DBApp"
  2. 将destion字符串进行sha1哈希加密,string1保存对应的哈希值,比较哈希值
  3. 输入第二个6字符字符串,假设是 ~!3a@0,再将上文的"123321@DBApp"拼接到第二次输入的字符串的后方构成 str字符串"~!3a@0123321@DBApp"
  4. 将str字符串进行md5哈希加密,string1保存对应md5值,比较md5值
  5. 最后一个判断,不怎么影响结果,所以只要把str字符串对应md5值解密即可

三.关键函数

1.主函数

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  DWORD destion_len; // eax
  DWORD v4; // eax
  char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
  int v7; // [esp+150h] [ebp-20Ch]
  char String1[260]; // [esp+154h] [ebp-208h] BYREF
  char Destination[260]; // [esp+258h] [ebp-104h] BYREF

  memset(Destination, 0, sizeof(Destination));
  memset(String1, 0, sizeof(String1));
  v7 = 0;
  printf("pls input the first passwd(1): ");
  scanf("%s", Destination);                     // 必定是6位
  if ( strlen(Destination) != 6 )
  {
    printf("Must be 6 characters!\n");
    ExitProcess(0);
  }
  v7 = atoi(Destination);
  if ( v7 < 100000 )                            // 预计v7并不怎么会影响运行
    ExitProcess(0);
  strcat(Destination, "@DBApp");                // 加上了@DBAPP
  destion_len = strlen(Destination);
  sub_40100A((BYTE *)Destination, destion_len, String1);// sha1哈希处理,string1保存了destination的40位哈希值
  if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
  {
    printf("continue...\n\n");
    printf("pls input the first passwd(2): ");
    memset(Str, 0, sizeof(Str));
    scanf("%s", Str);
    if ( strlen(Str) != 6 )                     // 也是6个字符
    {
      printf("Must be 6 characters!\n");
      ExitProcess(0);
    }
    strcat(Str, Destination);
    memset(String1, 0, sizeof(String1));
    v4 = strlen(Str);
    sub_401019((BYTE *)Str, v4, String1);       // 0x8003u,md5哈希算法
    if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
    {
      if ( !(unsigned __int8)sub_40100F(Str) )  // 并不影响结果
      {
        printf("Error!!\n");
        ExitProcess(0);
      }
      printf("bye ~~\n");
    }
  }
  return 0;
}

2.sub_40100A(sha1哈希加密函数)

点进去找到sub_401230函数
这里的一些关键函数都可以百度查windows api查到

  1. CryptAcquireContextA应该是获取CSP句柄权限
  2. CryptCreateHash创建数据流哈希对象,第二个参数0x8004u代表sha1加密
  3. CryptHashData把数据添加到指定哈希对象,这个应该就是进行哈希处理
  4. CryptGetHashParam检索实际哈希值,这个应该是用于把哈希对象的哈希值赋给v6数组
  5. wsprintfA将数据写入指定缓冲区,这里用于字符串复制
  6. lstrcatA类似于strcat函数功能,也适用于复制
  7. ALG_ID加密使用的哈希算法的id,0x8003表示sha1,0x8004表示md5
// pbdata是传入的字符数组指针,dwdatalen是数组长度,lpstr是字符指针
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
  DWORD i; // [esp+4Ch] [ebp-28h]
  char String2[4]; // [esp+50h] [ebp-24h] BYREF
  char v6[20]; // [esp+54h] [ebp-20h] BYREF
  DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
  HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF 表示哈希对象的句柄
  HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF csp句柄

  if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )// 获取csp句柄权限?
    return 0;
  if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )// 启动数据流哈希对象,这里参数是0x8004u,控制选择何种哈希算法,查询得知这是sha1
  {
    if ( CryptHashData(phHash, pbData, dwDataLen, 0) )// 将数据添加到指定哈希对象,用于计算哈希值
    {
      CryptGetHashParam(phHash, 2u, (BYTE *)v6, &pdwDataLen, 0);// 检索实际哈希值
      *lpString1 = 0;                           // 内容清空
      for ( i = 0; i < pdwDataLen; ++i )
      {
        wsprintfA(String2, "%02X", (unsigned __int8)v6[i]);// 将数据写入指定缓冲区
        lstrcatA(lpString1, String2);           // 将一个字符串追加到另一个字符串,这里是把哈希值复制给string1
      }
      CryptDestroyHash(phHash);                 // 销毁引用的哈希对象
      CryptReleaseContext(phProv, 0);           // 释放csp(加密服务提供程序)句柄
      return 1;
    }
    else
    {
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      return 0;                                 // 失败返回0,其他关闭操作相同
    }
  }
  else
  {
    CryptReleaseContext(phProv, 0);
    return 0;
  }
}

3.sub_401019(md5哈希加密)

点进去看到sub_401040,在点进去和上一个函数对比可以发现这个是md5哈希加密

4.atoi(这个函数不清楚具体作用)

猜测是返回输入字符串例如"123321"对应整数数值123321

int __cdecl atol(const char *String)
{
  int v4; // [esp+8h] [ebp-Ch]
  int v5; // [esp+Ch] [ebp-8h]
  int C; // [esp+10h] [ebp-4h]
  const char *Stringa; // [esp+1Ch] [ebp+8h]

  while ( (int)SrcSizeInBytes <= 1
        ? *((_WORD *)off_428A60 + *(unsigned __int8 *)String) & 8
        : _isctype(*(unsigned __int8 *)String, 8) )
    ++String;
  C = *(unsigned __int8 *)String;               // String首字符
  Stringa = String + 1;
  v4 = C;                                       // 也是String首字符
  if ( C == '-' || C == '+' )
    C = *(unsigned __int8 *)Stringa++;
  v5 = 0;
  while ( (int)SrcSizeInBytes <= 1 ? *((_WORD *)off_428A60 + C) & 4 : _isctype(C, 4) )
  {
    v5 = 10 * v5 + C - '0';                     // 盲猜v5等于输入的字符串的对应数值,例如123456
    C = *(unsigned __int8 *)Stringa++;
  }
  if ( v4 == '-' )
    return -v5;
  else
    return v5;
}

通过MD5在线解密"27019e688a4e62a649fd99cadaafdb4e",得到"~!3a@0123321@DBApp
在这里插入图片描述

"此时可以知道第一个输入的字符串就是123321,第二个是~!3a@0
运行程序输入对应字符串后,程序会在其所在目录创建一个dbapp.rtf文件
在这里插入图片描述

在这里插入图片描述

打开可以得到flag{N0_M0re_Free_Bugs}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

OrientalGlass

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

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

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

打赏作者

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

抵扣说明:

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

余额充值