[逆向工程]160个CrackMe入门实战之Afkayas.2.Exe解析(三)
一.AfKayAs.2.Exe功能
打开后弹出Nag窗口,过几秒后自动消失。程序主界面是账号序列号登录。
二.目的
去掉Nag窗口
登录爆破
分析算法编写注册机
三.开始破解
1.查壳
32位无壳vb语言
2.vb语言4c方法去掉Nag窗口
1.vb语言一般程序入口点push 后跟call指令
00401170 | 68 D4674000 | push afkayas.2.4067D4 |
00401175 | E8 F0FFFFFF | call <JMP.&ThunRTMain> |
2.数据窗口ctrl+g 4067D4 +4c
3.记下4067D4 +4c 值,数据窗口跳转到该值地址
4.00修改为01 01修改为00
入口地址+4c 后地址为406820 值为406868
数据窗口跳转到406868
双击修改后打补丁另存文件,Nag窗口已被破解掉
3.登录爆破
输入账号序列号弹出错误提示
根据错误提示找到关键跳转点(od中根据可根据VB语言按钮事件ctrl+b 816c24下断点 vb程序弹窗 rtcMsgBox 函数下断点)
详细找关键跳转点可参考[逆向工程]160个CrackMe入门实战之Acid_burn.exe解析(一)一文
找到关键跳转点直接爆破nop填充,保存程序
4.追码分析算法
账号输入:1111 序列号:1234
1.获取1111代码分析:
004081E3 | call __vbaHresultCheckObj ; 验证前一步COM对象操作是否成功
004081E9 | mov edx, [ebp-B0] ; 加载对象指针到EDX (可能是窗体或控件)
004081EF | mov eax, [ebp-1C] ; 加载字符串1地址 (内容为"1111")
004081F2 | push eax ; 压栈字符串1参数
004081F3 | mov ebx, [edx] ; 获取对象虚表指针
004081F5 | call __vbaLenBstr ; 计算字符串1长度 → EAX=4
004081FB | mov edi, eax ; EDI=4 (保存长度结果)
004081FD | mov ecx, [ebp-18] ; 加载字符串2地址 (内容为"1111")
00408200 | imul edi, edi, 15B38 ; EDI=4 * 88888 = 355552
00408206 | push ecx ; 压栈字符串2参数
00408207 | jo 4087C4 ; 检查乘法溢出(若溢出跳错误处理)
0040820D | call Ordinal#516 ; 调用关键函数 → 返回AX=49 (字符'1'的ASCII)
00408213 | movsx edx, ax ; 将16位返回值符号扩展至32位EDX
00408216 | add edi, edx ; EDI=355552 + 49 = 355601
00408218 | jo 4087C4 ; 检查加法溢出
0040821E | push edi ; 压入最终计算结果355601
0040821F | call __vbaStrI4 ; 将整数转为字符串 → EAX="355601"指针
00408225 | mov edx, eax ; EDX=结果字符串指针
00408227 | lea ecx, [ebp-20] ; 加载目标变量地址
0040822A | call __vbaStrMove ; 移动字符串到局部变量[ebp-20]
00408230 | mov edi, [ebp-B0] ; 重新加载对象指针到EDI
00408236 | push eax ; 压入结果字符串指针 (__vbaStrMove返回相同指针)
00408237 | push edi ; 压入对象指针 (this指针)
00408238 | call dword ptr [ebx+A4] ; 调用对象虚表方法(偏移A4)
0040823E | test eax, eax ; 检查方法返回值
00408240 | jge 408254 ; 若返回值>=0 (成功) 则跳转
算法:
结果 = (len("1111") × 88888) + Asc("11111"第一个字符)
= (4 × 88888) + 49
= 355601
2.三次浮点数计算
1.第一次
; 1. 验证前序操作
004082D7 | call __vbaHresultCheckObj ; 验证前一步对象操作成功
004082DD | mov ecx, [ebp-A8] ; 加载对象指针
004082E3 | mov edx, [ebp-18] ; 加载字符串"355601"地址
004082E6 | push edx ; 压栈字符串参数
004082E7 | mov ebx, [ecx] ; 获取对象虚表指针
004082E9 | call __vbaR8Str ; 将字符串转为双精度浮点 → ST0=355601.0
; 2. 浮点运算准备
004082EF | fld dword ptr [401008] ; 加载单精度浮点常量A → ST0=A, ST1=355601.0
004082F5 | cmp dword ptr [409000],0 ; 检查浮点控制字状态
004082FC | jne 408306 ; 非标准状态跳转
004082FE | fdiv dword ptr [40100C] ; ST0 = A / B (标准状态除法)
00408304 | jmp 408311 ; 跳过特殊处理
00408306 | push [40100C] ; 非标准状态处理
0040830C | call <JMP.&_adj_fdiv_m32> ; 调整浮点控制字后除法
00408311 | sub esp,8 ; 栈上分配8字节空间
; 3. 浮点运算与异常检查
00408314 | fnstsw ax ; 保存浮点状态字
00408316 | test al,0D ; 检查浮点异常(PE/UE/IE)
00408318 | jne 4087BF ; 有异常跳错误处理
0040831E | faddp st(1), st(0) ; ST1 = ST1 + ST0 → ST0=355601.0 + (A/B)
00408320 | fnstsw ax ; 再存浮点状态
00408322 | test al,0D ; 再查异常
00408324 | jne 4087BF ; 有异常跳错误处理
; 4. 结果处理与存储
0040832A | fstp qword ptr [esp] ; 存储浮点结果到栈顶
0040832D | call __vbaStrR8 ; 浮点转字符串 → EAX="355603"
00408333 | mov edx, eax ; EDX=结果字符串指针
00408335 | lea ecx, [ebp-1C] ; 目标变量地址[ebp-1C]
00408338 | call __vbaStrMove ; 移动字符串到变量
; 5. 对象方法调用
0040833E | mov [ebp-CC], ebx ; 保存虚表指针
00408344 | mov ebx, [ebp-A8] ; 重载对象指针
0040834A | push eax ; 压入结果字符串
0040834B | mov eax, [ebp-CC] ; 取回虚表指针
00408351 | push ebx ; 压入对象指针(this)
00408352 | call [eax+A4] ; 调用对象方法(虚表偏移A4)
00408358 | test eax, eax ; 检查返回值
0040835A | jge 40836E ; 成功则跳转继续执行
2.第二次
1111
1066807
3.第三次
第三次计算,输入1111,正确值算出是1066822
从以上分析1111的正确码为1066822.至次分析出验证码的算法如下:
计算name长度–name长度*0x15b38+name的第一个字符ANSI码–计算浮点数10.0/5.0=2 --转换为浮点数+2.0–乘以3.0–j减去2–减去-15–得到的值转换为文本为正确序列号
5.注册机编写
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> // 添加math.h头文件以使用浮点运算
int main()
{
char username[50] = {0};
char serial[100] = {0};
printf("Enter Username: ");
scanf("%s", username);
if(strlen(username) < 4) {
printf("Error: Username must be at least 4 characters!\n");
// 添加暂停
printf("Press Enter to exit...");
getchar(); // 清除输入缓冲区残留的换行符
getchar(); // 等待用户按键
return 1;
}
// 新序列号生成算法
int len = strlen(username);
int baseValue = len * 0x15B38 + (int)username[0]; // 长度*0x15B38+首字符ASCII
// 浮点运算部分
float floatValue = (float)baseValue; // 转换为浮点数
float step1 = 10.0f / 5.0f; // 10.0/5.0=2.0
float step2 = floatValue + step1; // 加上2.0
float step3 = step2 * 3.0f; // 乘以3.0
float step4 = step3 - 2.0f; // 减去2
float step5 = step4 - (-15.0f); // 减去-15(相当于加15)
// 将浮点数结果转换为整数序列号
int serialNum = (int)roundf(step5); // 四舍五入取整
sprintf(serial, "%d", serialNum);
printf("Your Serial: %s\n", serial);
// 添加暂停
printf("Press Enter to exit...");
getchar(); // 清除输入缓冲区残留的换行符
getchar(); // 等待用户按键
return 0;
}
//gcc -o afk2.exe afk2.c
注册机测试
如果你觉得本教程对你有帮助,请点赞❤️、收藏⭐、关注支持!欢迎在评论区留言交流技术细节!