一、检测父进程反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
Public Enum CRCAlgorithms
CRC16
CRC32
End Enum
Private m_Algorithm As Boolean
Private m_CRC16 As Long
Private m_CRC16Asm() As Byte
Private m_CRC16Init As Boolean
Private m_CRC16Table(0 To 255) As Long
Private m_CRC32 As Long
Private m_CRC32Asm() As Byte
Private m_CRC32Init As Boolean
Private m_CRC32Table(0 To 255) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'此函数作用在这里是内联汇编之用
Public Function AddBytes(ByteArray() As Byte) As Variant
Dim ByteSize As Long
'异常处理
On Local Error GoTo NoData
'计算大小
ByteSize = UBound(ByteArray) - LBound(ByteArray) + 1
'异常处理
On Local Error GoTo 0
'内联汇编提高处理速度
Select Case m_Algorithm
Case CRC16
Call CallWindowProc(VarPtr(m_CRC16Asm(0)), VarPtr(m_CRC16), VarPtr(ByteArray(LBound(ByteArray))), VarPtr(m_CRC16Table(0)), ByteSize)
Case CRC32
Call CallWindowProc(VarPtr(m_CRC32Asm(0)), VarPtr(m_CRC32), VarPtr(ByteArray(LBound(ByteArray))), VarPtr(m_CRC32Table(0)), ByteSize)
End Select
NoData:
'返回新值
AddBytes = Value
End Function
Public Function AddString(Text As String) As Variant
End Function
Public Property Let Algorithm(New_Value As CRCAlgorithms)
'选择新算法
m_Algorithm = New_Value
'确定已经初始化新算法
Select Case m_Algorithm
Case CRC16
If (Not m_CRC16Init) Then Call InitializeCRC16
Case CRC32
If (Not m_CRC32Init) Then Call InitializeCRC32
End Select
'标记
Call Clear
End Property
Public Property Get Algorithm() As CRCAlgorithms
Algorithm = m_Algorithm
End Property
Public Function CalculateBytes(ByteArray() As Byte) As Variant
Select Case m_Algorithm
Case CRC16
Value = (m_CRC16 And 65535)
Case CRC32
Value = (Not m_CRC32)
End Select
End Property
Public Property Let Value(New_Value As Variant)
Select Case m_Algorithm
Case CRC16
m_CRC16 = New_Value
Case CRC32
m_CRC32 = New_Value
End Select
End Property
Private Sub InitializeCRC16()
Dim i As Long
Dim j As Long
Dim k As Long
Dim CRC As Long
Dim sASM As String
'创建表格
For i = 0 To 255
k = i * 256
CRC = 0
For j = 0 To 7
If (((CRC Xor k) And 32768) = 32768) Then
CRC = (CRC * 2) Xor &H1021
Else
CRC = (CRC * 2)
End If
k = k * 2
Next
m_CRC16Table(i) = CRC '(CRC And 65535)
Next
'内联汇编预处理
sASM = "5589E55756505351528B45088B008B750C8B7D108B4D1431DB8A1E30E3668B149F30C66689D0464975EF25FFFF00008B4D0889015A595B585E5F89EC5DC21000"
ReDim m_CRC16Asm(0 To Len(sASM) \ 2 - 1)
For i = 1 To Len(sASM) Step 2
m_CRC16Asm(i \ 2) = Val("&H" & Mid$(sASM, i, 2))
Next
'内联汇编预处理
sASM = "5589E557565053518B45088B008B750C8B7D108B4D1431DB8A1E30C3C1E80833049F464975F28B4D088901595B585E5F89EC5DC21000"
ReDim m_CRC32Asm(0 To Len(sASM) \ 2 - 1)
For i = 1 To Len(sASM) Step 2
m_CRC32Asm(i \ 2) = Val("&H" & Mid$(sASM, i, 2))
Next
If Len(FileCRC) < 8 Then FileCRC = Left("00000000", 8 - Len(FileCRC)) & Hex(cCRC.CalculateFile(Lujing))
'如果CRC32值不足8位,那么要在前面添加零来补足位数
CRC32zhi = FileCRC
'CRC32值储存
FileNum = FreeFile
'获得个文件号(通道)
Open Lujing For Binary As #FileNum
Seek FileNum, FileLen(Lujing) - 7
Put #FileNum, , FileCRC
Close FileNum
'用二进制模式打开目标程序,通过SEEK定位需要添加CRC32数值的位置,请注意:这个位置以后大家可以自己改
'通过PUT将我们计算号的CRC32写入指定位置
'通过CLOSE关闭通道
tfile = FreeFile
Open Lujing For Binary As #tfile '利用二进制打开自身
FilelenNO1 = LOF(tfile)
ReDim ArrBytes(1 To FilelenNO1) As Byte '将目标末尾8位储存
Get tfile, , ArrBytes
Close tfile
For Xunhuan = FilelenNO1 - 7 To FilelenNO1
'开始获取这具体的8位
GetZHI = GetZHI & Chr(ArrBytes(Xunhuan))
Next Xunhuan
‘在窗体代码部分增加:
Sub TurboShutdown(Index As Integer)
RtlAdjustPrivilege SE_SHUTDOWN_PRIVILEGE, 1, 0, 0
Select Case Index
Case 1 '关机
NtShutdownSystem ShutDown
Case 2 '重启动
NtShutdownSystem RESTART
Case 3 '关机
NtShutdownSystem POWEROFF
End Select
End Sub
Private Function ShiJiancheck() As Boolean
ShiJiancheck = False
Dim iFile As String
Dim FileTime As String
iFile = App.Path & "\" & App.EXEName & ".exe"
'获取自身启动路径
FileTime = Format(FileDateTime(iFile), "YYYYMMDDHHMMSS")
'获得字符串形式的文件最后修改时间
If FileTime = "20080808080808" Then
'示范设置为2008年08月08日08时08分08秒,这里大家可以自己定,最好不要太特别
ShiJiancheck = True
Else
ShiJiancheck = False
End If
End Function
我是代码终止线
调用该函数直接当作布尔变量用即可
如下调用:
我是代码起始线
Private Sub Command1_Click()
'注意,文件本身的修改时间应该不是我们设定的值,所以编译好EXE文件后,
'用文件属性修改器来修改文件最后修改时间到指定数值,这个数值不要太特殊了
'文件属性修改器已经放在同个文件夹下了,请使用
If ShiJiancheck = False Then
MsgBox "文件被修改", , "警告"
Else
MsgBox "文件正常", , "通过"
End If
End Sub
Private Function FileBig() As Long
'如果文件巨大,那么改LONG为DOUBLE
Dim FileLujin As String
filelujing = App.Path & "\" & App.EXEName & ".exe"
FileBig = FileLen(filelujing)
End Function
我是代码终止线
具体使用看下面代码:
我司代码起始线
Private Sub Form_Load()
If FileBig > 27300 Then
'第一次这个数字随便设置,先编译好
'用压缩壳将它压缩,查看压缩后文件大小
'回到这里,修改数值比压缩后的大小大那儿一些就够了
'如果被脱壳了,程序体积就会膨胀
'从而被我们检测出来
'当然要注意了,如果是保护壳的话
'加壳后反而更大,这个时候,我们的判断
'语句就要反过来了
MsgBox "程序被脱壳了", , "警告"
Else
MsgBox "程序正常", , "通过"
End If
End Sub
Dim Lujing As String
'储存程序自身路径
Dim Suiji(7) As Byte
'7个元素数组,用于储存每次随机数
Dim n As Long
'循环计数器
Dim Panduan As Double
'累加器
Lujing = App.Path & "\" & App.EXEName & ".exe"
'获得程序自身路径
For n = 1 To 7
Randomize
'每次都初始化随机数生成器
Suiji(n) = Int(Rnd * 2)
‘取随机数0或者1
Panduan = Panduan + Suiji(n)
'累加
Next n
If Panduan > 0 Then
'如果累加总结果不为零,那就SHELL自己一次,然后通过END结束自己
Shell Lujing, 1
'进入下一个轮回哦
End
'结束这一世
End If
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
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无效
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
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 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
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
我是代码起始线
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
Private Declare Function GetTickCount Lib "kernel32" Alias "GetTickCount" () As Long
我是代码终止线
在COMMAND1事件里添加以下代码:(实际编写中可以不拘一格)
我是代码起始线
Private Sub Command1_Click()
'假设这里是我们的注册过程,我们隔三差五随意将以下代码复制粘帖
'
Dim atime As Long
Dim btime As Long
atime = GetTickCount
btime = GetTickCount
If btime - atime = 0 Then
MsgBox btime - atime, , "正常运行,经历时间:"
'实际软件中,应该彻底隐蔽这些提示消息
Else
MsgBox btime - atime, , "发现调试器,经历时间:"
'实际软件中,应该彻底隐蔽这些提示消息,直接引入错误的分支去执行乱七八糟的代码
End If
'
'算法部分......
End Sub
我是代码终止线
效果如图:
发现调试工作:
再讲系统自带的TIMER函数
打开VB6.0 新建工程 添加COMMAND1
在它的事件代码里编写:
我是代码起始线
Private Sub Command1_Click()
'假设这里是我们的注册过程,我们隔三差五随意将以下代码复制粘帖
'------------------------------
Dim ctime As Double
Dim dtime As Double
ctime = Timer
dtime = Timer
If dtime - ctime = 0 Then
MsgBox dtime - ctime, , "正常运行,经历时间:"
'实际软件中,应该彻底隐蔽这些提示消息
Else
MsgBox dtime - ctime, , "发现调试器,经历时间:"
'实际软件中,应该彻底隐蔽这些提示消息,直接引入错误的分支去执行乱七八糟的代码
End If
End Sub
我是代码终止线
可见TIMER跟GetTickCount有差不多的应用,可以扩展下思维吧。
关于时间类反调试,我讲最后一个,一是重点介绍的一个:
API: GetProcessTimes
我们先来看看它的作用和使用规范:
Private Declare Function GetProcessTimes Lib "kernel32" Alias "GetProcessTimes" (ByVal hProcess As Long, lpCreationTime As FILETIME, lpExitTime As FILETIME, lpKernelTime As FILETIME, lpUserTime As FILETIME) As Long
Option Explicit
Private Declare Sub GetLocalTime Lib "kernel32" (ntt As SystemTime)
Private Declare Sub GetProcessTimes Lib "kernel32" (ByVal hProcess As Long, lpCreationTime As FILETIME, lpExitTime As FILETIME, lpKernelTime As FILETIME, lpUserTime As FILETIME)
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Declare Sub FileTimeToSystemTime Lib "kernel32" (lpFileTime As FILETIME, lpSystemTime As SystemTime)
Private Type SystemTime
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Dim id As Long
Dim ctt As FILETIME
Dim ett As FILETIME
Dim ktt As FILETIME
Dim utt As FILETIME
Dim stt As SystemTime
Dim ntt As SystemTime
Dim qtt As Double
我是代码终止线
下面开始编写一个函数,可以方便复制和移植。
我是代码起始线
Private Function Antitime() As Boolean
'自定义了一个利用GetProcessTimes自动进行检测调试器的函数
GetLocalTime ntt
'获得现在系统时间
id = GetCurrentProcess
'获得本进程ID
GetProcessTimes id, ctt, ett, ktt, utt
'获得本进程ID对应的信息
FileTimeToSystemTime ctt, stt
'转换时间格式
qtt = Val(ntt.wSecond) * 1000 + ntt.wMilliseconds - Val(stt.wSecond) * 1000 - stt.wMilliseconds
'计算时间差
If qtt > 80 Or qtt < 0 Then
'如果时间差大于80微秒或者小于0就认为发现了调试,这个值大家可以自行修改
Antitime = True
Else
Antitime = False
End If
End Function
Private Sub Form_Initialize()
'这样调用即可
If Antitime = True Then
MsgBox "发现调试器了", , "提醒"
Label1.ForeColor = &HFF&
Label1.Caption = "发现调试器"
Else
Label1.ForeColor = &H80000012
Label1.Caption = "没有发现调试"
End If
End Sub
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Declare Function GetProcessWorkingSetSize Lib "kernel32" (ByVal hProcess As Long, lpMinimumWorkingSetSize As Long, lpMaximumWorkingSetSize As Long) As Long
我是代码终止线
然后我们编写函数
我是代码起始线
Private Function JianNeicun(biggest As Long, smallest As Long) As Boolean
'我们这里的最大值是1413120,最小值是204800
Dim yx As Long
Dim ax As Long
Dim bx As Long
Dim cx As Long
yx = GetCurrentProcess
'获得ID
ax = GetProcessWorkingSetSize(yx, bx, cx)
'获得内存分配信息
'MsgBox bx, , "min" '这里可以自行打开,以确定你的程序实际最小内存分配
'MsgBox cx, , "max" '这里可以自行打开,以确定你的程序实际最大内存分配
If bx <> smallest Or cx <> biggest Then
JianNeicun = True '发现调试器OD
Else
JianNeicun = False
End If
End Function
我是代码终止线
怎样使用?
请看:
我是代码起始线
Private Sub Command1_Click()
If JianNeicun(1413120, 204800) = True Then
'这个值可以修改为你的程序的实际内存交付
MsgBox "发现调试器OD", , "提示"
Else
MsgBox "没有发现调试器", , "恭喜"
End If
End Sub
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const HWND_TOPMOST = -1
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOZORDER = &H8
我是代码终止线
设计以下两个过程
我是代码起始线
Private Sub SetFormTopmost()
'该过程是使窗口置顶
MAXme
SetWindowPos Form1.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOZORDER + SWP_NOMOVE + SWP_NOSIZE
End Sub
Private Sub MAXme()
'该过程是使窗口占满整个屏幕
Me.Left = 0
Me.Top = 0
Me.Width = Screen.Width
Me.Height = Screen.Height
End Sub
我是代码终止线
调用如下:
我是代码起始线
Private Sub Command1_Click()
If Val(Text1.Text) = 123456 Then
Label2.Caption = "注册成功,您可以关闭注册窗口了"
Text1.Enabled = False
Else
Label2.Caption = "授权码错误,请重新输入"
End If
End Sub
Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
'在鼠标触及按钮的时候启动置顶
'实际应用中,你可以自己选择个隐蔽的地方来启动
SetFormTopmost
End Sub
Option Explicit
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
'定义API调用
我是代码终止线
接着设计一个检测按键并做相应的SUB
我是代码起始线
Private Sub Anjian()
If GetAsyncKeyState(vbKeyF2) Then
MsgBox "你按了F2键,怀疑你在用OD断点", , "警告"
End
End If
If GetAsyncKeyState(vbKeyF7) Then
MsgBox "你按了F7键,怀疑你在用OD调试", , "警告"
End
End If
If GetAsyncKeyState(vbKeyF8) Then
MsgBox "你按了F8键,怀疑你在用OD调试", , "警告"
End
End If
If GetAsyncKeyState(vbKeyF9) Then
MsgBox "你按了F9键,怀疑你在用OD调试", , "警告"
End
End If
End Sub
我是代码终止线
调用如下:
我是代码起始线
Private Sub Command1_Click()
MsgBox "本按无对应算法注册,只是演示监视按键来反调试,假设破解者追踪本按钮事件", , "提醒"
End Sub
Private Sub Timer1_Timer()
'用计时器来时时检测按键
Anjian
End Sub
我是代码终止线
其他按钮也能检测出来,效果还好的。在程序运行的时候,如果下F2断点,同样可以发出警告提示。
第十五、检测前台窗口反调试
当你的程序的注册按钮被点击,或者其他互动事件发发生,程序窗体就调到前台位置了。
我们可以据此检测是否有调试活动
利用API:GetForegroundWindow
打开VB6.0 新建工程,添加注册按钮
在通用部分添加:
我是代码起始线
Private Declare Function GetForegroundWindow Lib "user32" () As Long
我是代码终止线
编写函数:
我是代码起始线
Private Function JianWindow() As Boolean
Dim abcg As Long
abcg = GetForegroundWindow
If abcg = Me.hWnd Then
JianWindow = False
Else
JianWindow = True
End If
End Function
我是代码终止线
调用如下:
我是代码起始线
Private Sub Command1_Click()
'如果断点在这里,那么就会被我们发现调试
If JianWindow = True Then
MsgBox "你在调试我", , "提示"
Else
MsgBox "没有发现调试", , "恭喜"
End If
End Sub
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 TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal blnheritHandle As Long, ByVal dwAppProcessId 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
我是代码终止线
下面开始编写函数
我是代码起始线
Private Function Opencsrss() As Boolean
'发现调试器返回TRUE,没有发现则返回FALSE
On Error GoTo maple
Dim Process As PROCESSENTRY32
Dim hSnapShot As Long
Dim l1 As Long
Dim flag As Boolean
Dim mName As String
Dim i As Integer
Dim pid As Long, WOW As Long '注意这2个变量就用来存放2个ID
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 = "csrss.exe" Then '是不是WOW.exe
WOW = Process.th32ProcessID '获得进程ID
End If
Loop Until (Process32Next(hSnapShot, Process) < 1) '遍历所有进程直到返回值为False
End If
l1 = CloseHandle(hSnapShot)
End If
If WOW <> 0 Then
Dim jiejie As Long
jiejie = OpenProcess(1&, -1&, WOW)
'测试打开能力
If jiejie <> 0 Then
Opencsrss = True
Else
Opencsrss = False
End If
End If
Exit Function
maple:
Opencsrss = False
End Function
我是代码终止线
调用如下:
我是代码起始线
Private Sub Command1_Click()
If Opencsrss = True Then
MsgBox "发现调试器,请关闭", , "警告"
Else
MsgBox "没有发现调试", , "恭喜"
End If
End Sub
Private Type RECT
left As Long
top As Long
right As Long
bottom As Long
End Type
Private Type POINT
x As Long
y As Long
End Type
Private Declare Sub ClipCursor Lib "user32" (lpRect As Any)
Private Declare Sub GetClientRect Lib "user32" (ByVal hWnd As Long, lpRect As RECT)
Private Declare Sub ClientToScreen Lib "user32" (ByVal hWnd As Long, lpPoint As POINT)
Private Declare Sub OffsetRect Lib "user32" (lpRect As RECT, ByVal x As Long, ByVal y As Long)
我是代码终止线
下面写成过程
我是代码起始线
Private Sub Suoding()
'锁定鼠标范围的过程
Dim client As RECT
Dim upperleft As POINT
GetClientRect Me.hWnd, client
upperleft.x = client.left
upperleft.y = client.top
ClientToScreen Me.hWnd, upperleft
OffsetRect client, upperleft.x, upperleft.y
ClipCursor client
End Sub
我是代码终止线
其他调用和算法:
我是代码起始线
Private Sub Timer1_Timer()
Suoding
'每过100微秒锁定一次,防止鼠标脱离
'设计思路是程序一起动就自动锁定鼠标不离开程序范围,除非结束掉程序,或者完成注册为止
End Sub
Private Sub Image1_Click()
'自定义关闭按钮,画出来的,哈哈哈
End
End Sub
Private Sub Command1_Click()
If Val(Text1.Text) = 123456 Then
MsgBox "恭喜你,注册成功", , "恭喜": End
Else
MsgBox "注册码错误哦", , "错误"
End If
End Sub
Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Type STARTUPINFO '(createprocess)
cb As Long
lpReserved As Long
lpDesktop As Long
lpTitle As Long
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
我是代码终止线
编写函数:
我是代码起始线
Private Function StartAnti() As Boolean
Dim Huanjing As STARTUPINFO
GetStartupInfo Huanjing
If Huanjing.dwX <> 0 Or Huanjing.dwY <> 0 Or Huanjing.dwXCountChars <> 0 Or Huanjing.dwYCountChars <> 0 Or Huanjing.dwFillAttribute <> 0 Or Huanjing.dwXSize <> 0 Or Huanjing.dwYSize <> 0 Then
StartAnti = True
Else
StartAnti = False
End If
End Function
我是代码终止线
调用如下:
我是代码起始线
Private Sub Command1_Click()
If StartAnti = True Then
MsgBox "发现调试器,请关闭", , "警告"
Else
MsgBox "没有发现调试器", , "通过"
End If
End Sub
Private Sub Form_Load()
If StartAnti = True Then
MsgBox "发现调试器,请关闭", , "警告"
Else
MsgBox "没有发现调试器", , "通过"
End If
End Sub