VB共享软件防破解设计技术初探(三)
程序员文章站
2022-07-03 21:02:19
第一篇我粗略的讲了以下几个内容:
1、 文件完整性,防止被非法修改
2、 运行时的校验,防止被LOADER
3、 反调试,防止动态跟踪...
第一篇我粗略的讲了以下几个内容:
1、 文件完整性,防止被非法修改
2、 运行时的校验,防止被LOADER
3、 反调试,防止动态跟踪和挂接
4、 防静态反汇编分析
5、 注册码系统(算法部分,核心内容)
6、 加壳防脱壳
7、 隐蔽性设计
8、 另辟蹊径
第二篇我详细得讲解了以下内容:
1、设计思想:水桶原理
2、完整性校验,包括VB-CRC32注射器和主体的编写;文件修改时间自校验;文件大小自校验等
3、防LOADER设计,包括查找标题,经典时值,还有重点的“金蝉脱壳”反LOADER
上节课我忘讲了种常见的反LOADER的方法:释放程序法(借鸡生蛋)
这里赶紧补上:
释放程序法(借鸡生蛋)
原理:讲需要保护的程序作为自定义资源添加到新的VB程序里,新程序一运行就自动释放内部包含的程序到特定位置,然后通过SHELL调用,自身则结束运行,怀疑就是“借鸡生蛋”,也是常用的木马病毒免杀技术。
打开VB6.0 新建工程,然后单击“外接程序”――“外接程序管理器”――“VB 6 资源编辑器”,在右下角选项里选择“加载/卸载”,然后单击“确定”
这时,在VB6.0主程序窗口上方的工具条最后,就会出现一个跟注册表编辑程序挺像的绿色图标,单击它。
如图:
选择自定义添加图标,然后在出现的窗口里,选择你要保护的EXE程序。
如图:
添加完资源后,按下“保存”按钮
这样,资源就被添加为代号为101的数据,同理,就绪添加,就从102开始,一直增长上去,你也可以自己修改代号,方便记忆。
下面我们来写一个自动释放子体的过程SUB
我是代码启示线————————————————————————————————
Private Sub Shifang()
On Error GoTo TakeError
Dim Lujing As String
Lujing = "C:\WINDOWS\system32\缓存.exe"
'定义缓存路径
If Dir(Lujing) = "" Then
'检测子体是否已经存在
'不是的话就直接释放一个即可
Dim Shuzu() As Byte
'定义一个数组储存数据
Shuzu() = LoadResData(101, "CUSTOM")
'加载数据,101代号指的就是我们添加的EXE子体
Open Lujing For Binary As #1
'定义一个缓存路径
Put #1, , Shuzu()
'开始写入
Close #1
'关闭通道
Else
Kill Lujing
'发现已经存在就删除它,然后就重新创建;目的是为了防止有人故意同名替换
'定义一个数组储存数据
Shuzu() = LoadResData(101, "CUSTOM")
'加载数据,101代号指的就是我们添加的EXE子体
Open Lujing For Binary As #1
'定义一个缓存路径
Put #1, , Shuzu()
'开始写入
Close #1
'关闭通道
End If
Shell Lujing, vbNormalFocus
'释放好后就SHELL使其运行,接下去就结束掉自己
End
TakeError:
'一般发生错误是因为子体正在运行,无法删除或者覆盖,或者是因为OD的HIDEOD插件引起SHELL错误
MsgBox "请检查我是否正在运行?或者,是因为调试器?请检查", , "发现问题了"
End
End Sub
我是代码终止线————————————————————————————————
调用的话,在FORM的LOAD事件里,或者其他启动事件里即可,如下:
Private Sub Form_Load()
Shifang
End Sub
看看效果吧!
正常运行,跟运行一个程序感觉上没有什么差别
如果加载OD调试器的话,如果OD刚好加载HIDEOD插件的话,就会提示错误,发现调试器:
如图:
如果关闭HIDEOD插件,就会自动借鸡生蛋,使调试器无法加载正确的子体程序
如图:
需要注意一点,只要够小心的人,一定会发现真正的程序所在,所以子体程序最好加上自删除代码,也就是检测到UNLOAD事件后,自动删除自己,或者调用批处理,隐藏删除自己。
我碰到过的一些用VB写的外挂也就是用这种技术来逃避调试器的。
这种技术就讲解到这里,我们开始新的篇章喽
第三篇我将具体介绍1、VB反调试反跟踪2、防静态反汇编分析
设计加密解密不久,能力有限,见识短浅,请各位高手见谅,有错误和不足之处敬请原谅,并请你阐述你自己的见解,共同完成《VB 共享软件防破解设计技术初探》系列文章,您的参与和支持是我的荣幸和骄傲。
这篇开篇前我把自己想到的VB程序防破解设计图展示下,看看大家想到了什么?*发挥
我心中总有个声音:你设计的只是针对技术层面,无法真的长久的保证你的软件不被破解
实际生活中,我们需要八卦一样的“阴阳鱼”,一切造化的根本。(我在故弄玄虚吧?)
一个是阳刚的“法律”保障,一个是怀柔的“道义”安抚。
在这里引述一篇文章里的片段,具体作者是谁,大家自己查
原文是《如何让你的共享软件在国内赚到每月超过万元》
引用从这里开始————————————————————————————————
四、面对盗版不惊慌
当用户找到了你的软件,下载安装并试用之后,发现使用简单,符合自己的需求,很想要,如果你是用户,显然,你就会去找盗版,如何找?两条途径吗
第一:到搜索引擎中去搜索某某软件后面加一个破解,注册机之类的。结果呢?人家找到了,也就不会买了。另外,还会去盗版碟市场去找,看看有没有破解版的碟。呵呵,第一种情况好对付,你不是在搜索引擎中找吗?作为作者,你也可以用你的正版软件冒充盗版软件到处去发布呀,比如:你可以发布某某软件的破解版,让你的假破解版充斥了整个搜索引擎的结果。当用户下载了10个假破解之后,也自然会想到,这个软件没有破解版了。哦,对了,如果有真破解出现在搜索结果的前几位,也不用着急,首先,打电话给你所在地的公证处,花300元,把你所找到的页面做个公证。(我保证你这300元能够赚回来),然后,想办法找到这个网站的站长,不要管这个网站是个人的还是企业的。联系这个站长,把公证书的复印件,你软件版权的复印件传真给他,限定他在7日内与你进行协商解决。过期,你将在你所在地的人民法院起诉(诉讼费50元),什么?诉状不会写?找我呀,100元,帮你写一份。如何计算赔偿金额?哦,很简单,一般下载站都有提供下载数统计的,用下载量乘以你的软件的价格就是他该赔的金额,(呵呵,算起来很吓人的)。如果你很忙,找个律师是值得的。
第二、如果在盗版碟市场出现了你的软件,那我告诉你,是喜也是忧,喜的是,你的软件很有名吗,忧的是,只有升级,做更好的软件了。辛苦你了。为了避免这种情况的出现,在你的软件中,加上网上认证的加密办法是可行的。加密点千万不要搞在一个地方,另外,为了防止跟踪破解,一个简单的办法,就是认证通过之后,不要弹出对话框之类的,默默的就让用户开始用吧。千万不要弹出什么:恭喜您,您注册成功了,恭喜您,您登陆成功了的对话框。人家找你的加密点,一跟踪这个对话框就行了。呵呵,这只是防破解的小技巧。其他的防破解方法,你可以查阅相关资料。一句话,与破解做斗争,不该妥协。哪怕花费比所得还要高,你也要与破解盗版做斗争,如同抗日一样,和平时期,也要抗日。要用抗日的精神来抵抗破解盗版。什么?你的软件也是用的盗版VC写的?这也没关系,那是另外一回事情,微软没找你麻烦,那是微软的事。并不妨碍你找破解者的麻烦。总不能因为唐朝的时候咋们侵略过别国,就不抵抗日本侵略了吧?
引用到此结束—————————————————————————————————
文中作者告诉我们的是:
1、 定时关注互联网搜索引擎关于你的软件破解信息
2、 到盗版碟市场看看有没有你的软件破解注册版
3、 冒充破解版在网上大量发布,覆盖大部分搜索出来的破解信息资源
4、 利用法律武器,作公证、取证,警告发布破解信息的站点或者论坛或者其他发布者
5、 聘请律师,委托代办,发律师信,甚至对簿公堂。
6、 发现破解版大量存在,那么只能更新软件,加强加密强度。
(如硬件加密狗,反正羊毛出在羊身上…..)
7、 隐藏注册结果信息,让用户用着吧,让破解者徘徊迷路吧
8、 要用抗日的精神来抵抗破解盗版
相信作者能靠共享软件月入万金,在这里由衷的恭喜他发财。
这个讲的就是我要说的法律武器,阳刚十足,捍卫自己的利益。但有时,刚性太强,反而容易折断,高碳钢硬度很大,但是脆;低碳钢硬度比较小,但是柔韧。
这里重点聊聊怀柔“道义”,你的软件的质量,软件的功能要对得起你收的注册费,这个就是道义;
你的售后服务,服务的态度和服务水平要对得起你的承诺,这个就是道义;
你对未注册版的限制要合理,不要给用户带来很不良好的体验和心理感觉,这个就是道义;不能强迫用户注册,动不动就跳到注册网页去了,这个就是道义;
你对某些支持过你,给你提好意见的用户,免费送注册码,这个也是道义;
定期给某些特定用户群发放免费注册码,或者在特定的时候发布特定的纪念版免费回馈社会,这个也是道义;
你的防破解设计里不应该有刺激破解者的语言和指令,如关机、格盘、删除系统文件、摧毁数据等,这个更是道义了。
有这样的软件,它在收取少量的注册费后,将部分收入以公开名义捐赠给四川灾区,这样的软件在道义上他是高尚的,在道义上便已经让很多有良知的高手罢手了。破解者破解了它,发布了它,他将遭到道义上的谴责和唾弃。
《道德经》讲“道可道非常道”,这“道”便是一切,道义也是“道”的范畴。你的软件连道义都没有,先不说会不会激怒破解者和一些不搞破解的逆向分析师来破解你的软件(无他,只因不爽)单单说有没有用户用你的破解版都还是个问题呢。
比如说:江民炸弹
引用从此开始—————————————————————————————————
简介
1997年6月24日王江民先生在其主页上发布了kv300l++版,内含逻辑炸弹。
凡是在mk300v4制作的仿真盘(盗版盘)上执行kv300l++的用户硬盘数据均被破坏,
同时硬盘被锁,软硬盘皆不能启动。
Kv300逻辑炸弹可以造成电脑软硬盘都不能启动的现象,
当时在电脑界引起轰动。这是用常规原理不能解释的现象。
kv300逻辑炸弹表现
1.先破坏文件分配表,然后修改分区表造成硬盘被锁。
2.不做任何备份
3.没有任何提示
4.在特定条件下激发(盗版盘)
5.王江民始终没有公开提供恢复程序;
6.如果用一般常用的修复磁盘工具,如NORTON,会造成不可逆转的损失;
7.对其的恢复类似于cih破坏数据的恢复,因为需要重建分区表。
8.在win95环境下执行同样会造成文件分配表被破坏,数据损失。
处理
1997年7月23日,国内5家反病毒软件公司(以下简称五厂商)在北京联合举行新闻发布会,一起谴责国内另一家著名的反病毒软件公司—北京江民新技术有限责任公司(以下简称江民公司)。在发布会上,五厂商向多家新闻机构发放了一份“联合声明”,称江民公司6月下旬发布的KV300L+ +版反病毒软件(网络下载版)(以下简称KV300,本文无特殊说明,所指的均是这一特定下载版本)中含有“逻辑炸弹”,“在特定条件下对计算机实施破坏,其结果与某些计算机病毒的破坏作用相似……” —7月24日,江民公司对五厂商的“联合声明”做出了强烈反应,认为这时“不正当的侵权行为”,并多次在各专业计算机所刊上登载“严正声明”,江民公司对此的解释是:江民公司并未在KV300中安放任何破坏性程序。五厂商所称的“逻辑炸弹”,其实是江民公司为打击日益猖獗的盗版软件行为而在软件中编制的“逻辑锁”,这一“逻辑锁”首先不可能对任何购买正版产品的用户造成任何影和损失,其次对部分盗版用户也只是起到暂时锁住机器的作用。江民公司特别强调,KV300中的“逻辑锁”与病毒没有关系,因为病毒是具有自我复制和传染性的破坏性程序,而“逻辑锁”却不会对用户数据造成任何伤害1997年9月8日,*部门认定kv300L++事件违反计算机安全保护条例之23条,属于故意输入有害数据,危害计算机信息 系统安全的行为,对其做出罚款3000元的决定
关于KV300 '逻辑锁' 的看法
王江民口口声声说KV300 L++ 里面没有逻辑炸弹, 只有所谓'主动式逻辑锁'. 似乎这样讲就不需要承担责任了!
的确, 在中国有关计算机安全和保护软件消费者的法律还不健全, 王江民的类似行为暂时还无法受到法律的制裁. 但我们需要进行这方面的讨论. 以了解我们消费者的权利和可以采取的措施.
从道义上和法律意义上讲, 王江民的这种做法已经侵犯了公民的权利. 王江民认为盗版应视同小偷, 可以用锁把他锁起来. 但是, 我们应该清楚, 中国法律只付予了警察等执法机关有执法的权利, 你王江民有什么权利执法? 你用逻辑锁把你的软件锁起来, 是你的权利. 但王江民没有权利锁别人的门! 即使, 经过国家有关部门的批准, 王江民有这样的执法权. 王江民也必须经过广告等形式公之于众.
从计算机安全角度讲. 王江民至今没能确认正版用户不会受到意外损害, 他也没有通过适当渠道告知正版用户避免意外损害的方法, 他只是说明受到损害时由他解锁. 这使KV300 的正版用户会因此存在资料失密和损失的危险. 对此, 如何赔偿??
由于, KV 300 内含有可能危害消费者权利和计算机安全的'逻辑锁'. 在目前, 国家没有相关法律的情况下. 我们消费者有权利相互告知并通知KV300 的潜在用户, 选择其他的产品, 放弃KV 300.
引用到此结束—————————————————————————————————
就像坛子里很多人讲的一样“先别急着想着防破解,要把软件功能和质量放在第一位”
呵呵,还好,扯得不是很远,可以把大家的思路引向更广阔的防破解设计上去,我很荣幸。
回到技术层面,下面我们开始讲解:1、VB反调试反跟踪2、防静态反汇编分析
第三章 第一讲
备注:这一讲将是比较长的,请准备好开水茶叶或者咖啡。
VB的反调试设计
这里要讲解的VB反调试有以下几种:
1、 检测父进程反RING3调试器
2、 FindWindow检测敏感窗口
3、 EnumChildWindows枚举所有窗体,检测敏感字符
4、 检测进程名字来排雷
5、 利用IsDebuggerPresent检测调试器
6、 加密字符串,错误提示诱导陷阱
7、 利用SEH进行反跟踪
8、 加VM或者P-CODE编译
9、 直接反汇编修改代码(破解者用的招数,我们也可以用来防守)
10、 隐蔽性设计
11、 步长、时值反调试(包含启动时间检测)
12、 检测内存分配大小反调试
13、 窗口置顶反调试
14、 检测按键,一般是F2(断点)、F7(单步步进)、F8(逐过程步过)、F9(运行)
15、 检测前台窗口反调试
16、 SeDebugPrivilege检测调试器
17、 关键部分算法的时候锁定鼠标干扰调试分析
18、 GetStartupInfo反调试
其实方法很多很多,建议大家购买《加密与解密第三版》里面有些很好的方法,奈何能力有限,经验不足,我还在琢磨中……与君共勉……
第一、 检测父进程反RING3调试器
简要介绍下原理:一般用户都是在WINDOWS桌面下打开我们写的程序,而不是在什么调试器下的,也就是说,调用我们程序的是explorer.exe程序,而不是A.exe也不是B.exe,它们都对应有个ID,我们只需要检测其父亲ID跟explorer.exe的ID是否一致,就说明是否正常打开还是被调试了。
提示,OD的HIDEOD插件里,如果挑上“Process32NEXT”的话,此方法无效。
请打开VB6.0 新建工程
在窗体通用里,复制以下API和其他参数声明
我是代码启始线————————————————————————————————
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapShot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapShot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long
Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Const MAX_PATH As Integer = 260
Const TH32CS_SNAPPROCESS As Long = 2&
Private Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * 1024
End Type
我是代码终止线————————————————————————————————
然后我们在窗体里写个SUB过程,以后直接调用即可。
我是代码起始线————————————————————————————————
Private Sub Fujincheng()
'这个过程是检测父进程的父进程是否是EXPLORE的父进程
Dim Process As PROCESSENTRY32
Dim hSnapShot As Long
Dim XNN As Long
Dim flag As Boolean
Dim mName As String
Dim i As Integer
Dim pid As Long, explorer As Long '注意这2个变量就用来存放2个ID
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&) '建立进程快照
'搜索explorer.exe进程,并获得其ID
If hSnapShot Then
Process.dwSize = 1060
If (Process32First(hSnapShot, Process)) Then '遍历第一个进程,获得PROCESSENTRY32结构
Do
i = InStr(1, Process.szExeFile, Chr(0)) '获得映像名称
mName = LCase(Left(Process.szExeFile, i - 1)) '并转换成小写
If mName = "explorer.exe" Then '是不是explorer.exe
explorer = Process.th32ProcessID
ElseIf mName = LCase(App.EXEName & ".exe") Then '是不是自己
pid = Process.th32ParentProcessID '获得父进程ID
Else
flag = False
End If
Loop Until (Process32Next(hSnapShot, Process) < 1) '遍历所有进程直到返回值为False
End If
XNN = CloseHandle(hSnapShot)
End If
Dim Openit As Long
Openit = OpenProcess(1&, -1&, pid)
If pid <> explorer Then MsgBox "发现父进程调试", , "警告": TerminateProcess Openit, 0
'如果发现父进程不对,就结束掉父进程,对使用HIDEOD中的Process32NEXT的OD无效
End Sub
我是代码终止线————————————————————————————————
我们设计的代码先找出explorer.exe进程的ID号,然后找出本程序的父进程,两者对比,发现不一样就说明很有可能被调试,那么就用OpenProcess打开该进程,用TerminateProcess结束掉该可以进程。
使用方法如下:
Private Sub Form_Load()
Fujincheng
End Sub
效果如图:
说明该方法对待通用RING3级调试器应该都是有效果的
第二、 FindWindow检测敏感窗口
原理简介:一般程序都有标题栏,也就是程序最上面的那一栏,该栏显示的就是FindWindow可以查到的字符。我们以此来检测黑名单即可简单检测到调试,继而作出响应。
请打开VB6.0 新建工程
在窗体通用里,复制以下API声明
Private Declare Function FINDWINDOW Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
然后在窗体代码处,我们写一个过程即可实现调用:
我是代码起始线————————————————————————————————
Private Sub GuanbiFindwindow(Mingzi As String)
winHwnd = FINDWINDOW(vbNullString, Mingzi)
'获得窗口句柄
If winHwnd <> 0 Then
AppActivate Mingzi
'激活窗体为活动
SendKeys "%{f4}", True
'ALT+F4 结束掉
SendKeys "%y", True
Else
End If
End Sub
我是代码终止线————————————————————————————————
如何调用?请看:
Private Sub Form_Load()
GuanbiFindwindow ("计算器")
'关闭计算器是为了方便大家验证
GuanbiFindwindow ("Numega SmartCheck")
'这样调用即可,不要放到按钮事件,如果不小心激活了我们自己的窗体,那就结束不掉调试进程了
End Sub
实验效果表明,可以结束掉我们规定好的窗口,效果不错。
第三、 EnumChildWindows枚举所有窗体,检测敏感字符
简要原理:
可以用EnumChildWindows来枚举窗体,检测到所有相关敏感字眼,即可判断为调试,继而作出响应。为什么要用这个呢?你有没有发现OD调试器版本真的很多,尤其是修改版的,基本上,高手都喜欢收藏些特别点的调试器,标题栏都不一样,如果用FINDWINDOW来查找的话,还不郁闷死啊?
但是你又发现了没?基本OD打开后,或者开始调试后,标题栏里总有“CPU”这三个字符,
而SMARTCHECK大都包含smart字样?所以我们可以用EnumChildWindows来枚举所有窗口标题栏来检测它们中有没有包含CPU三个字,如果有,宁可错杀,也不放过。
请打开VB6.0 新建工程 添加个TIMER1 定时500 建立个模块
在模块中添加:
我是代码起始线————————————————————————————————
Declare Function GetDesktopWindow Lib "user32" () As Long
Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
'针对EnumChildProc写的自动处理函数
Dim sSave As String
sSave = Space$(GetWindowTextLength(hwnd) + 1)
GetWindowText hwnd, sSave, Len(sSave)
sSave = Left$(sSave, Len(sSave) - 1)
sSave = Trim(sSave)
If JianCPU(sSave) = True Then MsgBox "发现调试器,请关闭", , "警告"
EnumChildProc = 1
End Function
Function JianCPU(abcdef As String) As Boolean
'检测字符集里有没有cpu或者smart字样,返回TRUE表示包含
JianCPU = False
Dim nnn As Long
For nnn = 1 To Len(abcdef) - 2
If LCase(Mid(abcdef, nnn, 3)) = "cpu" Then
JianCPU = True
Exit For
End If
Next nnn
For nnn = 1 To Len(abcdef) - 4
If LCase(Mid(abcdef, nnn, 5)) = "smart" Then
JianCPU = True
Exit For
End If
Next nnn
End Function
Public Sub MeiJu()
'具体整合成为反调试利器
On Error Resume Next
EnumChildWindows GetDesktopWindow, AddressOf EnumChildProc, ByVal 0&
End Sub
我是代码终止线————————————————————————————————
然后在窗体代码中,怎样调用呢?如下:
Private Sub Form_Load()
'具体使用
MeiJu
End Sub
Private Sub Timer1_Timer()
'具体使用
MeiJu
End Sub
即可,怎么样简单吧?
为什么要搞个定时器呢?那当然,因为我们要时刻检测全部窗体的敏感字符,我认为有些网吧*迅雷和“破解”关键字眼,凡是发现有包含的,就直接关闭窗口,也应该是采用类似的技巧的吧?
看效果吧:
第四、 检测进程名字来排雷
不好意思,凌晨3点多,有些打盹了。尽量把这些提到的讲完,没有提到的请大家专研解决。
简要原理:同样是检测进程,只是比较黑名单而已,不是很简单吗?
打开VB6.0 新建工程
在通用部分复制以下变量声明和参数:
我是代码起始线————————————————————————————————
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * 1024
End Type
Const TH32CS_SNAPHEAPLIST = &H1
Const TH32CS_SNAPPROCESS = &H2
Const TH32CS_SNAPTHREAD = &H4
Const TH32CS_SNAPMODULE = &H8
Const TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)
Const TH32CS_INHERIT = &H80000000
我是代码终止线————————————————————————————————
然后写个函数:
我是代码起始线————————————————————————————————
Private Function Jincheng(namex As String) As Boolean
'编写个函数方便调用,namex是要检测的程序名,小写,返回TRUE表示发现
Dim my As PROCESSENTRY32
Dim l As Long
Dim l1 As Long
Dim mName As String
Dim i As Integer
l = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
If l Then
my.dwSize = 1060
If (Process32First(l, my)) Then '遍历第一个进程
Do
i = InStr(1, my.szExeFile, Chr(0))
mName = LCase(Left(my.szExeFile, i - 1))
If mName = namex Then
Jincheng = True
Exit Function
Else
Jincheng = False
End If
Loop Until (Process32Next(l, my) < 1) '遍历所有进程知道返回值为False
End If
l1 = CloseHandle(l)
End If
End Function
我是代码终止线————————————————————————————————
怎样调用?请看:
我是代码起始线————————————————————————————————
Private Sub Form_Load()
If Jincheng("ollydbg.exe") = True Then MsgBox "发现OLLYDBG调试器,请关闭", , "进程检测"
'如此调用即可
End Sub
我是代码终止线————————————————————————————————
效果如图:
第五、 利用IsDebuggerPresent检测调试器
这个算很老的东西了,拿出来给初学者看看
打开VB6.0 新建工程
在通用部分写:
我是代码起始线————————————————————————————————
Private Declare Function IsDebuggerPresent Lib "kernel32" () As Long
我是代码终止线————————————————————————————————
调用:
Private Sub Form_Load()
If IsDebuggerPresent Then MsgBox "发现调试器了,好累啊", , "凌晨3:36"
End Sub
效果见图:(对SMARTCHECK还有效果,对OD基本无效了)
第六、 加密字符串,错误提示诱导陷阱
原理:破解者在破解VB软件的时候,有的高手会查找宽字的十六进制码,然后在OD中直接搜索如“注册成功”对应的编码,然后上下翻找,爆破什么的都来了,噩梦啊。
所以为了反调试,你最好不要提示注册成功之类的,如果有什么字符容易泄露你的算法信息,那你必须把它隐藏,可以用Chrw线转成数字形式,然后用的时候直接Ascw转回字符即可实现隐藏,但是还是可以被人找到,最好的办法是实现动态解码。
我只简单的举个例子:字母F的ASCII码(十进制)为70,我用ASC(70)比直接用F好,但是ASC(70)也容易被查到,我建议是通过动态计算得出:A = 34 B =36 C = Chr(A+B)
这样就安全多了,但是设计Chr 和Chrw 的函数已经被人所知,也容易被人断点。但是我们还是要尽最大努力去阻止破解者。
我们随便设计一个实验下:
Private Function ZiFujiemi(anum As Long, bnum As Long) As String
'字符解密函数,用这个函数之前可以用相反的逆算来得到加密后的形式
ZiFujiemi = ChrW(anum Xor bnum)
End Function
Private Sub Command1_Click()
MsgBox ZiFujiemi(64, 22)
'大写字母V的ASCII为86,可拆成64 XOR 22,我的意思你能明白就好了
End Sub
可见只要是计算的,都比原字符要保险,中间进行转换即可。加密后,用VBExplorer就分析不出我们的原始字符串了。
那错误陷阱呢?恰恰相反,我们要设计一个捕捉CRACKER虫的全套,比方说引诱破解者进入你设计好的,可以注册成功,但是是假成功,而且尽量不给真正的提示,那如何提高陷阱的捕捉率呢?
可以主动设置好字符串提示,如:“注册码错误”,“注册码正确”,“恭喜你”,“已注册”,“未注册”等等。讲破解着的眼球吸引过来,进入一个貌似算法的,而且算法也挺逼真的地方,注册完后也在标题栏或者什么地方显示个“已注册”字样,其实功能未解禁,^_^
打开VB6.0 新建工程 添加一个名为“注册”的按钮,该按钮就是用来迷惑破解者的。
编写以下一个SUB
我是代码起始线————————————————————————————————
Private Sub Jiazhuce()
'这是一个假注册陷阱
Dim aJia As String
Dim bJia As String
Dim cJia As String
Dim dJia As Double
Dim nxunhuan As Integer
aJia = Text1.Text
For nxunhuan = 1 To Len(Text1.Text)
aJia = aJia & Asc(Mid(aJia, nxunhuan, 1))
dJia = Val(aJia)
Next nxunhuan
If Val(dijia) = 57894321649498# Then
cJia = "注册码正确,恭喜你"
MsgBox cJia, , "感谢"
Else
bJia = "注册码错误,请重新输入"
MsgBox bJia, , "错误"
End If
End Sub
我是代码终止线————————————————————————————————
真正的注册过程在这里:
我是代码起始线————————————————————————————————
Private Sub Form_Unload(Cancel As Integer)
‘注册过程设计在关闭程序的瞬间,或者重新启动的时候验证,这里注册码是123456
If Val(Text1.Text) = 123456 Then MsgBox ChrW(30495) & ChrW(27491) & ChrW(25104) & ChrW(21151), , ChrW(25552) & ChrW(31034)
End Sub
我是代码终止线————————————————————————————————
按钮的代码调用:
Private Sub Command1_Click()
Jiazhuce
'这就是个典型的假圈套,用字符串吸引破解者注意
'其实真正的注册过程在UNLOAD事件中
End Sub
看看效果图吧:
字符查看软件显示了我们故意释放出来的假象
这个例子很简单,但是原理希望初学者能专研下哦
第七、 利用SEH 反调试
简介:SEH 是什么?你就简单得理解为异常处理机制好了,可以这样认为:没有调试器存在的时候,我们实现设计好的处理错误的程序代码就携带者注册算法什么的正常运行;如果有调试器开着,那么,就自动把异常错误递交给调试器来处理,也就不运行我们事先安排好的处理代码了,这样就起到反调试作用。
更多信息请自行网络搜索
打开VB6.0 新建工程,再添加一个FORM2,新建模块
在模块中添加:
我是代码起始线————————————————————————————————
Private Type EXCEPTION_POINTERS
pExceptionRecord As Long
ContextRecord As Long
End Type
Public Declare Function SetUnhandledExceptionFilter Lib "kernel32" (ByVal lpTopLevelExceptionFilter As Long) As Long
Public Function NewExceptionHandler(ByRef lpExceptionPointers As EXCEPTION_POINTERS) As Long
'在这里可以添加一些关键算法
'一些关键步骤都可以放这里
If Val(Form1.Text1.Text) = 123456 Then
MsgBox "注册码真正正确", , "恭喜": Form2.Label1.Caption = "注册成功": Form1.Hide: Form2.Show
Else
MsgBox "真实比较,注册码错误", , "错误"
End If
Resume Next
End Function
我是代码终止线————————————————————————————————
在FORM2中建立一个LABEL1,添加代码:
Private Sub Form_Unload(Cancel As Integer)
Unload Form1
End Sub
在FORM1中添加一个TEXT1和一个COMMAND1
添加如下代码:
我是代码起始线————————————————————————————————
Private Declare Sub DebugBreak Lib "kernel32" ()
Private Sub Command1_Click()
On Error GoTo BACK
DebugBreak
'产生异常的INT指令
'凡是流程顺这下来的都是错误的,不管怎样都是假注册圈套
'然后恰恰是调试器接手了这块,于是就变成了往下运行
Dim a As Long
Dim b As Long
a = 123456 + Val(Text1.Text)
b = a Xor 6543215
b = b + a
If b = 0 Then
'凡是流程顺着来到这里的都死错误的,不管怎样都是假注册圈套
MsgBox "虚假比较,注册码错误", , "提示"
Else
MsgBox "虚假比较,注册码错误", , "错误"
End If
BACK:
End Sub
Private Sub Form_Load()
SetUnhandledExceptionFilter AddressOf NewExceptionHandler
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetUnhandledExceptionFilter 0&
Unload Form2
End Sub
我是代码终止线————————————————————————————————
这样我们就简单的完成了一个VB编写的SEH反调试程序
一些关键的解密算法最好放到异常中去执行,然后判断等,而有调试器的状态下,会被调试器捕获异常,进而改变流程。起到了一定的反调试作用。
我们来尝试测试一下:
正常情况,输入注册码错误:
正常情况,注册成功
调试器状态下:
注意要这个SEH的效果的话,OD调试设置里不能选中“忽略INT3中断”。
从上述测试中我们看到:有调试器调试的时候,破解者被引到了我们设计好的假冒的算法部分,而真实的算法则因SEH作用而不再执行。
当然,你也可以自己精心设计一个异常,让调试器去捕获,让破解者中招。
第八、加VM或者P-CODE编译
加VM的话,其原理是虚拟执行加密的代码,模拟了一个新的环境,让破解者云里雾里,而且一句简单的代码可以发胖好多倍,一句话:累死你。
VMProtect是当前一款很强的虚拟机保护软件,经过VMProtect处理的软件,能大大提高破解者的分析成本,是目前一种比较理想的保护方式。VMProtect关键是用好,一定要将程序关键代码进行处理,必要时用OllyDBG检查一下被处理的代码。
另外,经虚拟机处理代码效率会降低,因此一些对效率要求比较高的代码就不适合用VMProtect进行处理。
关于VM怎么用,我只简单的讲了:用VMProtect打开你的VB程序,设置好要加密的地址简单加密即可,VMProtect 1.2版以后支持SDK,我们在Vb里可以这样标记保护的开始地址和结束地址:
标记开始:
Call VarPtr(“VMProtect begin”)
标记结束:
Call VarPtr(“VMProtect end”)
这样能更有效得定位和保护我们的算法部分。
实际生活中,VB程序虚拟环境用的最多的是P-CODE编译模式,准确的讲是解析执行。
在VB6.0里按照上述的图片设置编译方式即可,编译出来的VB程序基本上是在VB的DLL里穿来穿去,云里雾里,如果再配合异常处理,陷阱,反调试和修改程序代码的话,真的很难破解了。
第九、直接反汇编修改代码(破解者用的招数,我们也可以用来防守)
编译好一个程序后,为了防止VBExplorer等静态分析工具查看到我们的具体事件地址等,有必要修改下VB程序开始的代码,利用的就是平衡堆栈和JMP等指令,加上点经典的时值反调试,效果就出来了,你甚至可以利用这个技术,把自己关键的算法也给搬家了,东一个家,西一个家,让破解者东奔西走,疲惫不堪。
004018CC >/$ 68 7C1D4000 PUSH 1112.00401D7C
004018D1 |. E8 F0FFFFFF CALL <JMP.&MSVBVM60.#100>
这个明显透露了VB程序,我们要做的是搬走这个位置
换句话说是把这个CALL移走,移到天南海角去……
004018D6 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018D8 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018DA |. 0000 ADD BYTE PTR DS:[EAX],AL
004018DC |. 3000 XOR BYTE PTR DS:[EAX],AL
004018DE |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E0 |. 3800 CMP BYTE PTR DS:[EAX],AL
004018E2 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E4 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E6 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E8 |. 3A28 CMP CH,BYTE PTR DS:[EAX]
004018EA |. 8A00 MOV AL,BYTE PTR DS:[EAX]
004018EC \. CF IRETD
修改后:―――――――――――――――――――――――》》》
004018CC > $ /E9 7FA50100 JMP 了凡第一.0041BE50
004018D1 |90 NOP
004018D2 |90 NOP
004018D3 |90 NOP
004018D4 |90 NOP
004018D5 |90 NOP
0041BE50 60 PUSHAD
0041BE51 0F31 RDTSC
经典时值,单步跟,不小心的话就会进入到错误地址
0041BE53 8BC8 MOV ECX,EAX
0041BE55 0F31 RDTSC
0041BE57 2BC1 SUB EAX,ECX
0041BE59 3D 00050000 CMP EAX,500
0041BE5E ^ 0F8F BAF9FFFF JG 了凡第一.0041B81E
0041BE64 83F8 00 CMP EAX,0
0041BE67 ^ 0F8C C7F9FFFF JL 了凡第一.0041B834
0041BE6D 61 POPAD
0041BE6E 68 68504000 PUSH 了凡第一.00405068
0041BE73 60 PUSHAD
0041BE74 0F31 RDTSC
0041BE76 8BC8 MOV ECX,EAX
0041BE78 0F31 RDTSC
0041BE7A 2BC1 SUB EAX,ECX
0041BE7C 3D 00050000 CMP EAX,500
0041BE81 ^ 0F8F ADF9FFFF JG 了凡第一.0041B834
0041BE87 83F8 00 CMP EAX,0
0041BE8A ^ 0F8C 8EF9FFFF JL 了凡第一.0041B81E
0041BE90 61 POPAD
0041BE91 E8 305AFEFF CALL <JMP.&MSVBVM60.#100>
搬到这里来了
0041BE96 ^ 0F85 3A5AFEFF JNZ 了凡第一.004018D6
0041BE9C ^ 0F84 345AFEFF JE 了凡第一.004018D6
效果:
RDTSC指令是什么意思?当然是检测时间用的,不少壳代码使用RDTSC来检测运行时间,如果在OD里调试,时间必定要延长,这样壳利用两次RDTSC时间差就可发现被调试器。
如果有时间,你可以自己设计得更恐怖点,东一句西一句,让别人无法还原
第十、 隐蔽性设计
调试者如果已经分析到了你的算法部分,你怎么办?
所以事先还得有准备,也就是隐蔽性设计。
一段算法,在一般情况下,不执行正确的算法验证,除非已经悄悄得验证了注册码前面几位或者后面几位正确的时候,才悄悄得执行正确的算法验证。
这样可以在一定程度上让破解者感到郁闷,和无所适从。
打开VB6.0 新建工程 新建TEXT1和TEXT2 还有添加一个COMMAND1
在代码窗口我们编写如下注册过程SUB
我是代码起始线————————————————————————————————
Sub ZhuCe(name As String, code As String)
'设计一个隐蔽算法的反调试注册过程
'可用真正注册码:
'name: maplescg
'code: 844123456
'假注册码:
'name: maplescg
'code: 14245066571
Dim Nxunhuan As Integer
Dim Yinbi As Double
Dim Zhongzhuan As String
Dim Zhang As Double
Dim Mihuo As String
On Error GoTo Yes
If Len(name) < 5 Then MsgBox "用户名长度不能小于5位", , "提示": Exit Sub
For Nxunhuan = 1 To Len(name)
Yinbi = Yinbi + Asc(Mid(name, Nxunhuan, 1))
Next Nxunhuan
'循环取用户名所有字母的ASC值之和
Zhongzhuan = Yinbi
If Len(code) <= Len((Zhongzhuan)) Then MsgBox "注册码长度不够", , "提示": Exit Sub
Zhang = Yinbi Xor Val(Left(code, Len(Zhongzhuan)))
'将上述值跟输入注册码左边开始,相同长度的数值XOR
Zhang = Log(Zhang)
'对XOR值进行LOG运算,注意LOG有个特点是什么?
'当ZHANG为零的时候会出现一个数值为5的异常
'我们这里就是利用这个特点来设计隐蔽的注册过程来
'反跟踪。
'请问,破解者跟到这里了,他怎么会知道这里必须
'产生一个异常值为5的异常才能跟到正确的注册过程,
'如果不出现5异常的话,也没什么现象可以给他分析的
'他只能继续分析下去,就像走一条大路,他根本就没发现
'必须走小路才有收获,而一直走下去,貌似正确,实际被耍了
Mihuo = Right(Zhang, 8)
If Right(code, Len(code) - Len(Zhongzhuan)) = Mihuo Then
MsgBox "假注册成功,无语了吧?", , "^_^"
Else
MsgBox "假注册码错误,落入陷阱", , "^_^"
End If
Exit Sub
Yes:
If Err.Number = 5 Then
'检测异常类型是否为5,若为5说明是LOG异常了,表示可以进行正确的注册算法过程了
'如果不是5,那就处理一般性的异常处理事务。
'__________
'这里开始可以嵌入我们真正的算法了
If Right(code, Len(code) - Len(Zhongzhuan)) = "123456" Then
MsgBox "真正注册成功", , "学会了吧?"
End If
'真正算法里面最好不要出现太明显的提示信息,所以这里我把注册失败给提示给去掉了
'__________
Else
MsgBox "不好意思,请检测输入是否有问题", , "出错了"
End If
End Sub
我是代码终止线————————————————————————————————
调用如下:
我是代码起始线————————————————————————————————
Private Sub Command1_Click()
ZhuCe Text1.Text, Text2.Text
'可用真正注册码:
'name: maplescg
'code: 844123456
'假注册码:
'name: maplescg
'code: 14245066571
End Sub
我是代码终止线————————————————————————————————
看明白了吧?原来是这样的注册反调试过程:
如图:
正常状况下,维持左边正常假注册过程,只有在三叉路口,实现了代号为5的异常的时候,才执行右边正确的算法过程,而破解者基本看不明白在哪里有跳转,因为没有出现JMP这类的代码,而关键的LOG也被认为是计算注册码的必要函数,其实我们只是拿它来实现特定条件下的跳转工作。
我们把这个决定分支启动的关键点称为“门”。“门”要么
1、 文件完整性,防止被非法修改
2、 运行时的校验,防止被LOADER
3、 反调试,防止动态跟踪和挂接
4、 防静态反汇编分析
5、 注册码系统(算法部分,核心内容)
6、 加壳防脱壳
7、 隐蔽性设计
8、 另辟蹊径
第二篇我详细得讲解了以下内容:
1、设计思想:水桶原理
2、完整性校验,包括VB-CRC32注射器和主体的编写;文件修改时间自校验;文件大小自校验等
3、防LOADER设计,包括查找标题,经典时值,还有重点的“金蝉脱壳”反LOADER
上节课我忘讲了种常见的反LOADER的方法:释放程序法(借鸡生蛋)
这里赶紧补上:
释放程序法(借鸡生蛋)
原理:讲需要保护的程序作为自定义资源添加到新的VB程序里,新程序一运行就自动释放内部包含的程序到特定位置,然后通过SHELL调用,自身则结束运行,怀疑就是“借鸡生蛋”,也是常用的木马病毒免杀技术。
打开VB6.0 新建工程,然后单击“外接程序”――“外接程序管理器”――“VB 6 资源编辑器”,在右下角选项里选择“加载/卸载”,然后单击“确定”
这时,在VB6.0主程序窗口上方的工具条最后,就会出现一个跟注册表编辑程序挺像的绿色图标,单击它。
如图:
选择自定义添加图标,然后在出现的窗口里,选择你要保护的EXE程序。
如图:
添加完资源后,按下“保存”按钮
这样,资源就被添加为代号为101的数据,同理,就绪添加,就从102开始,一直增长上去,你也可以自己修改代号,方便记忆。
下面我们来写一个自动释放子体的过程SUB
我是代码启示线————————————————————————————————
Private Sub Shifang()
On Error GoTo TakeError
Dim Lujing As String
Lujing = "C:\WINDOWS\system32\缓存.exe"
'定义缓存路径
If Dir(Lujing) = "" Then
'检测子体是否已经存在
'不是的话就直接释放一个即可
Dim Shuzu() As Byte
'定义一个数组储存数据
Shuzu() = LoadResData(101, "CUSTOM")
'加载数据,101代号指的就是我们添加的EXE子体
Open Lujing For Binary As #1
'定义一个缓存路径
Put #1, , Shuzu()
'开始写入
Close #1
'关闭通道
Else
Kill Lujing
'发现已经存在就删除它,然后就重新创建;目的是为了防止有人故意同名替换
'定义一个数组储存数据
Shuzu() = LoadResData(101, "CUSTOM")
'加载数据,101代号指的就是我们添加的EXE子体
Open Lujing For Binary As #1
'定义一个缓存路径
Put #1, , Shuzu()
'开始写入
Close #1
'关闭通道
End If
Shell Lujing, vbNormalFocus
'释放好后就SHELL使其运行,接下去就结束掉自己
End
TakeError:
'一般发生错误是因为子体正在运行,无法删除或者覆盖,或者是因为OD的HIDEOD插件引起SHELL错误
MsgBox "请检查我是否正在运行?或者,是因为调试器?请检查", , "发现问题了"
End
End Sub
我是代码终止线————————————————————————————————
调用的话,在FORM的LOAD事件里,或者其他启动事件里即可,如下:
Private Sub Form_Load()
Shifang
End Sub
看看效果吧!
正常运行,跟运行一个程序感觉上没有什么差别
如果加载OD调试器的话,如果OD刚好加载HIDEOD插件的话,就会提示错误,发现调试器:
如图:
如果关闭HIDEOD插件,就会自动借鸡生蛋,使调试器无法加载正确的子体程序
如图:
需要注意一点,只要够小心的人,一定会发现真正的程序所在,所以子体程序最好加上自删除代码,也就是检测到UNLOAD事件后,自动删除自己,或者调用批处理,隐藏删除自己。
我碰到过的一些用VB写的外挂也就是用这种技术来逃避调试器的。
这种技术就讲解到这里,我们开始新的篇章喽
第三篇我将具体介绍1、VB反调试反跟踪2、防静态反汇编分析
设计加密解密不久,能力有限,见识短浅,请各位高手见谅,有错误和不足之处敬请原谅,并请你阐述你自己的见解,共同完成《VB 共享软件防破解设计技术初探》系列文章,您的参与和支持是我的荣幸和骄傲。
这篇开篇前我把自己想到的VB程序防破解设计图展示下,看看大家想到了什么?*发挥
我心中总有个声音:你设计的只是针对技术层面,无法真的长久的保证你的软件不被破解
实际生活中,我们需要八卦一样的“阴阳鱼”,一切造化的根本。(我在故弄玄虚吧?)
一个是阳刚的“法律”保障,一个是怀柔的“道义”安抚。
在这里引述一篇文章里的片段,具体作者是谁,大家自己查
原文是《如何让你的共享软件在国内赚到每月超过万元》
引用从这里开始————————————————————————————————
四、面对盗版不惊慌
当用户找到了你的软件,下载安装并试用之后,发现使用简单,符合自己的需求,很想要,如果你是用户,显然,你就会去找盗版,如何找?两条途径吗
第一:到搜索引擎中去搜索某某软件后面加一个破解,注册机之类的。结果呢?人家找到了,也就不会买了。另外,还会去盗版碟市场去找,看看有没有破解版的碟。呵呵,第一种情况好对付,你不是在搜索引擎中找吗?作为作者,你也可以用你的正版软件冒充盗版软件到处去发布呀,比如:你可以发布某某软件的破解版,让你的假破解版充斥了整个搜索引擎的结果。当用户下载了10个假破解之后,也自然会想到,这个软件没有破解版了。哦,对了,如果有真破解出现在搜索结果的前几位,也不用着急,首先,打电话给你所在地的公证处,花300元,把你所找到的页面做个公证。(我保证你这300元能够赚回来),然后,想办法找到这个网站的站长,不要管这个网站是个人的还是企业的。联系这个站长,把公证书的复印件,你软件版权的复印件传真给他,限定他在7日内与你进行协商解决。过期,你将在你所在地的人民法院起诉(诉讼费50元),什么?诉状不会写?找我呀,100元,帮你写一份。如何计算赔偿金额?哦,很简单,一般下载站都有提供下载数统计的,用下载量乘以你的软件的价格就是他该赔的金额,(呵呵,算起来很吓人的)。如果你很忙,找个律师是值得的。
第二、如果在盗版碟市场出现了你的软件,那我告诉你,是喜也是忧,喜的是,你的软件很有名吗,忧的是,只有升级,做更好的软件了。辛苦你了。为了避免这种情况的出现,在你的软件中,加上网上认证的加密办法是可行的。加密点千万不要搞在一个地方,另外,为了防止跟踪破解,一个简单的办法,就是认证通过之后,不要弹出对话框之类的,默默的就让用户开始用吧。千万不要弹出什么:恭喜您,您注册成功了,恭喜您,您登陆成功了的对话框。人家找你的加密点,一跟踪这个对话框就行了。呵呵,这只是防破解的小技巧。其他的防破解方法,你可以查阅相关资料。一句话,与破解做斗争,不该妥协。哪怕花费比所得还要高,你也要与破解盗版做斗争,如同抗日一样,和平时期,也要抗日。要用抗日的精神来抵抗破解盗版。什么?你的软件也是用的盗版VC写的?这也没关系,那是另外一回事情,微软没找你麻烦,那是微软的事。并不妨碍你找破解者的麻烦。总不能因为唐朝的时候咋们侵略过别国,就不抵抗日本侵略了吧?
引用到此结束—————————————————————————————————
文中作者告诉我们的是:
1、 定时关注互联网搜索引擎关于你的软件破解信息
2、 到盗版碟市场看看有没有你的软件破解注册版
3、 冒充破解版在网上大量发布,覆盖大部分搜索出来的破解信息资源
4、 利用法律武器,作公证、取证,警告发布破解信息的站点或者论坛或者其他发布者
5、 聘请律师,委托代办,发律师信,甚至对簿公堂。
6、 发现破解版大量存在,那么只能更新软件,加强加密强度。
(如硬件加密狗,反正羊毛出在羊身上…..)
7、 隐藏注册结果信息,让用户用着吧,让破解者徘徊迷路吧
8、 要用抗日的精神来抵抗破解盗版
相信作者能靠共享软件月入万金,在这里由衷的恭喜他发财。
这个讲的就是我要说的法律武器,阳刚十足,捍卫自己的利益。但有时,刚性太强,反而容易折断,高碳钢硬度很大,但是脆;低碳钢硬度比较小,但是柔韧。
这里重点聊聊怀柔“道义”,你的软件的质量,软件的功能要对得起你收的注册费,这个就是道义;
你的售后服务,服务的态度和服务水平要对得起你的承诺,这个就是道义;
你对未注册版的限制要合理,不要给用户带来很不良好的体验和心理感觉,这个就是道义;不能强迫用户注册,动不动就跳到注册网页去了,这个就是道义;
你对某些支持过你,给你提好意见的用户,免费送注册码,这个也是道义;
定期给某些特定用户群发放免费注册码,或者在特定的时候发布特定的纪念版免费回馈社会,这个也是道义;
你的防破解设计里不应该有刺激破解者的语言和指令,如关机、格盘、删除系统文件、摧毁数据等,这个更是道义了。
有这样的软件,它在收取少量的注册费后,将部分收入以公开名义捐赠给四川灾区,这样的软件在道义上他是高尚的,在道义上便已经让很多有良知的高手罢手了。破解者破解了它,发布了它,他将遭到道义上的谴责和唾弃。
《道德经》讲“道可道非常道”,这“道”便是一切,道义也是“道”的范畴。你的软件连道义都没有,先不说会不会激怒破解者和一些不搞破解的逆向分析师来破解你的软件(无他,只因不爽)单单说有没有用户用你的破解版都还是个问题呢。
比如说:江民炸弹
引用从此开始—————————————————————————————————
简介
1997年6月24日王江民先生在其主页上发布了kv300l++版,内含逻辑炸弹。
凡是在mk300v4制作的仿真盘(盗版盘)上执行kv300l++的用户硬盘数据均被破坏,
同时硬盘被锁,软硬盘皆不能启动。
Kv300逻辑炸弹可以造成电脑软硬盘都不能启动的现象,
当时在电脑界引起轰动。这是用常规原理不能解释的现象。
kv300逻辑炸弹表现
1.先破坏文件分配表,然后修改分区表造成硬盘被锁。
2.不做任何备份
3.没有任何提示
4.在特定条件下激发(盗版盘)
5.王江民始终没有公开提供恢复程序;
6.如果用一般常用的修复磁盘工具,如NORTON,会造成不可逆转的损失;
7.对其的恢复类似于cih破坏数据的恢复,因为需要重建分区表。
8.在win95环境下执行同样会造成文件分配表被破坏,数据损失。
处理
1997年7月23日,国内5家反病毒软件公司(以下简称五厂商)在北京联合举行新闻发布会,一起谴责国内另一家著名的反病毒软件公司—北京江民新技术有限责任公司(以下简称江民公司)。在发布会上,五厂商向多家新闻机构发放了一份“联合声明”,称江民公司6月下旬发布的KV300L+ +版反病毒软件(网络下载版)(以下简称KV300,本文无特殊说明,所指的均是这一特定下载版本)中含有“逻辑炸弹”,“在特定条件下对计算机实施破坏,其结果与某些计算机病毒的破坏作用相似……” —7月24日,江民公司对五厂商的“联合声明”做出了强烈反应,认为这时“不正当的侵权行为”,并多次在各专业计算机所刊上登载“严正声明”,江民公司对此的解释是:江民公司并未在KV300中安放任何破坏性程序。五厂商所称的“逻辑炸弹”,其实是江民公司为打击日益猖獗的盗版软件行为而在软件中编制的“逻辑锁”,这一“逻辑锁”首先不可能对任何购买正版产品的用户造成任何影和损失,其次对部分盗版用户也只是起到暂时锁住机器的作用。江民公司特别强调,KV300中的“逻辑锁”与病毒没有关系,因为病毒是具有自我复制和传染性的破坏性程序,而“逻辑锁”却不会对用户数据造成任何伤害1997年9月8日,*部门认定kv300L++事件违反计算机安全保护条例之23条,属于故意输入有害数据,危害计算机信息 系统安全的行为,对其做出罚款3000元的决定
关于KV300 '逻辑锁' 的看法
王江民口口声声说KV300 L++ 里面没有逻辑炸弹, 只有所谓'主动式逻辑锁'. 似乎这样讲就不需要承担责任了!
的确, 在中国有关计算机安全和保护软件消费者的法律还不健全, 王江民的类似行为暂时还无法受到法律的制裁. 但我们需要进行这方面的讨论. 以了解我们消费者的权利和可以采取的措施.
从道义上和法律意义上讲, 王江民的这种做法已经侵犯了公民的权利. 王江民认为盗版应视同小偷, 可以用锁把他锁起来. 但是, 我们应该清楚, 中国法律只付予了警察等执法机关有执法的权利, 你王江民有什么权利执法? 你用逻辑锁把你的软件锁起来, 是你的权利. 但王江民没有权利锁别人的门! 即使, 经过国家有关部门的批准, 王江民有这样的执法权. 王江民也必须经过广告等形式公之于众.
从计算机安全角度讲. 王江民至今没能确认正版用户不会受到意外损害, 他也没有通过适当渠道告知正版用户避免意外损害的方法, 他只是说明受到损害时由他解锁. 这使KV300 的正版用户会因此存在资料失密和损失的危险. 对此, 如何赔偿??
由于, KV 300 内含有可能危害消费者权利和计算机安全的'逻辑锁'. 在目前, 国家没有相关法律的情况下. 我们消费者有权利相互告知并通知KV300 的潜在用户, 选择其他的产品, 放弃KV 300.
引用到此结束—————————————————————————————————
就像坛子里很多人讲的一样“先别急着想着防破解,要把软件功能和质量放在第一位”
呵呵,还好,扯得不是很远,可以把大家的思路引向更广阔的防破解设计上去,我很荣幸。
回到技术层面,下面我们开始讲解:1、VB反调试反跟踪2、防静态反汇编分析
第三章 第一讲
备注:这一讲将是比较长的,请准备好开水茶叶或者咖啡。
VB的反调试设计
这里要讲解的VB反调试有以下几种:
1、 检测父进程反RING3调试器
2、 FindWindow检测敏感窗口
3、 EnumChildWindows枚举所有窗体,检测敏感字符
4、 检测进程名字来排雷
5、 利用IsDebuggerPresent检测调试器
6、 加密字符串,错误提示诱导陷阱
7、 利用SEH进行反跟踪
8、 加VM或者P-CODE编译
9、 直接反汇编修改代码(破解者用的招数,我们也可以用来防守)
10、 隐蔽性设计
11、 步长、时值反调试(包含启动时间检测)
12、 检测内存分配大小反调试
13、 窗口置顶反调试
14、 检测按键,一般是F2(断点)、F7(单步步进)、F8(逐过程步过)、F9(运行)
15、 检测前台窗口反调试
16、 SeDebugPrivilege检测调试器
17、 关键部分算法的时候锁定鼠标干扰调试分析
18、 GetStartupInfo反调试
其实方法很多很多,建议大家购买《加密与解密第三版》里面有些很好的方法,奈何能力有限,经验不足,我还在琢磨中……与君共勉……
第一、 检测父进程反RING3调试器
简要介绍下原理:一般用户都是在WINDOWS桌面下打开我们写的程序,而不是在什么调试器下的,也就是说,调用我们程序的是explorer.exe程序,而不是A.exe也不是B.exe,它们都对应有个ID,我们只需要检测其父亲ID跟explorer.exe的ID是否一致,就说明是否正常打开还是被调试了。
提示,OD的HIDEOD插件里,如果挑上“Process32NEXT”的话,此方法无效。
请打开VB6.0 新建工程
在窗体通用里,复制以下API和其他参数声明
我是代码启始线————————————————————————————————
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapShot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapShot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId As Long) As Long
Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Const MAX_PATH As Integer = 260
Const TH32CS_SNAPPROCESS As Long = 2&
Private Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * 1024
End Type
我是代码终止线————————————————————————————————
然后我们在窗体里写个SUB过程,以后直接调用即可。
我是代码起始线————————————————————————————————
Private Sub Fujincheng()
'这个过程是检测父进程的父进程是否是EXPLORE的父进程
Dim Process As PROCESSENTRY32
Dim hSnapShot As Long
Dim XNN As Long
Dim flag As Boolean
Dim mName As String
Dim i As Integer
Dim pid As Long, explorer As Long '注意这2个变量就用来存放2个ID
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&) '建立进程快照
'搜索explorer.exe进程,并获得其ID
If hSnapShot Then
Process.dwSize = 1060
If (Process32First(hSnapShot, Process)) Then '遍历第一个进程,获得PROCESSENTRY32结构
Do
i = InStr(1, Process.szExeFile, Chr(0)) '获得映像名称
mName = LCase(Left(Process.szExeFile, i - 1)) '并转换成小写
If mName = "explorer.exe" Then '是不是explorer.exe
explorer = Process.th32ProcessID
ElseIf mName = LCase(App.EXEName & ".exe") Then '是不是自己
pid = Process.th32ParentProcessID '获得父进程ID
Else
flag = False
End If
Loop Until (Process32Next(hSnapShot, Process) < 1) '遍历所有进程直到返回值为False
End If
XNN = CloseHandle(hSnapShot)
End If
Dim Openit As Long
Openit = OpenProcess(1&, -1&, pid)
If pid <> explorer Then MsgBox "发现父进程调试", , "警告": TerminateProcess Openit, 0
'如果发现父进程不对,就结束掉父进程,对使用HIDEOD中的Process32NEXT的OD无效
End Sub
我是代码终止线————————————————————————————————
我们设计的代码先找出explorer.exe进程的ID号,然后找出本程序的父进程,两者对比,发现不一样就说明很有可能被调试,那么就用OpenProcess打开该进程,用TerminateProcess结束掉该可以进程。
使用方法如下:
Private Sub Form_Load()
Fujincheng
End Sub
效果如图:
说明该方法对待通用RING3级调试器应该都是有效果的
第二、 FindWindow检测敏感窗口
原理简介:一般程序都有标题栏,也就是程序最上面的那一栏,该栏显示的就是FindWindow可以查到的字符。我们以此来检测黑名单即可简单检测到调试,继而作出响应。
请打开VB6.0 新建工程
在窗体通用里,复制以下API声明
Private Declare Function FINDWINDOW Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
然后在窗体代码处,我们写一个过程即可实现调用:
我是代码起始线————————————————————————————————
Private Sub GuanbiFindwindow(Mingzi As String)
winHwnd = FINDWINDOW(vbNullString, Mingzi)
'获得窗口句柄
If winHwnd <> 0 Then
AppActivate Mingzi
'激活窗体为活动
SendKeys "%{f4}", True
'ALT+F4 结束掉
SendKeys "%y", True
Else
End If
End Sub
我是代码终止线————————————————————————————————
如何调用?请看:
Private Sub Form_Load()
GuanbiFindwindow ("计算器")
'关闭计算器是为了方便大家验证
GuanbiFindwindow ("Numega SmartCheck")
'这样调用即可,不要放到按钮事件,如果不小心激活了我们自己的窗体,那就结束不掉调试进程了
End Sub
实验效果表明,可以结束掉我们规定好的窗口,效果不错。
第三、 EnumChildWindows枚举所有窗体,检测敏感字符
简要原理:
可以用EnumChildWindows来枚举窗体,检测到所有相关敏感字眼,即可判断为调试,继而作出响应。为什么要用这个呢?你有没有发现OD调试器版本真的很多,尤其是修改版的,基本上,高手都喜欢收藏些特别点的调试器,标题栏都不一样,如果用FINDWINDOW来查找的话,还不郁闷死啊?
但是你又发现了没?基本OD打开后,或者开始调试后,标题栏里总有“CPU”这三个字符,
而SMARTCHECK大都包含smart字样?所以我们可以用EnumChildWindows来枚举所有窗口标题栏来检测它们中有没有包含CPU三个字,如果有,宁可错杀,也不放过。
请打开VB6.0 新建工程 添加个TIMER1 定时500 建立个模块
在模块中添加:
我是代码起始线————————————————————————————————
Declare Function GetDesktopWindow Lib "user32" () As Long
Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
'针对EnumChildProc写的自动处理函数
Dim sSave As String
sSave = Space$(GetWindowTextLength(hwnd) + 1)
GetWindowText hwnd, sSave, Len(sSave)
sSave = Left$(sSave, Len(sSave) - 1)
sSave = Trim(sSave)
If JianCPU(sSave) = True Then MsgBox "发现调试器,请关闭", , "警告"
EnumChildProc = 1
End Function
Function JianCPU(abcdef As String) As Boolean
'检测字符集里有没有cpu或者smart字样,返回TRUE表示包含
JianCPU = False
Dim nnn As Long
For nnn = 1 To Len(abcdef) - 2
If LCase(Mid(abcdef, nnn, 3)) = "cpu" Then
JianCPU = True
Exit For
End If
Next nnn
For nnn = 1 To Len(abcdef) - 4
If LCase(Mid(abcdef, nnn, 5)) = "smart" Then
JianCPU = True
Exit For
End If
Next nnn
End Function
Public Sub MeiJu()
'具体整合成为反调试利器
On Error Resume Next
EnumChildWindows GetDesktopWindow, AddressOf EnumChildProc, ByVal 0&
End Sub
我是代码终止线————————————————————————————————
然后在窗体代码中,怎样调用呢?如下:
Private Sub Form_Load()
'具体使用
MeiJu
End Sub
Private Sub Timer1_Timer()
'具体使用
MeiJu
End Sub
即可,怎么样简单吧?
为什么要搞个定时器呢?那当然,因为我们要时刻检测全部窗体的敏感字符,我认为有些网吧*迅雷和“破解”关键字眼,凡是发现有包含的,就直接关闭窗口,也应该是采用类似的技巧的吧?
看效果吧:
第四、 检测进程名字来排雷
不好意思,凌晨3点多,有些打盹了。尽量把这些提到的讲完,没有提到的请大家专研解决。
简要原理:同样是检测进程,只是比较黑名单而已,不是很简单吗?
打开VB6.0 新建工程
在通用部分复制以下变量声明和参数:
我是代码起始线————————————————————————————————
Private Declare Function CreateToolhelp32Snapshot Lib "kernel32" (ByVal dwFlags As Long, ByVal th32ProcessID As Long) As Long
Private Declare Function Process32First Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function Process32Next Lib "kernel32" (ByVal hSnapshot As Long, lppe As PROCESSENTRY32) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Type PROCESSENTRY32
dwSize As Long
cntUsage As Long
th32ProcessID As Long
th32DefaultHeapID As Long
th32ModuleID As Long
cntThreads As Long
th32ParentProcessID As Long
pcPriClassBase As Long
dwFlags As Long
szExeFile As String * 1024
End Type
Const TH32CS_SNAPHEAPLIST = &H1
Const TH32CS_SNAPPROCESS = &H2
Const TH32CS_SNAPTHREAD = &H4
Const TH32CS_SNAPMODULE = &H8
Const TH32CS_SNAPALL = (TH32CS_SNAPHEAPLIST Or TH32CS_SNAPPROCESS Or TH32CS_SNAPTHREAD Or TH32CS_SNAPMODULE)
Const TH32CS_INHERIT = &H80000000
我是代码终止线————————————————————————————————
然后写个函数:
我是代码起始线————————————————————————————————
Private Function Jincheng(namex As String) As Boolean
'编写个函数方便调用,namex是要检测的程序名,小写,返回TRUE表示发现
Dim my As PROCESSENTRY32
Dim l As Long
Dim l1 As Long
Dim mName As String
Dim i As Integer
l = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
If l Then
my.dwSize = 1060
If (Process32First(l, my)) Then '遍历第一个进程
Do
i = InStr(1, my.szExeFile, Chr(0))
mName = LCase(Left(my.szExeFile, i - 1))
If mName = namex Then
Jincheng = True
Exit Function
Else
Jincheng = False
End If
Loop Until (Process32Next(l, my) < 1) '遍历所有进程知道返回值为False
End If
l1 = CloseHandle(l)
End If
End Function
我是代码终止线————————————————————————————————
怎样调用?请看:
我是代码起始线————————————————————————————————
Private Sub Form_Load()
If Jincheng("ollydbg.exe") = True Then MsgBox "发现OLLYDBG调试器,请关闭", , "进程检测"
'如此调用即可
End Sub
我是代码终止线————————————————————————————————
效果如图:
第五、 利用IsDebuggerPresent检测调试器
这个算很老的东西了,拿出来给初学者看看
打开VB6.0 新建工程
在通用部分写:
我是代码起始线————————————————————————————————
Private Declare Function IsDebuggerPresent Lib "kernel32" () As Long
我是代码终止线————————————————————————————————
调用:
Private Sub Form_Load()
If IsDebuggerPresent Then MsgBox "发现调试器了,好累啊", , "凌晨3:36"
End Sub
效果见图:(对SMARTCHECK还有效果,对OD基本无效了)
第六、 加密字符串,错误提示诱导陷阱
原理:破解者在破解VB软件的时候,有的高手会查找宽字的十六进制码,然后在OD中直接搜索如“注册成功”对应的编码,然后上下翻找,爆破什么的都来了,噩梦啊。
所以为了反调试,你最好不要提示注册成功之类的,如果有什么字符容易泄露你的算法信息,那你必须把它隐藏,可以用Chrw线转成数字形式,然后用的时候直接Ascw转回字符即可实现隐藏,但是还是可以被人找到,最好的办法是实现动态解码。
我只简单的举个例子:字母F的ASCII码(十进制)为70,我用ASC(70)比直接用F好,但是ASC(70)也容易被查到,我建议是通过动态计算得出:A = 34 B =36 C = Chr(A+B)
这样就安全多了,但是设计Chr 和Chrw 的函数已经被人所知,也容易被人断点。但是我们还是要尽最大努力去阻止破解者。
我们随便设计一个实验下:
Private Function ZiFujiemi(anum As Long, bnum As Long) As String
'字符解密函数,用这个函数之前可以用相反的逆算来得到加密后的形式
ZiFujiemi = ChrW(anum Xor bnum)
End Function
Private Sub Command1_Click()
MsgBox ZiFujiemi(64, 22)
'大写字母V的ASCII为86,可拆成64 XOR 22,我的意思你能明白就好了
End Sub
可见只要是计算的,都比原字符要保险,中间进行转换即可。加密后,用VBExplorer就分析不出我们的原始字符串了。
那错误陷阱呢?恰恰相反,我们要设计一个捕捉CRACKER虫的全套,比方说引诱破解者进入你设计好的,可以注册成功,但是是假成功,而且尽量不给真正的提示,那如何提高陷阱的捕捉率呢?
可以主动设置好字符串提示,如:“注册码错误”,“注册码正确”,“恭喜你”,“已注册”,“未注册”等等。讲破解着的眼球吸引过来,进入一个貌似算法的,而且算法也挺逼真的地方,注册完后也在标题栏或者什么地方显示个“已注册”字样,其实功能未解禁,^_^
打开VB6.0 新建工程 添加一个名为“注册”的按钮,该按钮就是用来迷惑破解者的。
编写以下一个SUB
我是代码起始线————————————————————————————————
Private Sub Jiazhuce()
'这是一个假注册陷阱
Dim aJia As String
Dim bJia As String
Dim cJia As String
Dim dJia As Double
Dim nxunhuan As Integer
aJia = Text1.Text
For nxunhuan = 1 To Len(Text1.Text)
aJia = aJia & Asc(Mid(aJia, nxunhuan, 1))
dJia = Val(aJia)
Next nxunhuan
If Val(dijia) = 57894321649498# Then
cJia = "注册码正确,恭喜你"
MsgBox cJia, , "感谢"
Else
bJia = "注册码错误,请重新输入"
MsgBox bJia, , "错误"
End If
End Sub
我是代码终止线————————————————————————————————
真正的注册过程在这里:
我是代码起始线————————————————————————————————
Private Sub Form_Unload(Cancel As Integer)
‘注册过程设计在关闭程序的瞬间,或者重新启动的时候验证,这里注册码是123456
If Val(Text1.Text) = 123456 Then MsgBox ChrW(30495) & ChrW(27491) & ChrW(25104) & ChrW(21151), , ChrW(25552) & ChrW(31034)
End Sub
我是代码终止线————————————————————————————————
按钮的代码调用:
Private Sub Command1_Click()
Jiazhuce
'这就是个典型的假圈套,用字符串吸引破解者注意
'其实真正的注册过程在UNLOAD事件中
End Sub
看看效果图吧:
字符查看软件显示了我们故意释放出来的假象
这个例子很简单,但是原理希望初学者能专研下哦
第七、 利用SEH 反调试
简介:SEH 是什么?你就简单得理解为异常处理机制好了,可以这样认为:没有调试器存在的时候,我们实现设计好的处理错误的程序代码就携带者注册算法什么的正常运行;如果有调试器开着,那么,就自动把异常错误递交给调试器来处理,也就不运行我们事先安排好的处理代码了,这样就起到反调试作用。
更多信息请自行网络搜索
打开VB6.0 新建工程,再添加一个FORM2,新建模块
在模块中添加:
我是代码起始线————————————————————————————————
Private Type EXCEPTION_POINTERS
pExceptionRecord As Long
ContextRecord As Long
End Type
Public Declare Function SetUnhandledExceptionFilter Lib "kernel32" (ByVal lpTopLevelExceptionFilter As Long) As Long
Public Function NewExceptionHandler(ByRef lpExceptionPointers As EXCEPTION_POINTERS) As Long
'在这里可以添加一些关键算法
'一些关键步骤都可以放这里
If Val(Form1.Text1.Text) = 123456 Then
MsgBox "注册码真正正确", , "恭喜": Form2.Label1.Caption = "注册成功": Form1.Hide: Form2.Show
Else
MsgBox "真实比较,注册码错误", , "错误"
End If
Resume Next
End Function
我是代码终止线————————————————————————————————
在FORM2中建立一个LABEL1,添加代码:
Private Sub Form_Unload(Cancel As Integer)
Unload Form1
End Sub
在FORM1中添加一个TEXT1和一个COMMAND1
添加如下代码:
我是代码起始线————————————————————————————————
Private Declare Sub DebugBreak Lib "kernel32" ()
Private Sub Command1_Click()
On Error GoTo BACK
DebugBreak
'产生异常的INT指令
'凡是流程顺这下来的都是错误的,不管怎样都是假注册圈套
'然后恰恰是调试器接手了这块,于是就变成了往下运行
Dim a As Long
Dim b As Long
a = 123456 + Val(Text1.Text)
b = a Xor 6543215
b = b + a
If b = 0 Then
'凡是流程顺着来到这里的都死错误的,不管怎样都是假注册圈套
MsgBox "虚假比较,注册码错误", , "提示"
Else
MsgBox "虚假比较,注册码错误", , "错误"
End If
BACK:
End Sub
Private Sub Form_Load()
SetUnhandledExceptionFilter AddressOf NewExceptionHandler
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetUnhandledExceptionFilter 0&
Unload Form2
End Sub
我是代码终止线————————————————————————————————
这样我们就简单的完成了一个VB编写的SEH反调试程序
一些关键的解密算法最好放到异常中去执行,然后判断等,而有调试器的状态下,会被调试器捕获异常,进而改变流程。起到了一定的反调试作用。
我们来尝试测试一下:
正常情况,输入注册码错误:
正常情况,注册成功
调试器状态下:
注意要这个SEH的效果的话,OD调试设置里不能选中“忽略INT3中断”。
从上述测试中我们看到:有调试器调试的时候,破解者被引到了我们设计好的假冒的算法部分,而真实的算法则因SEH作用而不再执行。
当然,你也可以自己精心设计一个异常,让调试器去捕获,让破解者中招。
第八、加VM或者P-CODE编译
加VM的话,其原理是虚拟执行加密的代码,模拟了一个新的环境,让破解者云里雾里,而且一句简单的代码可以发胖好多倍,一句话:累死你。
VMProtect是当前一款很强的虚拟机保护软件,经过VMProtect处理的软件,能大大提高破解者的分析成本,是目前一种比较理想的保护方式。VMProtect关键是用好,一定要将程序关键代码进行处理,必要时用OllyDBG检查一下被处理的代码。
另外,经虚拟机处理代码效率会降低,因此一些对效率要求比较高的代码就不适合用VMProtect进行处理。
关于VM怎么用,我只简单的讲了:用VMProtect打开你的VB程序,设置好要加密的地址简单加密即可,VMProtect 1.2版以后支持SDK,我们在Vb里可以这样标记保护的开始地址和结束地址:
标记开始:
Call VarPtr(“VMProtect begin”)
标记结束:
Call VarPtr(“VMProtect end”)
这样能更有效得定位和保护我们的算法部分。
实际生活中,VB程序虚拟环境用的最多的是P-CODE编译模式,准确的讲是解析执行。
在VB6.0里按照上述的图片设置编译方式即可,编译出来的VB程序基本上是在VB的DLL里穿来穿去,云里雾里,如果再配合异常处理,陷阱,反调试和修改程序代码的话,真的很难破解了。
第九、直接反汇编修改代码(破解者用的招数,我们也可以用来防守)
编译好一个程序后,为了防止VBExplorer等静态分析工具查看到我们的具体事件地址等,有必要修改下VB程序开始的代码,利用的就是平衡堆栈和JMP等指令,加上点经典的时值反调试,效果就出来了,你甚至可以利用这个技术,把自己关键的算法也给搬家了,东一个家,西一个家,让破解者东奔西走,疲惫不堪。
004018CC >/$ 68 7C1D4000 PUSH 1112.00401D7C
004018D1 |. E8 F0FFFFFF CALL <JMP.&MSVBVM60.#100>
这个明显透露了VB程序,我们要做的是搬走这个位置
换句话说是把这个CALL移走,移到天南海角去……
004018D6 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018D8 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018DA |. 0000 ADD BYTE PTR DS:[EAX],AL
004018DC |. 3000 XOR BYTE PTR DS:[EAX],AL
004018DE |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E0 |. 3800 CMP BYTE PTR DS:[EAX],AL
004018E2 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E4 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E6 |. 0000 ADD BYTE PTR DS:[EAX],AL
004018E8 |. 3A28 CMP CH,BYTE PTR DS:[EAX]
004018EA |. 8A00 MOV AL,BYTE PTR DS:[EAX]
004018EC \. CF IRETD
修改后:―――――――――――――――――――――――》》》
004018CC > $ /E9 7FA50100 JMP 了凡第一.0041BE50
004018D1 |90 NOP
004018D2 |90 NOP
004018D3 |90 NOP
004018D4 |90 NOP
004018D5 |90 NOP
0041BE50 60 PUSHAD
0041BE51 0F31 RDTSC
经典时值,单步跟,不小心的话就会进入到错误地址
0041BE53 8BC8 MOV ECX,EAX
0041BE55 0F31 RDTSC
0041BE57 2BC1 SUB EAX,ECX
0041BE59 3D 00050000 CMP EAX,500
0041BE5E ^ 0F8F BAF9FFFF JG 了凡第一.0041B81E
0041BE64 83F8 00 CMP EAX,0
0041BE67 ^ 0F8C C7F9FFFF JL 了凡第一.0041B834
0041BE6D 61 POPAD
0041BE6E 68 68504000 PUSH 了凡第一.00405068
0041BE73 60 PUSHAD
0041BE74 0F31 RDTSC
0041BE76 8BC8 MOV ECX,EAX
0041BE78 0F31 RDTSC
0041BE7A 2BC1 SUB EAX,ECX
0041BE7C 3D 00050000 CMP EAX,500
0041BE81 ^ 0F8F ADF9FFFF JG 了凡第一.0041B834
0041BE87 83F8 00 CMP EAX,0
0041BE8A ^ 0F8C 8EF9FFFF JL 了凡第一.0041B81E
0041BE90 61 POPAD
0041BE91 E8 305AFEFF CALL <JMP.&MSVBVM60.#100>
搬到这里来了
0041BE96 ^ 0F85 3A5AFEFF JNZ 了凡第一.004018D6
0041BE9C ^ 0F84 345AFEFF JE 了凡第一.004018D6
效果:
RDTSC指令是什么意思?当然是检测时间用的,不少壳代码使用RDTSC来检测运行时间,如果在OD里调试,时间必定要延长,这样壳利用两次RDTSC时间差就可发现被调试器。
如果有时间,你可以自己设计得更恐怖点,东一句西一句,让别人无法还原
第十、 隐蔽性设计
调试者如果已经分析到了你的算法部分,你怎么办?
所以事先还得有准备,也就是隐蔽性设计。
一段算法,在一般情况下,不执行正确的算法验证,除非已经悄悄得验证了注册码前面几位或者后面几位正确的时候,才悄悄得执行正确的算法验证。
这样可以在一定程度上让破解者感到郁闷,和无所适从。
打开VB6.0 新建工程 新建TEXT1和TEXT2 还有添加一个COMMAND1
在代码窗口我们编写如下注册过程SUB
我是代码起始线————————————————————————————————
Sub ZhuCe(name As String, code As String)
'设计一个隐蔽算法的反调试注册过程
'可用真正注册码:
'name: maplescg
'code: 844123456
'假注册码:
'name: maplescg
'code: 14245066571
Dim Nxunhuan As Integer
Dim Yinbi As Double
Dim Zhongzhuan As String
Dim Zhang As Double
Dim Mihuo As String
On Error GoTo Yes
If Len(name) < 5 Then MsgBox "用户名长度不能小于5位", , "提示": Exit Sub
For Nxunhuan = 1 To Len(name)
Yinbi = Yinbi + Asc(Mid(name, Nxunhuan, 1))
Next Nxunhuan
'循环取用户名所有字母的ASC值之和
Zhongzhuan = Yinbi
If Len(code) <= Len((Zhongzhuan)) Then MsgBox "注册码长度不够", , "提示": Exit Sub
Zhang = Yinbi Xor Val(Left(code, Len(Zhongzhuan)))
'将上述值跟输入注册码左边开始,相同长度的数值XOR
Zhang = Log(Zhang)
'对XOR值进行LOG运算,注意LOG有个特点是什么?
'当ZHANG为零的时候会出现一个数值为5的异常
'我们这里就是利用这个特点来设计隐蔽的注册过程来
'反跟踪。
'请问,破解者跟到这里了,他怎么会知道这里必须
'产生一个异常值为5的异常才能跟到正确的注册过程,
'如果不出现5异常的话,也没什么现象可以给他分析的
'他只能继续分析下去,就像走一条大路,他根本就没发现
'必须走小路才有收获,而一直走下去,貌似正确,实际被耍了
Mihuo = Right(Zhang, 8)
If Right(code, Len(code) - Len(Zhongzhuan)) = Mihuo Then
MsgBox "假注册成功,无语了吧?", , "^_^"
Else
MsgBox "假注册码错误,落入陷阱", , "^_^"
End If
Exit Sub
Yes:
If Err.Number = 5 Then
'检测异常类型是否为5,若为5说明是LOG异常了,表示可以进行正确的注册算法过程了
'如果不是5,那就处理一般性的异常处理事务。
'__________
'这里开始可以嵌入我们真正的算法了
If Right(code, Len(code) - Len(Zhongzhuan)) = "123456" Then
MsgBox "真正注册成功", , "学会了吧?"
End If
'真正算法里面最好不要出现太明显的提示信息,所以这里我把注册失败给提示给去掉了
'__________
Else
MsgBox "不好意思,请检测输入是否有问题", , "出错了"
End If
End Sub
我是代码终止线————————————————————————————————
调用如下:
我是代码起始线————————————————————————————————
Private Sub Command1_Click()
ZhuCe Text1.Text, Text2.Text
'可用真正注册码:
'name: maplescg
'code: 844123456
'假注册码:
'name: maplescg
'code: 14245066571
End Sub
我是代码终止线————————————————————————————————
看明白了吧?原来是这样的注册反调试过程:
如图:
正常状况下,维持左边正常假注册过程,只有在三叉路口,实现了代号为5的异常的时候,才执行右边正确的算法过程,而破解者基本看不明白在哪里有跳转,因为没有出现JMP这类的代码,而关键的LOG也被认为是计算注册码的必要函数,其实我们只是拿它来实现特定条件下的跳转工作。
我们把这个决定分支启动的关键点称为“门”。“门”要么