这是一道内部测试的ctf题目,源程序的md5是1902F635994FA663C3432553A67E5830
定位 主函数
这是一道windows sdk 写的 gui程序
尝试运行程序会得到如下运行结果
在ida中对字符串 Wrong Code!Try Again!
查找引用就可以找到 关键函数了
修复变量
- 分析主程序会发现这其实是一个标准的
c+sdk
程序 - ida的分析基本正确
- 但是这里有一个
全局变量
识别出错了
这里是识别出错的地方
- ida认为这四个地址应该是 四个数组,但是仔细阅读源码可以发现这四个
数组
在内存中应该是排在一起的 - 这里对四个数组 重新定义:以
byte_40313C
为起点,定义类型为char [10]
修复之后就可以看到如下的关键代码
分析主程序
- 接下来就是主程序进行分析
- 这里附上 分析后的代码
LRESULT __stdcall sub_401109(HWND hWndParent, UINT Msg, WPARAM wParam, LPARAM lParam)
{
int v4; // ebx
int v5; // eax
char v6; // al
char v7; // al
int v8; // edx
char v9; // cl
int v10; // edx
char v11; // al
char v12; // cl
__int16 v13; // ax
int v14; // ecx
CHAR ptr_pwd; // bl
char v16; // dl
char v17; // dl
switch ( Msg )
{
case 2u:
PostQuitMessage(0);
break;
case 1u:
CreateWindowExA(0x200u, aEdit, 0, 0x50800080u, 15, 15, 255, 25, hWndParent, (HMENU)2, hInstance, 0);
dword_403134 = SetDlgItemTextA(hWndParent, 2, String);
CreateWindowExA(0x200u, aEdit, 0, 0x50800080u, 15, 50, 255, 25, hWndParent, (HMENU)4, hInstance, 0);
dword_403134 = SetDlgItemTextA(hWndParent, 4, aEnterSerial);
dword_403138 = (int)CreateWindowExA(
0x200u,
aButton,
aTry,
0x50800000u,
15,
85,
255,
25,
hWndParent,
(HMENU)3,
hInstance,
0);
v4 = (unsigned int)(GetSystemMetrics(0) - 290) >> 1;
v5 = GetSystemMetrics(1);
SetWindowPos(hWndParent, 0, v4, (unsigned int)(v5 - 150) >> 1, 290, 150, 0x40u);
break;
case 0x111u:
if ( (_WORD)wParam == 3 && !HIWORD(wParam) )
{
v6 = GetDlgItemTextA(hWndParent, 2, str_name, 40);// 获取用户名
if ( v6 ) // 用户名长度在[5,32]之间
{
if ( v6 > 32 )
{
MessageBoxA(0, aNameCanBeMax32, aSorry, 0);
}
else if ( v6 < 5 )
{
MessageBoxA(0, aNameMustBeMin5, aSorry, 0);
}
else
{
v7 = 5;
v8 = 0;
do
{ // 前半段
v9 = v7 + (str_name[v8] ^ 41);
if ( v9 < 65 || v9 > 90 )
v9 = v7 + 82;
calc_name[v8] = v9; // 根据用户名计算出的数据
// 动态调试获取
// 对calc_name 重定义为10字节
calc_name[v8 + 1] = 0;
LOBYTE(v8) = v8 + 1;
--v7;
}
while ( v7 );
v10 = 0;
v11 = 5;
do // 后半段
{
v12 = v11 + (str_name[v10] ^ 0x27) + 1;
if ( v12 < 65 || v12 > 90 )
v12 = v11 + 77;
calc_name[v10 + 5] = v12;
calc_name[v10 + 6] = 0;
LOBYTE(v10) = v10 + 1;
--v11;
}
while ( v11 );
v13 = GetDlgItemTextA(hWndParent, 4, str_pwd, 40);
if ( v13 && v13 <= 10 && v13 >= 10 )// 密码长度为10
{
v14 = 0;
while ( 1 )
{
ptr_pwd = str_pwd[v14]; // 迭代密码的指针
if ( !ptr_pwd )
break;
v16 = calc_name[v14] + 5;
if ( v16 > 90 )
v16 = calc_name[v14] - 8;
v17 = v16 ^ 0xC;
if ( v17 < 65 )
{
v17 = v14 + 75;
}
else if ( v17 > 90 )
{
v17 = 75 - v14;
}
++v14;
if ( v17 != ptr_pwd )
goto LB_WRONG; // nop掉 这里的 goto跳转,直接在内存中查看数据
}
MessageBoxA(0, aSerialIsCorrec, aGoodCracker, 0);// 正确的分支
}
else
{
LB_WRONG:
MessageBoxA(0, Text, Caption, 0);
}
}
}
else
{
MessageBoxA(0, aEnterName_0, aSorry, 0);
}
}
break;
default:
return DefWindowProcA(hWndParent, Msg, wParam, lParam);
}
return 0;
}
程序逻辑
算法逻辑
程序首先 利用
算法1/算法2
分别计算出name
的前半段calc_name
以及后半段calc_name
注意这里 是用两个算法分别计算
- 接下载再利用
calc_name
计算出真正的 密码 - 这里要注意的是:真正的密码是以字节为单位进行计算,并且每计算出一个字节就会立即比对,因此无法直接在内存中获取到完整的密钥
这里选择 按照程序逻辑写出对应的注册机来解题
题目要求 用户名必须为
dcnctf
,所以注册机中直接写死了
calc_name
这里有一个关键的数据calc_name
,一共有两种方案:
- 由于程序需要的是指定 用户名 对应的密码,因此可以采用动态调试
- 直接 利用 源程序中 关于
calc_name
相关的代码计算
动态调试
直接在关键位置00401316
下断电,利用x64 断点即可
算法还原
阅读主程序中相关的代码,还原出 计算calc_name
的相关代码
//计算中间数据
void get_cala_name()
{
// 前半段
char v7 = 5;
int v8 = 0;
do
{
char v9 = v7 + (str_name[v8] ^ 41);
if ( v9 < 65 || v9 > 90 )
v9 = v7 + 82;
calc_name[v8] = v9;
calc_name[v8 + 1] = 0;
v8= v8 + 1;
--v7;
}
while ( v7 );
//后半段
int v10 = 0;
int v11 = 5;
do // 后半段
{
char v12 = v11 + (str_name[v10] ^ 0x27) + 1;
if ( v12 < 65 || v12 > 90 )
v12 = v11 + 77;
calc_name[v10 + 5] = v12;
calc_name[v10 + 6] = 0;
v10 = v10 + 1;
--v11;
}
while ( v11 );
}
注册机
##include <stdio.h>
##include <stdlib.h>
// 动态调式获取 calc_name
// 题目要求用户名为 "dcnctf"
char str_name[]="dcnctf";
// 调试出来的数据是 RNJLSIIMGU
// 这里长度给多点
char calc_name[16]={0};
//计算中间数据
void get_cala_name()
{
// 前半段
char v7 = 5;
int v8 = 0;
do
{
char v9 = v7 + (str_name[v8] ^ 41);
if ( v9 < 65 || v9 > 90 )
v9 = v7 + 82;
calc_name[v8] = v9;
calc_name[v8 + 1] = 0;
v8= v8 + 1;
--v7;
}
while ( v7 );
//后半段
int v10 = 0;
int v11 = 5;
do // 后半段
{
char v12 = v11 + (str_name[v10] ^ 0x27) + 1;
if ( v12 < 65 || v12 > 90 )
v12 = v11 + 77;
calc_name[v10 + 5] = v12;
calc_name[v10 + 6] = 0;
v10 = v10 + 1;
--v11;
}
while ( v11 );
}
void get_pwd()
{
// 这是从 程序中抠出来的代码
int v14=0;
while ( v14<10 )
{
char v16 = calc_name[v14] + 5;
if ( v16 > 90 )
v16 = calc_name[v14] - 8;
char v17 = v16 ^ 0xC;
if ( v17 < 65 )
{
v17 = v14 + 75;
}
else if ( v17 > 90 )
{
v17 = 75 - v14;
}
++v14;
printf("%c",v17);
}
}
int main ()
{
get_cala_name();
printf("calc_name: %s \r\n",calc_name);
get_pwd();
return 0;
}
flag
账号: dcnctf
密码: KJCHTBBDSV