一、检测父进程反RING3调试器,我们知道WIN32系统一般软件的父进程都是EXPLORE,而OD等RING3调试器对软件进行调试时都是将它们的线程设为它的子线程,我们只要让程序检查父进程是否为EXPLORE就行,看附件里的Anti-Debug,如果发现父进程不是EXPLORE.EXE就自动退出,源码如下:
'相关的API自己查查
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&) '建立进程快照
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 '获得进程ID
ElseIf Process.th32ProcessID = GetCurrentProcessId() Then '是不是自己
pid = Process.th32ParentProcessID '获得自己父进程ID
Else
flag = False
End If
Loop Until (Process32Next(hSnapShot, Process) < 1) '遍历所有进程直到返回值为False
End If
l1 = CloseHandle(hSnapShot)
End If
If pid <> explorer Then
TerminateProcess hprocess, 0
Else
MsgBox "ok"
On Error Resume Next
End If
End Sub
当然这个方法也不是万能的,在Process32First下断,更改跳转轻易躲过。
二、反SMARTCHECK加载,SMARTCHECK是调试VB的利器,有必要对其进行防范。小楼前辈在软件加密技术内幕中提到两种检测方法:
利用VB的AppActivate函数激活SMARTCHECK窗口,然后发送ALT+F4进行关闭该窗口和利用FindWindow发现SMARTCHECK窗口直接将其关闭,其代码基本上是这样:
winHwnd = FindWindow(vbNullString, "Numega SmartCheck")
If winHwnd <> 0 Then
AppActivate "Numega SmartCheck"
sendkey "%{f4}", True
sendkey "%y", True
其实,我觉得直接检测进程SMARTCHK.EXE是否存在也可以,方法跟上面类似,你还可以检测其它比如W32DASM等进程,附件中的Anti-Load就是实例,发现SMARTCHK调用,自动退出:
…..
If InStr(LCase(Process.szExeFile), "smartchk.exe") > 0 Then
smart = Process.th32ProcessID
TerminateProcess hprocess, 0
Unload Me
Exit Do
End If
…….
四、利用IsDebuggerPresent检测调试器,这个对于OD来说已经一点用都没有了。具体看附件中的IsDebuggerPresent。
Private Declare Function IsDebuggerPresent Lib "kernel32" () As Long
Private Sub Command1_Click()
If IsDebuggerPresent Then
End
Else
MsgBox "没有被调试"
End If
End Sub
六、实现软件代码校检防止被修改,比如用CRC或者MD5进行自身代码完整性检测,实现方法:
先写一个用于增加CRC特征码的软件,假设定义为结尾部分:
Const CRC_HEAD = &H761226 '用于判断是否添加了CRC校验
Private Type stCRC
lHead As Long '验证是否进行CRC校验的标识
lCRC As Long 'CRC校验值
End Type
Private Sub Command1_Click()
CRC_Exe App.Path & "\工程1.Exe"
End Sub
Private Function CRC_Exe(ByVal strExe As String) As Boolean
Dim hFile As Long
Dim lFileLen As Long
Dim sCRC As stCRC
Dim btExe() As Byte
On Error GoTo Err_CRC_Exe
lFileLen = FileLen(strExe)
hFile = FreeFile
Open strExe For Binary As #hFile '打开加密文件
Seek hFile, lFileLen - LenB(sCRC) + 1 '定位CRC标识域,位于Exe文件尾部文件
Get hFile, , sCRC
If sCRC.lHead = CRC_HEAD Then '如果已经添加了CRC校验则退出,反之添加CRC校验
MsgBox "已CRC验证!"
Close #hFile
Exit Function
Else
Seek hFile, 1 '定位到文件首部
ReDim btExe(lFileLen - 1)
Get hFile, , btExe '按字节方式将Exe数据读入数组
sCRC.lHead = CRC_HEAD '添加CRC验证标识
sCRC.lCRC = Get_CRC(VarPtr(btExe(0)), lFileLen) '获取Exe内容CRC值
Put hFile, , sCRC '将CRC校验写入Exe文件尾部
End If
Close #hFile
MsgBox "CRC校验完成!"
CRC_Exe = True
Exit Function
Err_CRC_Exe:
If hFile <> 0 Then Close #hFile
CRC_Exe = False
MsgBox Err.Description
End Function
为程序本身增加CRC校检代码:
Const CRC_HEAD = &H761226 '用于判断是否添加了CRC校验
Private Type stCRC
lHead As Long '验证是否进行CRC校验的标识
lCRC As Long 'CRC校验值
End Type
Private Sub Form_Load()
Dim hFile As Long
Dim sCRC As stCRC
Dim strExe As String
Dim lFileLen As Long
Dim btExe() As Byte
strExe = App.Path & "\" & App.EXEName & ".exe"
lFileLen = FileLen(strExe)
ReDim btExe(lFileLen - LenB(sCRC) - 1) As Byte '定义Exe字节缓存数组
hFile = FreeFile
Open strExe For Binary As #hFile '读取Exe数据到数组
Get #hFile, , btExe
Get #hFile, , sCRC
Close #hFile
If sCRC.lHead = CRC_HEAD Then '如果程序添加了CRC验证则验证CRC值
If Get_CRC(VarPtr(btExe(0)), UBound(btExe) + 1) = lCRC Then '验证Exe数据CRC和保存的CRC值是否相同
MsgBox "文件未修改!".
Else
MsgBox "文件被非法修改!"
End If
Else
MsgBox "文件尚未进行CRC验证!" '检查尾部是否已已经增加CRC校检
End If
End Sub
七、利用SEH进行反跟踪,附件里的SHE如果用SMARTCHECK调试的话就合自动退出,附上小楼的源码:
Option Explicit
Private Declare Sub DebugBreak Lib "kernel32" ()
Private Sub Command1_Click()
On Error GoTo ERR_RaiseException
DebugBreak
DebugBreak
Exit Sub
ERR_RaiseException:
MsgBox "没有发现调试器!"
End Sub
Sub SetHandler()
SetUnhandledExceptionFilter AddressOf NewExceptionHandler
End Sub
Sub RestoreHandler()
SetUnhandledExceptionFilter 0
End Sub
Private Sub Form_Load()
SetHandler
End Sub
Private Sub Form_Unload(Cancel As Integer)
RestoreHandler
End Sub
'SHE模块略过。
除了上面的一些方法外,你还可以用一些密码学知识增加难度,如果技术够强,还可以借用内嵌汇编弄一些花指令和反调试SEH机制
______________________________________________________________
Private Function Modx(x As Double, y As Double) As Double
Dim w As Double
w = Fix(x / y) * y
Modx = x - w
End Function
这样就实现了稍大数在Vb里的求余动作
Private Function Rsa(p As Double) As Double
Dim b As Double
Dim rsan As Double
rsan = 99221
Rsa = 1
For b = 1 To 15935
Rsa = Rsa * p
Rsa = Modx(Rsa, rsan)
Next b
End Function
通过每次都求余,剩下的继续执行指数运算,下次再求余,就避免的误差和溢出
我们看下:
3^6 mod 25 = 729 mod 25 =4
等同于:
3^6 mod 25 =(((((((((3 mod 25) * 3) mod 25) * 3) mod 25) * 3) mod 25)* 3) mod 25)* 3 mod 25 = 4
可以用RSAtool来分解,速度很快……
分解后计算出D (解密密钥)
则解密算法为 P = C^D mod N
这里计算出D为48767
也就是说逆运算为:
'解密过程
Private Function Jiemi(c As Double) As Double
Dim b As Double
Dim rsan As Double
rsan = 99221
Jiemi = 1
For b = 1 To 48767
Jiemi = Jiemi * c
Jiemi = Modx(Jiemi, rsan)
Next b
End Function
当然,还有其他算法也很不错,到时有空再后续系列里详细演示……
关于如何隐藏关键算法到异常处理中去?
这个一直是Vb里很酷的技术,今天我把自己琢磨出的东西简单说一下,到下期系列的时候再具体演示
VB里的异常处理机制是 通过 On error goto 这个语句进行的,这个功能可大了,配合Resume Next、Resume 等语句,基本可以实现VB里的高级隐藏技术
大致是先在算法里嵌入迷惑性质的算法,通过一个可行的数值来产生一个指定的,不常见的错误,比方说可以除零,通过 On error goto 进行异常捕获,判断Err.Number 是否等于某个数值(不同数值对应不同错误类型)或者故意制造出一个溢出来捕获,捕获后可以解密一两句算法,然后通过Resume Next回到原程序,继续解密,又产生其他的错误,然后继续解密真正的算法……
如:
On Error GoTo chuling
Dim a As Double
Dim b As Double
a = 110
b = "&H" & Left(MD5(Right(Text2.Text, 16)), 5
a = 110 / b Xor 123456789
If a = 123 Then
MsgBox "注册码错误", , "提示"
Else
If a = 0 Then MsgBox "注册码正确", , "提示"
提示 :想想,正常过来的画,a怎么可能等于0
End If
Exit Sub
chuling:
'关键核心算法
噢,原来是发生了一次异常,在流程里注射了 “a = 0”这条指令
a = 0
Resume Next