【有奖CrackMe】春节快乐!给大家发一道未被采纳的Windows基础红包题来热热身!
本帖最后由 烟99 于 2025-1-30 19:23 编辑今年本来想也想参与红包解题活动的命题工作的,结果H大的一句话让我扎心了——题够多不需要。
https://liuxue.xdf.cn/blog/diyawei/blog/W020201204686996466948.jpg
那好,那就以我个人的名誉来出这道题吧,也当做是赛前热身了。
请听题:
```
如图,这是一个VC6.0编写的程序,没有壳,也没有混淆指令,请解出本题答案,并列出解题步骤,直接说答案无效!
大年初五将在编程语言区公布本题源代码。
这次的题目也是有奖答题,评出特等奖、一、二、三等奖各一位,奖项设置如下:
```
特等奖:解出本题,并率先单独在『脱壳破解区』发帖写出解题步骤并被站务人员加精华加优秀的奖励500CB。
一等奖:第一位解出本题者奖300CB。
二等奖、第二位解出本题者奖200CB。
三等奖:第三位解出本题者奖100CB。
```
本活动截止日期为大年初五凌晨零点,所有CB奖金也将在大年初五统一发放。
再次强调:只写答案没有解题步骤的无效!AI解题无效!
CM区禁止无关回复!违者重罚!
战报:
截至2025.01.3017:35,3楼的同学就贴了两张图,相当于啥也没说,而4—7楼的同学借助了AI答题,而且7楼的同学还回答错误,还有一位同学在昨天单独从脱壳破解区开了帖子分析,也借助了AI,因此这些楼层的答案无效。
8楼的同学勉强能过关,而@Barnes 同学在脱壳破解区写出了自己的破解流程,条理也比较清晰,所以目前只有两位同学解开了此题!一二等奖已有人认领,来看看三等奖花落谁家!
领取题目:
解压密码:52pojie 本帖最后由 ahov 于 2025-1-30 18:53 编辑
首先将程序拖入x32dbg中,运行到答案输入提示,如下图所示:
右击-搜索-当前区域-字符串,随后可以发现“恭喜你,回答正确”字符串,如下图所示:
点击进入,可以发现上下文中存在多个比较指令,我们将其统统下上断点,如下图所示
尽管可以看到有两个字符"H"和"a"极有可能是flag的一部分,但是我们确保科学性和严谨性,仍然输入"abcdefg"作为提交答案尝试。
答案提交后,运行到cmp ebp,48时可以看到,程序将我们输入的答案中的第1个字符"a"存储进了EBP中,并与"H"进行了比较(见左下),证明了我们的猜想——flag的第一个字符为"H",如下图所示:
为了使答案检验过程继续运行,我们需要将EBP中的数据修改为"H",如下图所示:
继续执行程序,可以看到程序将我们输入的答案中的第2个字符"b"与"a"进行了比较(见左下),如下图所示:
同样的方式,我们将地址ESP+18修改为"a"然后继续运行,如下图所示:
在程序运行到cmp edi,ebx时,我们可以看到:此时edi = "c" (我们输入的答案的第3位); ebx = "d" (我们输入的答案的第4位),如下图所示:
程序此时正准备比较edi和ebx,说明edi和ebx应当是相等的,即flag的第3位和第4位应当相等。
结合前面的"Ha",我们可以猜测flag的前4位为"Happ"。
我们不妨将edi和ebx均改为"p",如下图所示:
继续运行后,发现程序进行了异或运算后将edx与值FFFFF1E3进行比较,只有当edx的值等于FFFFF1E3时才继续执行后续操作,如下图所示:
该逻辑不影响flag的获取,为了使答案检验过程继续运行,我们将edx的值修改为FFFFF1E3后继续运行程序,如下图所示:
程序继续执行后,我们可以关注到,下一个比较指令为cmp a1,c1,目的是判断a1与c1是否相等,在此之前我们需要先确定a1和c1是什么东西以及a1与c1和flag之间的关系。
在比较指令上面,我们可以看到两行至关重要的指令:
mov a1,byte ptr ds:
mov b1,byte ptr ds:
在调试器中我们可以看到,esi+01:"bcdefg" (把我们输入的答案的第1位移除了),然后程序取了"bcdefg"的第1位 (也就是我们输入的答案的第2位) 作为a1,然后对a1进行异或计算,如下图所示:
同理,esi+04:"efg" (把我们输入的答案的前4位移除了),然后程序取了"efg"的第1位 (也就是我们输入的答案的第5位) 作为c1,如下图所示:
我们当前可以猜测flag前五位为"Happy",不妨重新启动程序并调试,输入"Happyfg"作为提交答案尝试。经过尝试后发现,当提交答案前5位为"Happy"时(理论上只需要提交的答案第2位为"a"且第5位为"y"时就可以,未具体测试),a1就会与c1相等(见左下),如下图所示:
继续运行程序,在mov dword ptr ss:,eax指令处或fild dword ptr ss:指令处断下来之后,直接查看eax就可以看到"2025",如下图所示:
将上述信息整合后确定flag为"Happy2025",提交后确认flag答案正确,如下图所示:
确实,有了AI现在逆向都方便多了
答案:Happy2025
过程
1、IDA看验证的伪代码
2、代码很清晰,复制给AI
要推断出输入什么字符串可以使代码输出 `anser_is_true`,需要逐步分析 `if` 语句中的各个条件。
### 条件分析
#### 1. `v2 == 72`
`v2 = *a1;`,这意味着输入字符串的第一个字符的 ASCII 码值必须为 72,而 ASCII 码值为 72 的字符是 `'H'`。
#### 2. `v10 == 97`
`v10 = a1;`,这表示输入字符串的第二个字符的 ASCII 码值必须为 97,即字符 `'a'`。
#### 3. `v3 == v4` 且 `v5 = 32 * v4`
`v3 = a1;`,`v4 = a1;`,所以输入字符串的第三个字符和第四个字符必须相同。设这个字符的 ASCII 码值为 `x`,则 `v3 = v4 = x`,`v5 = 32 * x`。
#### 4. `((v3 >> 2) ^ v5 ^ ~((v3 >> 2) & v5)) == -3613`
将 `v3 = v4 = x` 和 `v5 = 32 * x` 代入该条件:
设 `y = x >> 2`,则条件变为 `y ^ (32 * x) ^ ~(y & (32 * x)) == -3613`。
通过遍历可能的字符 ASCII 码值(范围是 0 - 255)来求解 `x`:
```c
#include <stdio.h>
int main() {
for (int x = 0; x < 256; x++) {
int y = x >> 2;
int v5 = 32 * x;
if ((y ^ v5 ^ ~(y & v5)) == -3613) {
printf("Found x: %d\n", x);
}
}
return 0;
}
```
运行上述代码可得到满足条件的 `x` 值。
#### 5. `((v5 = (v5 & 0xFFFFFF00) | (unsigned char)a1), ((unsigned char)a1 ^ 0x18) == (unsigned char)v5)`
已知 `a1 = 97`,则 `(unsigned char)a1 ^ 0x18 = 97 ^ 24 = 113`。所以 `v5` 的低 8 位必须为 113。结合前面计算出的 `v5`,通过 `v5 = (v5 & 0xFFFFFF00) | 113` 来调整 `v5` 的低 8 位,进而确定 `a1` 的值。
#### 6. `(double)(int)(__int64)(cos(1.36690694026) * 10000.0 + 0.5) * 0.0001 == (double)v9 * 0.0001`
计算 `(double)(int)(__int64)(cos(1.36690694026) * 10000.0 + 0.5) * 0.0001` 的值:
```c
#include <stdio.h>
#include <math.h>
int main() {
double result = (double)(int)(__int64)(cos(1.36690694026) * 10000.0 + 0.5) * 0.0001;
printf("Result: %f\n", result);
return 0;
}
```
得到结果后,`v9` 必须等于该结果乘以 10000 并四舍五入后的整数。然后将 `v9` 转换为字符串,作为输入字符串从第 6 个字符开始的部分。
### 完整求解代码
```c
#include <stdio.h>
#include <math.h>
int main() {
// 条件 1 和 2
char str;
str = 'H';
str = 'a';
// 条件 3、4
int x;
for (x = 0; x < 256; x++) {
int y = x >> 2;
int v5 = 32 * x;
if ((y ^ v5 ^ ~(y & v5)) == -3613) {
break;
}
}
str = (char)x;
str = (char)x;
// 条件 5
int v5 = 32 * x;
unsigned char target = 97 ^ 0x18;
v5 = (v5 & 0xFFFFFF00) | target;
str = (char)target;
// 条件 6
double result = (double)(int)(__int64)(cos(1.36690694026) * 10000.0 + 0.5) * 0.0001;
int v9 = (int)(result * 10000);
sprintf(str + 5, "%d", v9);
printf("Input string: %s\n", str);
return 0;
}
```
运行上述代码,输出的字符串即为能使原代码输出 `anser_is_true` 的输入字符串。
3、运行,把AI的代码直接运行,得到结果
本帖最后由 工具人丶小方 于 2025-1-30 00:59 编辑
哈哈,Happy2025,用OD写出来的,算是半猜半逆向了,猜了字母'pp',但也能写脚本爆破出来
先用OD智能搜索,找出关键位置,第一个字母和第二个字母很简单,直接就和字母16进制比较,是'H'和'a'
然后下面就是要求第三第四位要要相同,并且满足一个算法,这里我就开始把JNE NOP掉,打算后面再来爆破,最后发现就是Happy中的‘pp’
继续,这里是取你输入的第二个字母'a'与0x18异或,最后与你输入的第五位比较,就是'y'
然后还是新手,所以一些地方还不很了解,下面这段代码好像就是取你最后的几位,然后与计算出来的2025.29676...取整进行对比,最后推出来应该是2025
```
0040107E|.DB4424 10 fild dword ptr ss:
00401082|.DC0D E0904000 fmul qword ptr ds:
00401088|.DD05 D8904000 fld qword ptr ds:
0040108E|.D9FF fcos
00401090|.DC0D D0904000 fmul qword ptr ds:
00401096|.DC05 C8904000 fadd qword ptr ds:
0040109C|.E8 57010000 call DaNianCh.004011F8 ;
```
所以结果就是Happy2025
本来也想用AI分析一下,不过因为是新手,就多练练
然后也祝楼主新年快乐 反馈一个无伤大雅的小bug:
你好像把“很遗憾”打成了“很疑惑”,真是太疑惑了{:301_998:} Happy2025
解题过程如下:
解题步骤就是打开ida,按下f5,问ai
好的,我们来详细分析这段伪代码,它是一个校验函数,目标是找到满足特定条件的输入字符串 `a1`。
**代码结构分析**
1. **函数入口:**
- `int __cdecl sub_401010(char *a1)`:函数名为 `sub_401010`,接收一个字符指针 `a1` 作为输入,返回一个整数。`__cdecl` 表示 C 调用约定。
2. **局部变量:**
- `v2`, `v3`, `v4`, `v5`:整数类型,用于存储从输入字符串 `a1` 中提取的值。
- `v7`:字符指针,指向 `a1` 中某个位置。
- `v8`:整数类型,用于存储从字符串转换来的数值。
- `v9`: 整数类型,用于存储 `a1` 的值。
3. **数据提取:**
- `v2 = *a1;`:`v2` 存储 `a1` 的第一个字符(`a1`)。
- `v3 = a1;`:`v3` 存储 `a1` 的第三个字符。
- `v4 = a1;`:`v4` 存储 `a1` 的第四个字符。
- `v7 = a1 + 5;`:`v7` 指向 `a1` 的第六个字符(`a1`)。
- `v9 = a1;`: `v9` 存储 `a1` 的第二个字符。
- `sscanf(v7, "%d", &v8);`:使用 `sscanf` 从 `v7` 指向的字符串中读取一个整数,并存储到 `v8` 中。这意味着 `a1` 的第六个字符开始应该是一个表示整数的字符串。
4. **校验条件:**
- `if ( v2 == 72 ...)`:这是一个复合的 `if` 条件,只有所有条件都为真,才会执行 `return sub_401140(&unk_40A078);`,否则执行 `return sub_401000(v5);`。
- **条件 1:** `v2 == 72`:`a1` 的第一个字符必须是 ASCII 码为 72 的字符,即 'H'。
- **条件 2:** `v9 == 97`:`a1` 的第二个字符必须是 ASCII 码为 97 的字符,即 'a'。
- **条件 3:** `(v5 = 32 * v4, v3 == v4)`:`v5` 被赋值为 `32 * v4`,并且 `v3` 必须等于 `v4`。即 `a1` 必须等于 `a1`。
- **条件 4:** `((v3 >> 2) ^ v5 ^ ~((v3 >> 2) & v5)) == 0xFFFFF1E3`: 这是一个位运算的条件,涉及到 `v3` 和 `v5`。
- `v3 >> 2`:将 `v3` 右移两位。
- `(v3 >> 2) & v5`:将右移后的 `v3` 与 `v5` 进行按位与运算。
- `~((v3 >> 2) & v5)`:对按位与运算的结果取反。
- `(v3 >> 2) ^ v5 ^ ~((v3 >> 2) & v5)`:将右移后的 `v3`、`v5` 和取反后的结果进行按位异或运算。
- 最终结果必须等于 `0xFFFFF1E3`。
- **条件 5:** `(LOBYTE(v5) = a1, ((unsigned __int8)a1 ^ 0x18) == (_BYTE)v5)`:
- `LOBYTE(v5) = a1`:将 `a1` 的第五个字符(`a1`) 的低 8 位赋值给 `v5` 的低 8 位。
- `((unsigned __int8)a1 ^ 0x18) == (_BYTE)v5`:将 `a1` 的第二个字符(`a1`) 与 0x18 进行异或运算,结果必须等于 `v5` 的低 8 位。
- **条件 6:** `(double)(int)(__int64)(cos(1.36690694026) * 10000.0 + 0.5) * 0.0001 == (double)v8 * 0.0001`:
- `cos(1.36690694026)`: 计算 1.36690694026 的余弦值。
- `cos(1.36690694026) * 10000.0 + 0.5`: 将余弦值乘以 10000.0 并加上 0.5。
- `(int)(__int64)(...)`: 将结果转换为整数。
- `(double)(...) * 0.0001`: 将整数转换为 double 类型,并乘以 0.0001。
- `(double)v8 * 0.0001`: 将 `v8` 转换为 double 类型,并乘以 0.0001。
- 两个计算结果必须相等。
5. **函数返回:**
- 如果所有条件都满足,则调用 `sub_401140(&unk_40A078)` 并返回其结果。
- 否则,调用 `sub_401000(v5)` 并返回其结果。
**解题思路**
为了找到满足条件的输入字符串 `a1`,我们需要反向求解这些条件:
1. **条件 1 和 2:** `a1` 必须是 'H',`a1` 必须是 'a'。
2. **条件 3:** `a1` 必须等于 `a1`。
3. **条件 4:**这是一个位运算的条件,可能需要尝试不同的值来找到满足条件的 `a1` 和 `a1`。
4. **条件 5:** `a1` 必须满足 `a1 ^ 0x18 == a1`,其中 `a1` 是 'a'。
5. **条件 6:**计算 `cos(1.36690694026)` 的值,然后根据公式推导出 `v8` 的值,从而确定 `a1` 从第六个字符开始的字符串。
**具体步骤**
1. **已知字符:** `a1` 的前两个字符是 "Ha"。
2. **条件 5:** `a1` 的低 8 位必须等于 `a1 ^ 0x18`,即 `'a' ^ 0x18`。 'a' 的 ASCII 码是 97,97 ^ 0x18 = 97 ^ 24 = 121,对应字符 'y'。所以 `a1` 是 'y'。
3. **条件 6:**计算 `cos(1.36690694026) * 10000.0 + 0.5` 的值。
- `cos(1.36690694026) ≈ 0.203079`
- `0.203079 * 10000.0 + 0.5 ≈ 2031.29`
- 取整后得到 2031。所以 `v8` 必须是 2031。
4. **条件 3 和 4:**这需要一些尝试。假设 `a1` 和 `a1` 的值都为 0x79 (121),即 'y',计算如下:
- `v3 = v4 = 0x79 = 121`
- `v5 = 32 * v4 = 32 * 121 = 3872`
- `(v3 >> 2) = 121 >> 2 = 30`
- `(v3 >> 2) ^ v5 ^ ~((v3 >> 2) & v5) = 30 ^ 3872 ^ ~(30 & 3872) = 30 ^ 3872 ^ ~0 = 30 ^ 3872 ^ -1 = 3843 ^ -1 = 0xFFFFF1E3`
- 看起来 `a1 = a1 = 'y'` 满足条件4。
5. **构造 `a1`:**结合以上所有信息,`a1` 的形式为 "Hayyy2031"。
**总结**
经过分析,满足校验条件的输入字符串 `a1` 应该是 `Hayyy2031`。
**注意:**
* 实际逆向分析中,可能需要使用调试器来验证中间结果。
* 位运算条件可能需要一些尝试和错误来找到正确的值。
* `cos` 函数的结果可能因浮点数精度而略有不同,需要注意取整操作。
希望这个详细的分析对你有帮助!如果你有其他问题,请随时提出。
本帖最后由 scncrenyong 于 2025-1-30 09:20 编辑
每步都手动分析,人脑还是比不过AI呀!!!
用IDA解析现关键函数
int __cdecl sub_401010(char *a1)
{
int v2; // ebp
int v3; // edi
int v4; // ebx
int v5; // ecx
const char *v7; //
int v8; //
int v9; // BYREF
int v10; //
v2 = *a1;
v3 = a1;
v4 = a1;
v7 = a1 + 5;
v10 = a1;
sscanf(v7, "%d", &v9);
if ( v2 == 'H'
&& v10 == 'a'
&& (v5 = 32 * v4, v3 == v4)
&& ((v3 >> 2) ^ v5 ^ ~((v3 >> 2) & v5)) == 0xFFFFF1E3
&& (LOBYTE(v5) = a1, ((unsigned __int8)a1 ^ 0x18) == (_BYTE)v5)
&& (double)(int)(__int64)(cos(1.36690694026) * 10000.0 + 0.5) * 0.0001 == (double)v9 * 0.0001 )
{
return sub_401140((int)&unk_40A078, v8);
}
else
{
return sub_401000(v5);
}
}
以下是用AI对关键函数做的分析
看样子答案可能不是唯一的
页:
[1]
2