当前位置: 首页 > ops >正文

珠海金山2007逆向分析挑战赛-CrackMe看雪(九连环)(writeup)

这几天在玩看雪上的KCTF,reverse里面有一个觉得有点意思的就是珠海金山2007逆向分析挑战赛-CrackMe看雪

首先,IDA打开,无壳程序还原C代码

INT_PTR start()
{HMODULE ModuleHandleA; // eaxModuleHandleA = GetModuleHandleA(0);return DialogBoxParamA(ModuleHandleA, (LPCSTR)0x65, 0, DialogFunc, 0);
}

首先,start函数注册了回调函数DialogFunc

INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{unsigned int v4; // ebxsigned int v5; // edxunsigned int v6; // kr04_4int v7; // esiCHAR v9[4100]; // [esp+Ch] [ebp-1018h] BYREFCHAR String; // [esp+1010h] [ebp-14h] BYREFint v11; // [esp+1011h] [ebp-13h]int v12; // [esp+1015h] [ebp-Fh]int v13; // [esp+1019h] [ebp-Bh]int v14; // [esp+101Dh] [ebp-7h]String = 0;v11 = 0;v12 = 0;memset(v9, 0, 4097);v13 = 0;v14 = 0;v4 = 0x13572468;if ( a2 == 0x111 ){if ( (unsigned __int16)a3 == 2 ){EndDialog(hDlg, 0);}else if ( (unsigned __int16)a3 == 1000&& GetDlgItemTextA(hDlg, 1001, &String, 16)&& GetDlgItemTextA(hDlg, 1002, v9, 4096) ){v5 = 0;v6 = strlen(&String) + 1;if ( (int)(v6 - 1) > 0 ){do{v7 = ((int)(57807475 * (v4 + *(&String + v5)) + 610800471) >> 7) | ((57807475 * (v4 + *(&String + v5))+ 610800471) << 25);++v5;v4 = v7;}while ( v5 < (int)(v6 - 1) );}sub_4002CC(v4, v9);}}return 0;
}

回调函数里,处理程序结束消息和注册按钮按下的消息。

题目要求得到注册码,主要看注册按钮事件。

首先根据输入的用户名计算v4,再根据v4校验注册码。(v9为输入的注册码,String为输入的用户名),其中,题目要求用户名为"KCTF",简化一下,得到

    unsigned int v4 = 0x13572468;char userName[] = "KCTF";for(int i = 0; i < 4; i++){v4 = ((int)(57807475 * (v4 + userName[i]) + 610800471) >> 7) | ((57807475 * (v4 + userName[i])+ 610800471) << 25);}printf("%u\n", v4);

注册码校验的核心逻辑就是sub_4002CC

int __cdecl sub_4002CC(unsigned int a1, const char *a2)
{int v2; // esiint i; // ediint v4; // ediunsigned int v5; // edxint v6; // ecxint v7; // eaxCHAR Text[257]; // [esp+Ch] [ebp-128h] BYREF__int16 v10; // [esp+10Dh] [ebp-27h]char v11; // [esp+10Fh] [ebp-25h]char v12; // [esp+110h] [ebp-24h]int v13; // [esp+111h] [ebp-23h]int v14; // [esp+115h] [ebp-1Fh]char v15; // [esp+119h] [ebp-1Bh]char v16[12]; // [esp+11Ch] [ebp-18h] BYREFchar v17[11]; // [esp+128h] [ebp-Ch] BYREFchar v18; // [esp+133h] [ebp-1h]v12 = 0;v13 = 0;memset(Text, 0, sizeof(Text));v14 = 0;v15 = 0;v17[0] = 0xFF;v16[0] = 0xFF;v10 = 0;v11 = 0;v17[1] = 0x63;v17[2] = 0xFB;v17[3] = 0x9A;v17[4] = 3;v17[5] = 0xA3;v17[6] = 0xDA;v17[7] = 0x72;v17[8] = 0xFE;v17[9] = 0xC9;v17[10] = 0xB7;v16[1] = 0x6A;v16[2] = 0xD1;v16[3] = 0xD2;v16[4] = 0x4E;v16[5] = 0x82;v16[6] = 0xDA;v16[7] = 0x72;v16[8] = 0xFE;v16[9] = 0xC9;v16[10] = 0xB7;v2 = strlen(a2);for ( i = 1; i < 9; ++i )*(&v12 + i) = (a1 >> i) & 1;v4 = 0;v15 = 1;if ( v2 <= 0 ){
LABEL_14:v7 = 1;while ( *(&v12 + v7) != 1 ){if ( ++v7 >= 10 ){sub_400240((int)v16, Text);goto LABEL_18;}}}else{while ( 1 ){v18 = a2[v4];if ( v18 < 48 || v18 > 57 )break;v5 = ((a1 >> (v4 % 31)) % 0xA + v18 - 48) % 0xA;if ( v5 == 1 ){LOBYTE(v13) = v13 ^ 1;}else{if ( *(&v11 + v5) != 1 )break;v6 = 1;if ( (int)(v5 - 2) >= 1 ){while ( *(&v12 + v6) != 1 ){if ( ++v6 > (int)(v5 - 2) )goto LABEL_12;}break;}
LABEL_12:*(&v12 + v5) ^= 1u;}if ( ++v4 >= v2 )goto LABEL_14;}}sub_400240((int)v17, Text);
LABEL_18:MessageBoxA(0, Text, Caption, 0);return 1;
}

开始的地方对v16和v17进行了异或解密,v16解密后为OK!!,v17为Fail!

整体逻辑看,需要v7>=10,才是正确的注册码。而V2不可能小于等于0,我们先看else里面的逻辑。

1、检查输入的注册码是否为数字,如果不是数字直接报错。

2、之前计算的v4通过右移0-31位后取个位数(右移的值是注册码对应字符的位置与31取模)+注册码对应位置的数字值

3、2计算的值取个位数。

4、

4-1、如果3的值为1,则v13^1

4-2、如果v5对应的位置值不为1,报错。

4-3、v5-2>=1,遍历前面是否全为0,如果全为0,则v5对应位置的值^1;若不全为0,则报错

4-4、v5-2<1,则v5对应位置^1

上面的逻辑看似没什么规律,x32dbg跟里几步发现,和我高中玩的九连环很像。

九连环的特点就是,第一个环不受限制,如果你想挂上或者拆下某个其它位置的环,必须保正这个环的前一个环是挂上的,且出了前一个环之外,前面没有其它的环挂上,后面的环是否挂上不影响。

根据这个规律可以简单写一个九连环的挂/摘逻辑

unsigned char valueList[4096] = { 0 };
int cnt = 0;
void down(char* nine, int len);
void up(char* nine, int idx)
{if (idx == 0){if (nine[idx] == '0'){nine[idx] = '1';valueList[cnt++] = idx + 1;printf("%s\n", nine);}return;}if (nine[idx - 1] == '0'){up(nine, idx - 1);}if (idx - 2 >= 0){down(nine, idx - 1);}nine[idx] = '1';valueList[cnt++] = idx + 1;printf("%s\n", nine);
}void down(char* nine, int len)
{for (int i = len - 1;i >= 0;i--){if (nine[i] == '1'){if (i == 0){nine[i] = '0';valueList[cnt++] = i + 1;printf("%s\n", nine);return;}if (nine[i - 1] == '1'){nine[i] = '0';valueList[cnt++] = i + 1;printf("%s\n", nine);}else{up(nine, i - 1);if (i - 2 >= 0){down(nine, i - 1);}nine[i] = '0';valueList[cnt++] = i + 1;printf("%s\n", nine);};}}}

根据题目生成9连环的初始状态,调用上面的函数,可以推出注册码,完整代码如下:

unsigned char valueList[4096] = { 0 };
int cnt = 0;
void down(char* nine, int len);
void up(char* nine, int idx)
{if (idx == 0){if (nine[idx] == '0'){nine[idx] = '1';valueList[cnt++] = idx + 1;printf("%s\n", nine);}return;}if (nine[idx - 1] == '0'){up(nine, idx - 1);}if (idx - 2 >= 0){down(nine, idx - 1);}nine[idx] = '1';valueList[cnt++] = idx + 1;printf("%s\n", nine);
}void down(char* nine, int len)
{for (int i = len - 1;i >= 0;i--){if (nine[i] == '1'){if (i == 0){nine[i] = '0';valueList[cnt++] = i + 1;printf("%s\n", nine);return;}if (nine[i - 1] == '1'){nine[i] = '0';valueList[cnt++] = i + 1;printf("%s\n", nine);}else{up(nine, i - 1);if (i - 2 >= 0){down(nine, i - 1);}nine[i] = '0';valueList[cnt++] = i + 1;printf("%s\n", nine);};}}}
int main(void)    
{unsigned int v4 = 0x13572468;char userName[] = "KCTF";for(int i = 0; i < 4; i++){v4 = ((int)(57807475 * (v4 + userName[i]) + 610800471) >> 7) | ((57807475 * (v4 + userName[i])+ 610800471) << 25);}printf("%u\n", v4);char Nine[] = "000100001";down(Nine, 9);int idxList[31] = { 0 };char flag[4096] = { 0 };for (int i = 0;i < 31;i++){idxList[i] = (v4 >> i) % 10;}for (int i = 0;i < cnt;i++){if (valueList[i] < idxList[i % 31]){flag[i] = valueList[i] + 10 - idxList[i % 31] + 48;}else{flag[i] = valueList[i] - idxList[i % 31] + 48;}}printf("%s\n", flag);return 0;}

运行结果

http://www.xdnf.cn/news/5637.html

相关文章:

  • 【运维】MacOS蓝牙故障排查与修复指南
  • 大地网接地电阻测试的必要性
  • Python如何使用进行风险管理和投资组合优化
  • 2025智能体基建在进化过程中带来的质变
  • 国外付费AI软件充值教程
  • 《棒球百科》MLB棒球公益课·棒球1号位
  • 02.Golang 切片(slice)源码分析(一、定义与基础操作实现)
  • VBA —— 学习Day6
  • 解读RTOS:第一篇 · RTOS 基础与选型指南
  • WebSocket的原理及QT示例
  • PHP 连接和使用 Kafka 的指南
  • 使用SSH协议克隆详细步骤
  • 数据结构(六)——树和二叉树
  • vCDMstudio 软件
  • ​​​​​​​大规模预训练范式(Large-scale Pre-training)
  • 【TVM 教程】microTVM PyTorch 教程
  • 如何快速入门大模型?
  • 【套题】GESP C++四级认证各题详解/详细代码
  • 查看购物车
  • sql语句面经手撕(定制整理版)
  • MYSQL 全量,增量备份与恢复
  • HTTP3
  • 一次IPA被破解后的教训(附Ipa Guard等混淆工具实测)
  • [Java] 输入输出方法+猜数字游戏
  • 支持私有化部署的小天互连即时通讯平台:助力企业数字化转型的通讯利器
  • lenis选项卡举例
  • LeetCode 373 查找和最小的 K 对数字题解
  • Git安装教程及常用命令
  • 【DeepSeek问答记录】请结合实例,讲解一下pytorch的DataLoader的使用方法
  • 11 配置Hadoop集群-免密登录