这个C语言课程作业实现了一个有趣而危险的功能——"非法用户则自毁程序"。这是一个典型的自我保护机制设计,当程序检测到非法使用条件时(这里以系统时间作为判断依据),会尝试删除自身可执行文件。这种设计在早期的DOS时代较为常见,主要用于软件版权保护或敏感程序的安全销毁。
核心逻辑很简单:程序启动时检查系统时间的分钟数,如果大于30则判定为非法使用,立即执行自毁操作;否则正常继续运行。自毁过程分为三个步骤:
注意:这种自毁机制在现代操作系统中可能无法正常工作,因为大多数系统会锁定正在运行的可执行文件。实际应用中需要更复杂的设计。
原始代码明确标注"在BC31下编译"(Borland C++ 3.1),这是一个1990年代的16位DOS编译器。将其移植到现代编译环境(如GCC、MSVC)时,出现了多个兼容性问题:
c复制// 原始代码
void main(int argc, char* argv[]) {
return; // void返回类型
}
// 修复后
int main(int argc, char* argv[]) {
return 0; // 符合C99/C11标准
}
现代C标准(C99/C11)严格要求main函数返回int类型,用于向操作系统返回程序状态码。0表示成功,非零值表示错误。这是第一个需要修复的语法错误。
c复制// 原始代码
#include <dos.h>
struct time now;
gettime(&now);
// 修复后
#include <time.h>
struct tm *now;
time_t t;
time(&t);
now = localtime(&t);
原始代码使用Borland特有的<dos.h>和struct time,现代标准库使用<time.h>中的time()和localtime()函数组合来获取系统时间。tm结构体的tm_min成员对应分钟数,与原始功能保持一致。
原始代码缺少几个必要的标准库包含:
c复制// 需要添加的头文件
#include <stdlib.h> // 提供exit()函数声明
#include <conio.h> // 提供_getch()函数声明
现代编译器对函数声明要求更严格,所有使用的标准库函数都必须通过包含对应头文件来声明。特别是:
原始代码中存在一个潜在危险:
c复制FILE* fp; // 声明但未初始化
if((errno)&& ( fp != NULL )) // 检查fp是否为NULL
{
fclose(fp); // 如果fp未初始化,这里会导致未定义行为
}
fp指针从未被初始化就直接使用,这在原始代码中可能侥幸工作(旧编译器往往将自动变量初始化为0),但在现代环境中会导致不可预测的行为。正确的做法是:
c复制FILE* fp = NULL; // 显式初始化为NULL
c复制#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h> // 提供unlink()函数
#include <conio.h> // Windows特有,Linux下使用ncurses
int main(int argc, char* argv[])
{
// 获取当前时间
time_t t = time(NULL);
struct tm *now = localtime(&t);
// 检查是否为非法使用时间(分钟>30)
if(now->tm_min > 30)
{
// 尝试修改文件权限为可写
if(chmod(argv[0], S_IWUSR) == 0)
{
// 尝试删除程序文件
if(unlink(argv[0]) == 0)
{
printf("Self-destruct sequence completed.\n");
exit(EXIT_SUCCESS);
}
}
// 如果删除失败
printf("\nYou are an illegal user to run this program!\a\n");
exit(EXIT_FAILURE);
}
// 合法用户继续执行
printf("You are a legitimate user to run this program!\n");
_getch(); // 等待用户按键
return EXIT_SUCCESS;
}
跨平台考虑:
错误处理增强:
安全性改进:
在Windows和Linux等现代操作系统中,正在运行的可执行文件通常会被系统锁定,防止修改。这使得自删除操作变得困难。可能的解决方案:
延迟删除技术:
c复制// Windows下可以使用批处理延迟删除
if(unlink(argv[0]) != 0) {
char cmd[256];
sprintf(cmd, "cmd /C ping 127.0.0.1 -n 2 > nul & del \"%s\"", argv[0]);
system(cmd);
}
使用专用工具:
原始代码仅检查分钟数是否>30,这种保护机制过于简单,容易被绕过。改进方案:
多重验证机制:
c复制// 检查日期、时间、运行环境等多因素
if(now->tm_min > 30 ||
now->tm_year < 120 || // 2020年以后
getenv("VALID_LICENSE") == NULL)
{
// 触发保护
}
加密校验:
这种自毁机制可以应用于:
备份策略:
调试技巧:
c复制// 调试时添加保护开关
#define DEBUG_MODE 1
if(now->tm_min > 30 && !DEBUG_MODE) {
// 自毁代码
}
用户提示:
相比直接删除文件,更安全的做法是:
从技术发展角度看,这个作业展示了几个有趣的技术演进点:
从物理删除到逻辑禁用:
从时间检查到在线验证:
从显式保护到隐式保护:
这个简单的课程作业实际上涉及了软件保护、系统编程、跨平台开发等多个计算机科学的重要领域,是一个很好的学习案例。通过修复和改进这样的传统代码,可以深入理解计算机系统底层原理和技术发展脉络。