看雪ctf-国色天香 题解

程序运行基本逻辑

因为本文用到了OD入手分析(纯IDA也是可以的),所以需要了解程序运行的基本逻辑,可以通过直接运行测试来了解。

首先直接运行的选项框是这样的

输入CTFHUB注册码随便输入我这里输入的是11111111然后会有错误提示

基本逻辑如下

  1. 获取Enter Name
  2. 可能存在对Enter Name的加密
  3. 获取Enter Serial
  4. 可能存在对Enter Serial的加密
  5. 验证是否正确(分支)

OD分析

首先因为知道错误提示是

使用关键字搜索(右键-中文搜索引擎-只能搜索:找到对应的关键字然后双击进入)很容易定位到对应目标行,然后顺着红线

找到跳转来自哪(因为要找关键call,本题按照程序逻辑就是找获取文本框的两个函数call)找到的第二个获取文件文本函数的call(GetDlgItemTextA 是c语言中的一个获取数据流的一个函数)。为了方便后续分析我下了一个断点(F2)。

再往上又是一个,这个就是第一个获取用户名的调用了,为了方便后续分析我下了一个断点(F2)。

可以发现在两个获取文本之间间隔许多代码。而第二个获取输入的注册码文本的call之后没有什么大量操作的代码,看不出验证过程的端倪,这一段可以暂时先留着用IDA看看验证过程是什么样的。接下来就在OD中分析两个获取文本框内容之间代码的作用。

按F9执行输入

  • CTFHUB
  • 111111111111111111(这一行随便输的便于在执行中辨别)

然后点确认后程序会停在第一个断点处也就是第一个获取ID中,然后单步执行结合汇编代码阅读和输入的用户名CTFHUB来理解

发现这两者之间大致是将CTFHUB一个个读入cl然后经过一系列变换最后存入两个数组(这两个数组地址连续很可能是一个数组从40313C403145十个ascii码),然后就到获取注册码文本框文本的call了,因此在两个保存数值处下断点

然后重新执行看看信息栏每次存入数组的值是多少(这里把每次执行到这两个断点的值都截图如图一共十次)

则这个起始位置为40313C的数组保存的值为WVUTSRQPON。之后来分析获取我输入的密码之后发送了什么(OD看不出来)

IDA分析

通过错误提示的string很容易找到关键函数对应的关键代码,对应OD中的关键节点找到如下

那么只需要暴力枚举验证代码部分了,验证部分代码如下,其中

  • byte_40313C就是那个加密过后储存的十个字符的数组啦
  • byte_4031B4就是输入的验证值保存的数组
             v14 = 0;
              while ( 1 )
              {
                v15 = byte_4031B4[v14];
                if ( !v15 )
                  break;
                v16 = byte_40313C[v14] + 5;
                if ( v16 > 90 )
                  v16 = byte_40313C[v14] - 8;
                v17 = v16 ^ 0xC;
                if ( v17 < 65 )
                {
                  v17 = v14 + 75;
                }
                else if ( v17 > 90 )
                {
                  v17 = 75 - v14;
                }
                ++v14;
                if ( v17 != v15 )
                  goto LABEL_36;
              }
              MessageBoxA(0, aSerialIsCorrec, aGoodCracker, 0);

爆破密码代码如下

#include <bits/stdc++.h>
using namespace std;

int main(){
    char byte_40313C[]="WVUTSRQPON";
    char ans[20]; int p=-1;
    int v14 = 0;
    while ( 1 )
    {
        //v14 v15 v16 v17
        for(int v15='A'; v15<='Z'; v15++){
            int v16 = byte_40313C[v14] + 5;
            if ( !v15 )
                continue;
            if ( v16 > 90 )
                v16 = byte_40313C[v14] - 8;
            int v17 = v16 ^ 0xC;
            if ( v17 < 65 )
            {
                v17 = v14 + 75;
            }
            else if ( v17 > 90 )
            {
                v17 = 75 - v14;
            }
            if ( v17 != v15 ){
                continue;
            }
            ans[++p]=v15;
            ++v14;
        }
        if(v14>=10)
            goto LABLE_END;
    }
    LABLE_END:
    ans[10]='\0';
    cout<<ans<<endl;//输出:CBVUTFZYXB
    cout<<"Good Cracker"<<endl;
    return 0;
} 

因此答案就是flag{CBVUTFZYXB}

完整注册机版

加上前面的部分就是注册机了

#include<bits/stdc++.h>
using namespace std;

int main(){
//    char byte_40318C[205]="CTFHUB";
    char byte_40318C[205]={0};cin>>byte_40318C;
    int v6=strlen(byte_40318C); 
    
//  第一次加密 
      int v7 = 5;
      int v8 = 0;
      char byte_40313C[205]={0};
      do
      {
        int v9 = v7 + (byte_40318C[v8] ^ 0x29);
        if ( v9 < 65 || v9 > 90 )
          v9 = v7 + 82;
        byte_40313C[v8] = v9;
//        byte_40313D[v8] = 0;
        byte_40313C[v8+1] = 0;
//        LOBYTE(v8) = v8 + 1;
        ++v8;
        --v7;
      }while ( v7 );
      
      int v10 = 0;
      int v11 = 5;
//      char byte_403141[205]={0};
      do
      {
        int v12 = v11 + (byte_40318C[v10] ^ 0x27) + 1;
        if ( v12 < 65 || v12 > 90 )
          v12 = v11 + 77;
        byte_40313C[5+v10] = v12;
        byte_40313C[5+v10+1] = 0;
//        LOBYTE(v10) = v10 + 1;
        ++v10;
        --v11;
      }
      while ( v11 );
      
//    char byte_40313C[]="WVUTSRQPON"; 第一次加密结束接下来是第二次加密
 
    char ans[20]; int p=-1;
    int v14 = 0;
    while ( 1 )
    {
        //v14 v15 v16 v17
        for(int v15='A'; v15<='Z'; v15++){
            int v16 = byte_40313C[v14] + 5;
            if ( !v15 )
                continue;
            if ( v16 > 90 )
                v16 = byte_40313C[v14] - 8;
            int v17 = v16 ^ 0xC;
            if ( v17 < 65 )
            {
                v17 = v14 + 75;
            }
            else if ( v17 > 90 )
            {
                v17 = 75 - v14;
            }
            if ( v17 != v15 ){
                continue;
            }
            ans[++p]=v15;
            ++v14;
        }
        if(v14>=10)
            goto LABLE_END;
    }
LABLE_END:
    ans[10]='\0';
    cout<<ans<<endl;//输出:CBVUTFZYXB
    cout<<"Good Cracker"<<endl;
    return 0;
}
Last modification:January 20, 2021
如果觉得我的文章对你有用,请随意赞赏。咖啡(12RMB)进度+100%,一块巧克力(1RMB)进度+6%。
(赞赏请备注你的名称哦!后台记录中来自理工小菜狗)