程序运行要求注册文件
![]() |
|---|
详细汇编逻辑分析
第一阶段:文件基础检查 (地址: 004010B8-004010D6)
004010B8 | cmp dword ptr ds:[0x402173],0x12 ; 检查文件长度是否≥18字节
004010BF | jl short 004010F7 ; 小于则跳转到失败处理
; 循环统计0x01分隔符数量
004010C1 | mov al,byte ptr ds:[ebx+0x40211A] ; 读取文件字节
004010C7 | cmp al,0 ; 遇到0x00结束循环
004010C9 | je short 004010D3
004010CB | cmp al,1 ; 检查是否为0x01
004010CD | jne short 004010D0
004010CF | inc esi ; 如果是0x01,计数器加1
004010D0 | inc ebx ; 移动到下一个字节
004010D1 | jmp short 004010C1 ; 继续循环
004010D3 | cmp esi,2 ; 检查是否恰好有2个0x01
004010D6 | jl short 004010F7 ; 少于2个则失败
关键要求:
文件大小必须 ≥ 0x12 (18) 字节
文件中必须恰好包含2个值为0x01的分隔符
第二阶段:第一段校验和验证 (地址: 004010DA-004010F5)
004010DA | xor ebx,ebx ; 重置索引
004010DC | mov al,byte ptr ds:[ebx+0x40211A] ; 读取字节
004010E2 | cmp al,0 ; 遇到0x00结束
004010E4 | je short 004010EF
004010E6 | cmp al,1 ; 遇到0x01结束
004010E8 | je short 004010EF
004010EA | add esi,eax ; 累加字节值(排除0x00和0x01)
004010EC | inc ebx
004010ED | jmp short 004010DC
004010EF | cmp esi,0x1D5 ; 检查校验和是否等于469
004010F5 | jne short 004010F7 ; 不等于则失败
关键要求:
-
计算从文件开始到第一个0x01分隔符之间的字节累加和
-
累加时排除0x00和0x01字节
-
校验和必须等于0x1D5 (469)
第三阶段:异或解码用户名 (地址: 00401114-00401139)
00401114 | xor esi,esi ; 重置索引
00401116 | inc ebx ; 跳过第一个0x01分隔符
00401117 | mov al,byte ptr ds:[ebx+0x40211A] ; 读取第一个0x01之后的字节
0040111D | cmp al,0 ; 遇到0x00结束
0040111F | je short 00401139
00401121 | cmp al,1 ; 遇到0x01结束
00401123 | je short 00401139
00401125 | cmp esi,0xF ; 最多处理15个字节
00401128 | jae short 00401139
; 关键异或操作
0040112A | xor al,byte ptr ds:[esi+0x40211A] ; 与文件开头对应位置字节异或
00401130 | mov dword ptr ds:[esi+0x402160],eax ; 存储解码结果
00401136 | inc esi
00401137 | jmp short 00401116
关键逻辑:
-
从第一个0x01分隔符之后开始处理
-
将每个字节与文件开头对应位置的字节进行异或操作
-
异或结果存储在0x402160地址,用于显示用户名
-
最多处理15个字节(0xF)
第四阶段:第二段校验和验证 (地址: 0040113A-00401155)
0040113A | xor esi,esi ; 重置累加器
0040113C | mov al,byte ptr ds:[ebx+0x40211A] ; 读取字节
00401142 | cmp al,0 ; 遇到0x00结束
00401144 | je short 0040114F
00401146 | cmp al,1 ; 遇到0x01跳过(不累加)
00401148 | je short 0040113C
0040114A | add esi,eax ; 累加字节值
0040114C | inc ebx
0040114D | jmp short 0040113C
0040114F | cmp esi,0x1B2 ; 检查校验和是否等于434
00401155 | jne short 004010F7 ; 不等于则失败
关键要求:
-
计算从第二个0x01分隔符之后到文件结束的字节累加和
-
遇到0x00字节结束计算
-
遇到0x01字节跳过(不累加)
-
校验和必须等于0x1B2 (434)
due-cm2.dat文件必须满足以下结构:
[第一段数据] 0x01 [异或编码的用户名数据] [填充数据] 0x01 [第二段数据]
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
// 生成安全随机字节
unsigned char generate_safe_byte() {
return (unsigned char)(rand() % 254 + 2);
}
// 调整数据段校验和(排除0x00和0x01)
int adjust_checksum_exclude_separators(unsigned char* data, int length, int target_sum) {
// 计算当前有效字节的和(排除0x00和0x01)
int current_sum = 0;
for (int idx = 0; idx < length; idx++) {
if (data[idx] != 0x00 && data[idx] != 0x01) {
current_sum += data[idx];
}
}
int difference = current_sum - target_sum;
if (difference == 0) return 1;
// 调整策略:修改非0x00/0x01的字节
if (difference > 0) {
// 需要减小
for (int pos = length - 1; pos >= 0 && difference > 0; pos--) {
if (data[pos] != 0x00 && data[pos] != 0x01 && data[pos] > 2) {
int adjustment = (difference < data[pos] - 2) ? difference : (data[pos] - 2);
data[pos] -= adjustment;
difference -= adjustment;
}
}
} else {
// 需要增加
difference = -difference;
for (int pos = length - 1; pos >= 0 && difference > 0; pos--) {
if (data[pos] != 0x00 && data[pos] != 0x01 && data[pos] < 253) {
int adjustment = (difference < 255 - data[pos]) ? difference : (255 - data[pos]);
data[pos] += adjustment;
difference -= adjustment;
}
}
}
return (difference == 0);
}
// 计算有效字节的和(排除0x00和0x01)
int calculate_valid_sum(unsigned char* data, int length) {
int sum = 0;
for (int idx = 0; idx < length; idx++) {
if (data[idx] != 0x00 && data[idx] != 0x01) {
sum += data[idx];
}
}
return sum;
}
int main() {
printf("=========================================\n");
srand((unsigned int)time(NULL));
unsigned char keyfile_data[100];
size_t data_position = 0;
int success_flag = 1;
// 1. 生成文件开头数据(到第一个0x01之前)
printf("1. 生成文件开头数据...\n");
int header_length = 10 + rand() % 10; // 10-19字节
unsigned char header_section[20];
for (int h = 0; h < header_length; h++) {
header_section[h] = generate_safe_byte();
keyfile_data[data_position++] = header_section[h];
}
// 调整这部分数据的有效字节和为469
if (adjust_checksum_exclude_separators(header_section, header_length, 469)) {
// 更新调整后的数据到keyfile
for (int u = 0; u < header_length; u++) {
keyfile_data[data_position - header_length + u] = header_section[u];
}
printf("调整成功!有效字节和=469\n");
// 验证调整结果
int verified_sum = calculate_valid_sum(keyfile_data, header_length);
printf("验证结果: %d %s\n", verified_sum, verified_sum == 469 ? "?" : "?");
} else {
printf("调整失败!\n");
success_flag = 0;
}
if (success_flag) {
// 2. 添加第一个分隔符
printf("2. 添加第一个分隔符(0x01)\n");
keyfile_data[data_position++] = 0x01;
// 3. 生成异或编码的用户名段
printf("3. 生成用户名编码段...\n");
const char* username = "Test123";
int username_length = strlen(username);
// 关键修正:使用文件开头的字节作为异或密钥
for (int c = 0; c < username_length && c < header_length; c++) {
keyfile_data[data_position++] = username[c] ^ header_section[c];
}
// 4. 添加第二个分隔符
printf("4. 添加第二个分隔符(0x01)\n");
keyfile_data[data_position++] = 0x01;
// 5. 生成文件结尾数据(第二个0x01之后)
printf("5. 生成文件结尾数据...\n");
int footer_length = 8 + rand() % 10; // 8-17字节
unsigned char footer_section[20];
int footer_start = data_position;
for (int f = 0; f < footer_length; f++) {
footer_section[f] = generate_safe_byte();
keyfile_data[data_position++] = footer_section[f];
}
// 调整这部分数据的有效字节和为434
if (adjust_checksum_exclude_separators(footer_section, footer_length, 434)) {
// 更新调整后的数据到keyfile
for (int v = 0; v < footer_length; v++) {
keyfile_data[footer_start + v] = footer_section[v];
}
printf("调整成功!有效字节和=434\n");
// 验证调整结果
int footer_sum = calculate_valid_sum(keyfile_data + footer_start, footer_length);
printf("验证结果: %d %s\n", footer_sum, footer_sum == 434 ? "?" : "?");
} else {
printf("调整失败!\n");
success_flag = 0;
}
}
if (success_flag) {
// 6. 写入文件
printf("6. 写入文件 due-cm2.dat\n");
FILE* file_pointer = fopen("due-cm2.dat", "wb");
if (file_pointer) {
size_t bytes_written = fwrite(keyfile_data, 1, data_position, file_pointer);
fclose(file_pointer);
if (bytes_written == data_position) {
printf("文件写入成功: %d 字节\n", data_position);
// 7. 完整验证生成的文件
printf("\n7. 完整验证文件...\n");
// 查找分隔符位置
int separator_count = 0;
int separator_positions[2] = {-1, -1};
for (size_t s = 0; s < data_position; s++) {
if (keyfile_data[s] == 0x01) {
if (separator_count < 2) {
separator_positions[separator_count] = s;
}
separator_count++;
}
}
printf("分隔符数量: %d %s\n", separator_count, separator_count == 2 ? "?" : "?");
// 验证第一段(到第一个0x01)
int sum_first = 0;
if (separator_positions[0] != -1) {
for (int a = 0; a < separator_positions[0]; a++) {
if (keyfile_data[a] != 0x00 && keyfile_data[a] != 0x01) {
sum_first += keyfile_data[a];
}
}
}
printf("第一段有效字节和: %d/469 %s\n", sum_first, sum_first == 469 ? "?" : "?");
// 验证第二段(第二个0x01之后)
int sum_second = 0;
if (separator_positions[1] != -1) {
for (size_t b = separator_positions[1] + 1; b < data_position; b++) {
if (keyfile_data[b] == 0x00 || keyfile_data[b] == 0x01) break;
sum_second += keyfile_data[b];
}
}
printf("第二段有效字节和: %d/434 %s\n", sum_second, sum_second == 434 ? "?" : "?");
// 验证异或解码
if (separator_positions[0] != -1 && separator_positions[1] != -1) {
printf("异或解码用户名: ");
for (int d = separator_positions[0] + 1, e = 0;
d < separator_positions[1] && e < header_length; d++, e++) {
unsigned char decoded_char = keyfile_data[d] ^ keyfile_data[e];
printf("%c", (decoded_char >= 32 && decoded_char <= 126) ? decoded_char : '?');
}
printf("\n");
}
// 显示文件内容
printf("\n生成的keyfile内容:\n");
for (size_t p = 0; p < data_position; p++) {
printf("%02X ", keyfile_data[p]);
if ((p + 1) % 16 == 0) printf("\n");
}
printf("\n");
if (sum_first == 469 && sum_second == 434 && separator_count == 2) {
printf("\n? 文件验证通过!可以用于CrackMe。\n");
} else {
printf("\n? 文件验证失败!\n");
success_flag = 0;
}
} else {
printf("错误: 文件写入不完整\n");
success_flag = 0;
}
} else {
printf("错误: 无法创建文件\n");
success_flag = 0;
}
}
if (success_flag) {
printf("\n生成完成!请将 due-cm2.dat 复制到 CrackMe 程序目录测试。\n");
} else {
printf("\n生成失败!\n");
}
printf("\n按任意键退出...\n");
getchar();
return success_flag ? 0 : 1;
}
![]() |
|---|
![]() |
|---|


