首页
论坛
专栏
课程

[原创]看雪CTF 2019Q3 第二题 血肉佣兵

2019-9-21 15:15 501

[原创]看雪CTF 2019Q3 第二题 血肉佣兵

2019-9-21 15:15
501

这是作者第三次改版的题目,上一次是个微型的vm,但这一次没看出来是什么

与Q1赛季题目的异同

  1. 00498C00 _Tfrmcrackme_FormCreate事件函数的00498C72 call sub_4969C0函数中注册sptWBCallback回调函数,回调函数地址:00496C7C
  2. 加载HTML作为界面,JavaScript获取输入,触发sptWBCallback事件
    function ckpswd() {
     key = "simpower91";
     a = document.all.pswd.value;
     if (a.indexOf(key) == 0) {
         l = a.length;
         i = key.length;
         sptWBCallback(a.substring(i, l));
     } else {
         alert("wrong!<" + a + "> is not my GUID ;-)");
         return "1234";
     }
    }
    function ok() {
     alert("congratulations!");
    }
    

1. js代码没有触发sptWBCallback回调

simpower91放在输入的开头,并不能触发sptWBCallback回调函数

  • 原因是在_Tfrmcrackme_FormCreate事件函数中又注册了一个定时事件,修改了内存中加载的js代码
    CODE:00498D1B                 push    ebx
    CODE:00498D1C                 push    offset sub_499388 ; 替换 simpower91 首字母为大写
    CODE:00498D21                 mov     eax, [ebx+33Ch] ; this
    CODE:00498D27                 call    @Idsyslogmessage@TIdSysLogMessage@SetTimeStamp$qqrx16System@TDateTime
    // 定时事件函数替换代码
    sub_499388:
    CODE:004993B5                 mov     eax, [ebp+System::AnsiString] ; System::AnsiString
    CODE:004993B8                 mov     ecx, offset _str_d_return_Si.Text ; int
    CODE:004993BD                 mov     edx, offset _str_d_return_si.Text ; System::AnsiString
    CODE:004993C2                 call    SysUtils_StringReplace
    
    图片描述
  • 所以实际内存中加载的js验证代码如下
    function ckpswd() {
      key = "Simpower91";
      a = document.all.pswd.value;
      if (a.indexOf(key) == 0) {
          l = a.length;
          i = key.length;
          sptWBCallback(a.substring(i, l));
      }
      ......
    

2.sptWBCallback回调函数中的验证代码与之前不同(解题过程)

  1. 00496E08 call dword ptr [ebx+40h]该位置调用反调试函数
  2. 之后的call指令在F8单步的时候,程序会重新跑起来,于是依次F8 F7确认后来到了如下位置... 004772CE -> 00476C3E -> 00475C64 -> 00474EF5 -> 00474EA2 -> 00474E98
    CODE:00474E98 f_call_fun_474E98 proc near             ; CODE XREF: sub_474EAC+49↓p
    CODE:00474E98
    CODE:00474E98 arg_0           = dword ptr  8
    CODE:00474E98 arg_4           = dword ptr  0Ch
    CODE:00474E98 arg_8           = dword ptr  10h
    CODE:00474E98
    CODE:00474E98                 push    ebp             ; SimInstructions.sub_00474E98
    CODE:00474E99                 mov     ebp, esp
    CODE:00474E9B                 mov     eax, [ebp+arg_0]
    CODE:00474E9E                 push    eax
    CODE:00474E9F                 mov     eax, [ebp+arg_8]
    CODE:00474EA2                 call    [ebp+arg_4]
    CODE:00474EA5                 pop     ebp
    CODE:00474EA6                 retn    0Ch
    CODE:00474EA6 f_call_fun_474E98 endp
    
  • 可以看出这个函数00474EA2的位置会调用参数中传进来的函数,最后跑飞的位置也在这里,疑似某种事件函数
  • 输入Simpower910123在这里下断点持续观察,会发现输入的字符串地址会在其中一次命中断点时出现在寄存器窗口
    图片描述
  • 这里调用的函数是sub_471720,调试后会发现此时将传进来的字符串的第一个字符作为DWORD数据保存在了函数第4个参数指向的地址中
  • 因为后面还有很多函数调用,不确定哪个函数是关键,于是在保存后的DWORD数据那里下硬件访问断点,再确认是否为验证操作
  • 经过两次断点触发,来到这里00470A21,发现给第一个字符加上了7F,最后保存在了原位置
    图片描述
  • 再经过几次断点触发,来到这里00470B36(注意在这里下个断点),发现将加上7F的数据再次减E0,将结果保存在edx
    图片描述
  • 然后F7单步,一直到这里,发现有个比较edx值是不是0的操作,于是怀疑这里就是判断,手动将edx的值改为0
    图片描述
  • 然后删除之前给数据下的硬件断点,F9运行观察一下,经过几次断点触发,又来到这这个熟悉的地方,不一样的是,开始操作第二个字符了
    图片描述
  • 于是重复刚才的操作会发现
    str[0] + 7F == E0
    str[1] + 7F == B2
    str[2] + 7F == B1
    str[3] + 7F == B0
    
  • 算出输入的值:
    E0 - 7F = 61
    B2 - 7F = 33
    B1 - 7F = 32
    B0 - 7F = 31
    // a123
    

    答案

    Simpower91a321
    // 这个答案。。。上次是a123,这次是a321,这说不定那个大佬猜都猜对了
    
    图片描述


[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com

上传的附件:
最新回复 (0)
游客
登录 | 注册 方可回帖
返回