看雪ctf-国色天香 题解
程序运行基本逻辑
因为本文用到了OD入手分析(纯IDA也是可以的),所以需要了解程序运行的基本逻辑,可以通过直接运行测试来了解。
首先直接运行的选项框是这样的
输入CTFHUB注册码随便输入我这里输入的是11111111然后会有错误提示
基本逻辑如下
- 获取Enter Name
- 可能存在对Enter Name的加密
- 获取Enter Serial
- 可能存在对Enter Serial的加密
- 验证是否正确(分支)
OD分析
首先因为知道错误提示是
使用关键字搜索(右键-中文搜索引擎-只能搜索:找到对应的关键字然后双击进入)很容易定位到对应目标行,然后顺着红线
找到跳转来自哪(因为要找关键call,本题按照程序逻辑就是找获取文本框的两个函数call)找到的第二个获取文件文本函数的call(GetDlgItemTextA
是c语言中的一个获取数据流的一个函数)。为了方便后续分析我下了一个断点(F2)。
再往上又是一个,这个就是第一个获取用户名的调用了,为了方便后续分析我下了一个断点(F2)。
可以发现在两个获取文本之间间隔许多代码。而第二个获取输入的注册码文本的call之后没有什么大量操作的代码,看不出验证过程的端倪,这一段可以暂时先留着用IDA看看验证过程是什么样的。接下来就在OD中分析两个获取文本框内容之间代码的作用。
按F9执行输入
- CTFHUB
- 111111111111111111(这一行随便输的便于在执行中辨别)
然后点确认后程序会停在第一个断点处也就是第一个获取ID中,然后单步执行结合汇编代码阅读和输入的用户名CTFHUB
来理解
发现这两者之间大致是将CTFHUB
一个个读入cl
然后经过一系列变换最后存入两个数组(这两个数组地址连续很可能是一个数组从40313C
到403145
十个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;
}