题目描述文档:
此题使用《规则:5.1.1 Windows方案一》,需要你输入一串序列号。
最终cm.exe文件的SHA1值为:db4b7754816c6ba61f8a9687feb2d50203941958
判胜条件:
打开cm.exe,输入你的序列号,然后回车,如果结果显示“success.”表示成功,其他为失败。
源码:
#include <iostream>
#pragma comment(linker, "/BASE:0x00700000")
const char keydict[]="0123456789abcdefghijklmnopqrstuvwxyz";
int maze_data[3][9][10] = {//不可分解为墙,可分解为路;
{
{11278, 4882,32580,19499,2406,7698,4707,6184,12705,21166},
{7622, 16409, 15753,24909,6730,14884,21510,3681,1624,3404},
{21539,18528,31112,8099,28763,17508,25583,16446,20683,5750},
{20786,26805,19000,28661,6299, 5899, 9988,8429,18744,18422},
{29970,20886,11132,19024,11860, 28606, 13618,31572,2749,10377},
{22208,12859,13976, 12751, 17242, 30202, 2603, 8203, 17901, 5695},
{9556,25275,13203, 10801,10582,12292,13956, 15854, 18348, 8807},
{25246, 7827, 31043, 26139, 32692,9562,15671,6148,27105,12588},
{14745,10559,12046,29754,279,25805,31862,23743,27312,1413}
},
// 第二层
{
{27900,15590,28587,3969,20764,9444,1925,23275,10615,3067},
{22008, 57, 10717, 18823, 4923, 24111, 32589, 25751, 25282, 14196},
{6182,1932,8379, 21619, 26806,10235,29473, 6926, 25216, 21730},
{20787, 25063, 12139, 23266, 6921, 25787, 1819, 25202, 17727, 8893},
{3432, 21161, 12566,6573,10486,5150,27408,12972,9108,13629},
{27200, 1687, 2062, 26755, 17047,5981,2874,17796,20408,21970},
{9618,24473,22918,23172, 29091, 14709, 1317, 15893, 2083, 4823},
{30959, 25451, 18202,21276,9208,14551,32356,6363,13390,22101},
{7273,3816,572,9672,1668,30573,2917,30805,24621,10110}
},
// 第三层
{
{24748,6277,28224,16108,5980,3371,20270,1605,27760,17789},
{23768,11026,284,31405,11870,14283,23800,23624, 25743, 14401},
{25412,32611,1474,3042,29050,29463,28954,17220, 793, 30062},
{12126,28402,9136,7893,23067,24575,29844,23628,32044, 11345},
{19428,20887,9092,25821,25321,20505,17534,7585,28851, 23299},
{6076,20180,15903,9976,10994,31588,17972,27097, 1882, 13387},
{10192,3228,14430,9366, 22069, 22442,17576,25263, 26087, 12932},
{31347,7375,9716,24013, 23774, 4251,30864,17871, 24559, 19131},
{38, 1149, 24865, 14914, 21785, 12975,8342,6375, 16769, 8114}
}
};
// 自定义绝对值函数
inline double custom_abs(double val) {
return (val < 0) ? -val : val;
}
// 自定义平方根函数
double my_sqrt(double x) {
// 处理边界值
if (x <= 0.0) return 0.0;
if (x == 1.0) return 1.0;
double guess = x / 2.0; // 初始猜测值
double prev = 0.0;
const double eps = 1e-7; // 精度阈值
int max_iter = 50; // 最大迭代次数
for (int i = 0; i < max_iter; ++i) {
prev = guess;
guess = 0.5 * (guess + x / guess); // 牛顿迭代公式
// 计算相对误差
double diff = custom_abs(guess - prev);
if (diff < eps * custom_abs(guess)) {
break;
}
}
return guess;
}
bool is_prime(int num) {
if (num < 2) return false;
for (int i = 2; i <= (int)my_sqrt((double)num); ++i) {
if (num % i == 0) return false;
}
return true;
}
bool is_product_of_two_primes(int n, int* p1 = NULL, int* p2 = NULL) {
if (n < 4) return false;
for (int i = 2; i <= (int)my_sqrt((double)n); ++i) {
if (n % i == 0) {
int j = n / i;
if (is_prime(i) && is_prime(j)) {
if (p1) *p1 = i;
if (p2) *p2 = j;
return true;
}
}
}
return false;
}
bool checklic(const char*input)
{
if(input==NULL || strlen(input)==0) return false;
int nLenInput=strlen(input);
int nLenDict=strlen(keydict);
int x,y,z,x0,y0,z0;
x=y=z=0;
x0=y0=z0=0;
int step=0;
for(int i=0;i<nLenInput;i++)
{
char a=input[i];
int p=0;
for (;p<nLenDict;p++)
{
if(keydict[p]==a) break;
}
if(p>=nLenDict) break;
if(p<12){
z=0;
}
else if(p<24){
z=1;
}
else{
z=2;
}
if((i&1)==0)
{
y=p%12;
}
else
{
x=p%12;
step++;
// 起点终点特殊检查
if(step==1 && maze_data[z][y][x]!=11278) {
return false;
}
if(i==nLenInput-1 && maze_data[z][y][x]!=8114) {
return false;
}
// 路径点有效性检查
if(step>1 && i<nLenInput) {
int p1, p2;
bool decomposable = is_product_of_two_primes(maze_data[z][y][x], &p1, &p2);
if (false == decomposable) {
return false;
}
if(p1<2 || p2<2) return false;
if(p1==p2) return false;
}
// 连续性检查
if(step>1) {
int dz = abs(z - z0);
int dy = abs(y - y0);
int dx = abs(x - x0);
if(dz+dy+dx != 1) {
return false;
}
}
x0=x;
y0=y;
z0=z;
//最后一步必须为8114
if(i==nLenInput-1 && maze_data[z][y][x]==8114) {
return true;
}
}
}
return false;
}
int main() {
printf( "input your serial:\n");
char szInputStr[200]={0};
std::cin.getline(szInputStr, sizeof(szInputStr));
if ( strlen(szInputStr) <= 112 ) // 输入不能超过112个字符
{
bool ret=checklic(szInputStr);
if(ret) {
printf("\nsuccess.\n");
} else {
printf("\nfail!\n");
}
}
else
{
printf("\nTry again...\n");
}
getchar();
return 0;
}writeup:
此题的解题思路:
此题是一个三维迷宫的题目,不过对迷宫的路和墙做了加噪干扰,不能直观的看出来,需要对迷宫每个坐标上的整数做质数分解来还原路和墙,同时也对主逻辑函数的一小段代码做了拆分,使程序在IDA7.0~IDA9.1版本不能直接还原出完整的函数代码(其他版本没试过),需要手工修改还原,大概的求解过程如下:
1.将主逻辑函数的代码进行还原,方便在IDA里分析过程(在x86dbg里查看也可以,就是只能看汇编代码);
2.利用程序里的质数分解函数将3X9X10的迷宫还原成路和墙(比如:00表示墙,11表示路),这样可以比较直观的解出迷宫的路线,即从{0,0,0}到{2,8,9}的通行路线坐标序列;
3.将路线坐标映射到字典字符,即得出要求解的序列号。
本题主要考查的是对三维数组映射到三维迷宫的空间想象力,还有一点就是对ret跳转的理解。
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-8-18 14:33
被kanxue编辑
,原因: