破解入门学习笔记题四十七
程序运行要求注册文件
![]() |
|---|
详细汇编逻辑分析
第一阶段:文件基础检查 (地址: 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];}// 调整这部分数据的有效字节和为469if (adjust_checksum_exclude_separators(header_section, header_length, 469)) {// 更新调整后的数据到keyfilefor (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];}// 调整这部分数据的有效字节和为434if (adjust_checksum_exclude_separators(footer_section, footer_length, 434)) {// 更新调整后的数据到keyfilefor (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;
}
![]() |
|---|
![]() |
|---|



