• 【CTF REVERSE】WHCTF2017-CRACKME


    1、前言

    假装大学生水一下CTF题目,常规思路。程序没有加壳,是VC写的MFC程序。

    2、破题思路

    • 1、MessageBox 下断点
    • 2、找到提示错误字符串的函数B
    • 3、跟踪函数
    • 4、跟踪算法

    3、实现过程

    PEID查询无壳,进IDA查看字符串。

    得到这个字符串的一个存放地址,向上跟踪到有这个字符串的地方,依稀可以看到加密后的Flag轮廓。

    .data:00403254 aVSIKTQU        db '注册码要往这里填呀 ! ',0     ; DATA XREF: sub_401230+40o
    

    当即想到有字符串就会有弹框的地方,查找所有模块后,在MessageBox断点。

    运行-输入Flag的地方输入【ABCDEFG1234567】,然后通过堆栈窗口跟踪到调用弹窗的地方。

    拿到弹窗地址00401759,到IDA里的窗口查看对比得到调用Flag输入错误提示信息的地址。

    sub_401720 调用提示错误字符串的函数B

    在sub_401720处断点,截获到存着flag的寄存器。说明计算离计算flag的距离已经不远了。

    继续通过堆栈窗口跳到调用401720这个函数的地方,也就是00401621这个地址

    00401621 调用提示字符串B - 错误

    调用00401621地址也就是错误提示的地方来自以上的一个判断,这里应该有一个判断。但是IDA识别不出来,自己手动创建函数后程序结构判断一目了然。

    int __thiscall sub_4015E0(CWnd *this)
    {
      CWnd *v1; // esi@1
      int result; // eax@3
      int v3; // [sp-4h] [bp-Ch]@2
      int *v4; // [sp+4h] [bp-4h]@2
    
      v1 = this;
      CWnd::UpdateData(this, 1);
      if ( *(_DWORD *)(*((_DWORD *)v1 + 165) - 8) == 33
        && (v3 = *((_DWORD *)v1 + 165), v4 = &v3, CString::CString(&v3, (char *)v1 + 660), (unsigned __int8)sub_401630(v3)) )
      {
        result = sub_4016E0(v1);
      }
      else
      {
        result = sub_401720(v1);
      }
      return result;
    }
    

    依赖IDA向上追溯已经没法子了,那么通过程序中可以看到会对比一个33的字符,那么这个判断里非常有可能就是计算flag的地方。计算要求必须要33个字符,那么我们就整个33字符输入进去即可!输入构造好的33个字符的字符串【ABCDEFG123456789qwertyuiopzxcvbnm】

    反汇编的注释如下:

    
    004015E0 push ecx                        ;  调用错误提示的函数
    004015E1 push esi
    004015E2 mov esi,ecx
    004015E4 push 0x1
    004015E6 call <jmp.&MFC42.#6334>         ;  MFC更新数据函数UpdateData()
    004015EB mov ecx,dword ptr ds:[esi+0x294];  将FLAG字符串存储到ECX寄存器中
    004015F1 lea eax,dword ptr ds:[esi+0x294]
    004015F7 cmp dword ptr ds:[ecx-0x8],0x21 ;  对比是否有33个字符
    004015FB jnz short WHCTF201.0040161F
    004015FD push ecx
    004015FE mov ecx,esp
    00401600 mov dword ptr ss:[esp+0x8],esp
    00401604 push eax
    00401605 call <jmp.&MFC42.#535>          ;  MFC_CString函数
    0040160A mov ecx,esi
    0040160C call WHCTF201.00401630          ;  这个函数出来之后就比较是否跳到提示错误弹窗函数
    00401611 test al,al                      ;  再一次比较是否到调用提示错误的弹窗函数
    00401613 je short WHCTF201.0040161F
    00401615 mov ecx,esi
    00401617 call WHCTF201.004016E0          ;  调用提示成功的弹窗函数
    0040161C pop esi                         ;  0012FEB4
    0040161D pop ecx                         ;  0012FEB4
    0040161E retn
    0040161F mov ecx,esi                     ;  调用提示错误的弹窗函数
    00401621 call WHCTF201.00401720
    00401626 pop esi                         ;  0012FEB4
    00401627 pop ecx                         ;  0012FEB4
    00401628 retn
    
    

    反汇编代码中首先对比了一个输入的字符串是否有33个字符,如果没有就调用提示错误的弹窗函数。计算Flag通常会有两次判断,一次是判断字符串长度是否正确,然后判断输入的字符串是否正确。以上汇编中从00401630这个函数出来后会进行结果的判断然后根据结果是否跳转到调用提示错误弹窗函数的地方。

    跟进00401630函数,OD反汇编代码如下:

    00401630 push ecx                               ;  Flag计算函数
    00401631 push ebx                               ;  msvcrt.srand
    00401632 mov ebx,dword ptr ds:[<&MSVCRT.srand>] ;  msvcrt.srand
    00401638 push ebp
    00401639 push esi
    0040163A push edi
    0040163B xor edi,edi
    0040163D mov ebp,ecx                            ;  -CRACKME
    0040163F mov byte ptr ss:[esp+0x13],0x1
    00401644 mov edx,0xA
    00401649 xor esi,esi
    0040164B push edx
    0040164C call ebx                               ;  msvcrt.srand
    0040164E add esp,0x4                            
    00401651 call dword ptr ds:[<&MSVCRT.rand>]     ; [rand
    00401657 cdq                                    
    00401658 mov ecx,0xA                            
    0040165D idiv ecx                               ;  有符号除法
    0040165F mov ecx,dword ptr ss:[esp+0x18]        ;  flag的值存储到ecx中
    00401663 mov cl,byte ptr ds:[edi+ecx]           
    00401666 lea eax,dword ptr ds:[esi+edx]         
    00401669 cmp cl,byte ptr ds:[eax+ebp+0x60]      ;  flag{出现的地方
    0040166D je short WHCTF201.00401674             
    0040166F mov byte ptr ss:[esp+0x13],0x0         
    00401674 add esi,0xA                            ;  esi+10
    00401677 inc edi                                ;  目标操作数+1
    00401678 cmp esi,0x14A                          ;  330
    0040167E jl short WHCTF201.0040164B             
    00401680 lea ecx,dword ptr ss:[esp+0x18]        
    00401684 call <jmp.&MFC42.#800>                 
    00401689 mov al,byte ptr ss:[esp+0x13]          
    0040168D pop edi                                
    0040168E pop esi                                
    0040168F pop ebp                                
    00401690 pop ebx                                ;  msvcrt.srand
    00401691 pop ecx                                
    00401692 retn 0x4                               
    

    Flag出现的地方,就是0040165F这里。

    ABCDEFG123456789qwertyuiopzxcvbnm
    flag{The-Y3ll0w-turb4ns-Upri$ing}
    

    思路小结:

    • 1、MessageBox 下断点
    • 2、找到提示错误字符串的函数B
    • 3、有个CMP对比字符串是不是等于0x21(33)个字符
    • 4、调用两个CALL,MFC自带的跳过
    • 5、跟进00401630 计算flag函数
    • 6、00401663 读取flag字符
    • 7、0040165F 转换为Key值

    4、附件下载

    https://github.com/zprogram/zprogram.github.io/blob/master/Blog_attachment/CTF/WHCTF2017-CRACKME.zip

  • 相关阅读:
    C++学习笔记十关联容器
    Ubuntu下使用GDB断点Go程序
    各种语言的数字转罗码方法的实现
    为什么 ++i和i++的效果是一样的,试了javascript ,c++ java
    罗马数字转换阿拉伯数字(Java版,考虑较为全面)
    C++学习笔记九顺序容器(二) ForFreeDom 博客园
    智立方 屁话真言108:能盛事者能成事_智立方的杨石头_新浪博客
    腾讯搜搜高管吴军离职的传闻与真相
    罗马数字_百度百科
    快速深入一门语言的几个问题 Shell909090 随笔杂记
  • 原文地址:https://www.cnblogs.com/17bdw/p/7693919.html
Copyright © 2020-2023  润新知