电脑软件(例如 Adobe、Autodesk)为什么那么容易被破解?
作者:DBinary
来自:https://www.zhihu.com/question/356008082/answer/1128751775
先说结论:
理论上不存在无法破解的软件,但并不是说破解软件比开发容易一万倍,naive,防破解的目的不是要做一个无法破解的软件,而是:
让破解软件的成本远大于购买软件的成本,知名软件大多都有实力让软件破解难度大大加大,但出于市场需求和争取用户的角度考虑,会放水!
光说不练假把式,52老司机来给各位简略表演几个有代表性的软件破解,当然,要看懂下面的内容,需要一点点C语言的编程基础。
我们先来一个最最简单的软件破解,假设我们写了一个软件,它的注册手段代码如下:
#include "stdio.h"
#include "string.h"
int main()
char Key[32];
printf("请输入注册码:");
gets(Key);
if (strcmp(Key,"abc123456")==0)
printf("注册成功");
printf("注册失败");
}
把他编译成exe,运行一下。
要破解怎么办呢?
超简单的,你把这个程序后缀改成txt然后打开,搜索注册码,然后翻一翻key就找到了。
别笑,就算是今天,仍然有一大堆软件采用这种软件保护机制,不过这类软件要不大多不怎么值钱比如xx管理系统,计算器之类的,要不就是软件作者别有用心防君子不防小人。
你可能会问了,问题出在那了呢?
这类保护机制的问题是,目前大部分的编译器都会将字符串常量直接存储在可执行文件结构中,所以你要是硬编码key,那么上面这种破解方法几乎是屡试不爽甚至不需要什么逆向破解知识就能搞定。
那么,让我们进入防破解V2.0时代,为了与时俱进,我们稍微把上面的代码改一下:
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
sprintf(Key,"%x",iID*8+123456);
printf("你的机器码是%x\n",iID);
printf("请输入注册码:");
gets(iKey);
if (strcmp(Key,iKey)==0)
MessageBoxA(0,"注册成功","",MB_OK);
MessageBoxA(0,"注册失败","",MB_OK);
}
现在"TXT"破解法已经不顶用了,你看,key找不到了。
怎么样,这种保护手段是不是熟悉的味道熟悉的配方,这个机器码可以从网卡MAC,CPU型号,内存大小等等等等去生成。
当然,key的算法也可以拉上MD5,SHA等等等等手段来弄,而不是简简单单的乘一个8再加上123456,总之从机器码到注册码的算法你能玩出花。
但这又有什么卵用呢?
打开ollydbg,对MessageBoxA下断点(也就是弹窗的函数)。
然后输入一个错误的注册码,命中断点,很快,我们来到了判断注册码是否正确的逻辑处理代码。
你猜猜,要是我们把这个判断注册码是否正确的代码给它删了)(nop指令填充)会怎么样?
你会发现,握草,不管我输入什么,都是注册成功!
那么问题出在哪了呢?
你发现,万恶之源都始于那个MessageBox函数,正是这个函数,让我们顺藤摸瓜找到了注册码判断代码。
关注微信公众号「程序员的成长之路」后台回复「2048」关键字,免费获取5T技术学习资源!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,单片机,树莓派,等等。
时至今日,仍然有非常非常大的一部分软件使用着这个二三十年前就在用的保护手段,每年死于MessageBox被破解的软件,围起来可以绕地球三圈,也就是这个保护机制,成就了所谓软件发布后十分钟就被破解的悲惨结局。
这个时候,你痛定思痛,mmp,有内鬼,既然MessageBox不好用,那我不用就是了,于是你把代码改成下面这样:
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
sprintf(Key,"%x",iID*8+123456);
printf("你的机器码是%x\n",iID);
printf("请输入注册码:");
gets(iKey);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
你看,啧,只要你注册码没输对,我直接把软件退出(或者跳到别的地方),看你怎么办?
可惜,内鬼年年有,"注册成功"几个字还是出卖了你,打开ollydbg,查找字符串参考,然后双击。
哦豁,完蛋,换汤不换药,还是给逮住了。
你察觉到这样一个地方判断注册码实在不靠谱,所以,你改变了策略,把检查注册码的代码复制了n遍,或者逐字符检查注册码的准确性,还有人将注册成功等字样进行加密或混淆,等到要用的时候再取出来。
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
sprintf(Key,"%x",iID*8+123456);
printf("你的机器码是%x\n",iID);
printf("请输入注册码:");
gets(iKey);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
if (strcmp(Key,iKey)==0)
printf("注册成功");
exit(0);
}
你放心,不管你复制多少次,要找出来都是时间问题,吃枣是会被破解的.同时只要你要用到明文字符串你迟早还是要解密的,这种手段类似于加upx压缩壳,只需要等待数据解压完成,所有的东西又都是明文的了。
因此比较聪明的做法是,要用时解密,用完后马上把明文抹掉,这样说不定能拖延更长的时间。
但这仍然也是时间问题。
你开始发现,与其揪出内鬼,不如主动出击,也就是我们著名的与其解决问题不如解决提出问题的人,终于,你开始对调试器下手了,然后你成功进化到防破解V2.5时代。
这个时候,不得不介绍一个老掉牙的但非常有名的函数:
IsDebugPresent
你可能有点懵,这个函数是干啥子用的?
简单来说,当我们破解一个程序的时候,大部分情况下我们会打开一个叫调试器的东西来对软件进行反编译分析,诶,重点就在这。
IsDebugPresent这个函数,就能检测我们的程序有没有被一个调试器附加,你想啊,正常情况我们用软件谁会吃饱撑着附加一个调试器来用,你要是用调试器附加我,你肯定就是想干坏事。
于是,你开始把代码写成这样:
#include "stdio.h"
#include "string.h"
#include "windows.h"
#include "math.h"
int main()
char iKey[32];
char Key[32];
char ID[32];
int iID=0xabc1d3f;
if (IsDebuggerPresent())
MessageBoxA(NULL,"小样,就你还破解我的程序,回家喝奶去吧","",MB_OK);
return 0;
sprintf(Key,"%x",iID*8+123456);
printf("你的机器码是%x\n",iID);
printf("请输入注册码:");
gets(iKey);
if (strcmp(Key,iKey)==0)