1.映像劫持(IFEO)
这种技术如今已经过时了,在这里简单说一下。通过IFEO可以对目标程序进行Hook,原理是利用了Windows的调试机制。在执行一个程序时,系统会检查注册表中Image File Execution Options项,若存在同名程序,则放弃执行原程序,而用Debugger后面的程序用来调试原来的程序,这里实际上运行了调试器,原程序只是一个参数而已。系统不会检查是否循环调用,出现这种情况则会卡死直到超出路径最大长度进而出错。这种技术可以被病毒利用达到偷梁换柱的目的,也可以用来限制某个程序运行。关于IFEO有个错误的说法,说是以lpApplicationName方式运行EXE就不会被调试,事实证明这种方式无效,只要有debugger就会被调试,除非指定DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS。
2.软件限制策略
系统组策略中有一项叫软件限制策略,用于限制一个程序运行,可以根据路径限制,也可以根据文件MD5来限制。比如禁止一个路径规则?:\*就会限制运行所有程序。
3.Manifest文件
这是一个程序集文件,有些软件会带有一个这样的文件,文件命名方式是在原EXE文件名后加上个.Manifest后缀。大多数软件是没有这个文件的,如果在软件目录里建了一个这样的空文件或文件夹,那么软件运行时因为读取出错就会终止运行。这种方式作用同IFEO差不多,都可以根据文件名来限制其运行,只是不能替换文件。
那么如何摆脱它们的限制呢?其实也很简单,通用调试CreateProcess会发现,中间会对上面的三者分别进程检查。检查的顺序是,软件限制策略->映像劫持->Manifest文件。首先是软件限制策略,执行到这一步系统会调用四个函数:SaferIdentifyLevel、SaferComputeTokenFromLevel、SaferCloseLevel和SaferRecordEventLogEntry。这4个函数位于advapi32.dll库中,其中SaferComputeTokenFromLevel比较关键,它的返回值决定了是去还是留,若返回0则表示校验失败,说明存在限制,若返回1代表校验成功,程序会往下执行。我们直接内部Hook掉SaferComputeTokenFromLevel返回1,便可无视软件限制策略。接下来再看映像劫持,程序之所以会被劫持是因为注册表中存在同名程序,所以系统势必会查询“Image File Execution Options”项,我们直接Hook掉ntdll.dll的NoOpenKey这个存根函数,一旦发现在查询IFEO就返回,程序就会跳过IFEO的检查了。其实这种方法比较烦琐一些,还有个更为简单的方法,ntdll.dll中有一个LdrQueryImageFileExecutionOptions函数,它负责管理IFEO,直接干掉它让它返回0即可。当然,如果还是感觉麻烦干脆如开头所说的直接在创建进程时加个DEBUG_PROCESS标志,运行后再结束调试即可。再来看最后一个,这个比较特别,因为我没有找到哪个函数在检查Manifest,找不到也无所谓,因为不管是什么函数在检查它,实质是要判断是否存在Manifest这个文件,系统一般会采用打开文件的方法来判断,也就是NtOpenFile,现在我们将NtOpenFile回调一下,只要监控到manifest则返回一个STATUS_OBJECT_NAME_NOT_FOUND,意思是没有找到对象名称,这样也就绕过了Manifest机制。剩余的就是系统的任务了,比如NtCreateSection、NtCreateProcessEx等等,到NtCreateProcessEx也就说明进程已经创建。
示例代码:
'Form1
Option Explicit
Private Sub cmdRun_Click()
If txtRun.Text <> "" Then
If RunExe(txtRun.Text) = False Then MsgBox "运行失败", vbCritical, ""
End If
End Sub
Private Sub Form_Load()
SetHook
End Sub
Private Sub Form_Unload(Cancel As Integer)
UnHook
End Sub
'modHook.bas
'用于拦截本进程API
Option Explicit
Private Declare Function NtOpenFile Lib "NTDLL.DLL" (ByRef Filehandle As Long, _
ByVal DesiredAccess As Long, _
ByRef ObjectAttributes As OBJECT_ATTRIBUTES, _
ByRef IoStatusBlock As IO_STATUS_BLOCK, _
ByVal ShareAccess As Long, _
ByVal OpenOptions As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Const STATUS_OBJECT_NAME_NOT_FOUND = &HC0000034
Private Type IO_STATUS_BLOCK
Status As Long
Information As Long
End Type
Private Type OBJECT_ATTRIBUTES
Length As Long
RootDirectory As Long
ObjectName As Long
Attributes As Long
SecurityDescriptor As Long
SecurityQualityOfService As Long
End Type
Private Hook_IFEO As clsHookInfo
Private Hook_Safer As clsHookInfo
Private Hook_Manifest As clsHookInfo
'Hook
Public Function SetHook()
Dim hProcessCurrent As Long
'定义两个Hook对象
Set Hook_Safer = New clsHookInfo '用于拦截软件限制策略
Set Hook_IFEO = New clsHookInfo '用于拦截IFEO
Set Hook_Manifest = New clsHookInfo '用于拦截Manifest文件
hProcessCurrent = GetCurrentProcess '得到当前进程
'挂钩
Hook_Safer.HookApi "advapi32.dll", "SaferComputeTokenFromLevel", GetFunAddr(AddressOf SaferComputeTokenFromLevelCallback), hProcessCurrent
Hook_IFEO.HookApi "ntdll.dll", "LdrQueryImageFileExecutionOptions", GetFunAddr(AddressOf LdrQueryImageFileExecutionOptionsCallback), hProcessCurrent
Hook_Manifest.HookApi "ntdll.dll", "NtOpenFile", GetFunAddr(AddressOf NtOpenFileCallback), hProcessCurrent
End Function
'函数地址
Public Function GetFunAddr(lngFunAddr As Long) As Long
GetFunAddr = lngFunAddr
End Function
'SaferComputeTokenFromLevel回调
Public Function SaferComputeTokenFromLevelCallback(ByVal LevelHandle As Long, ByVal InAccessToken As Long, OutAccessToken As Long, ByVal dwFlags As Long, lpReserved As Long) As Long
SaferComputeTokenFromLevelCallback = 1 '非0表示查询失败,以跳过策略检查
End Function
'LdrQueryImageFileExecutionOptions回调
Public Function LdrQueryImageFileExecutionOptionsCallback() As Long
LdrQueryImageFileExecutionOptionsCallback = 0 '欺骗系统让它认为执行失败从而跳过映像劫持
End Function
'NtOpenFile回调
Public Function NtOpenFileCallback(Filehandle As Long, ByVal DesiredAccess As Long, ObjectAttributes As OBJECT_ATTRIBUTES, IoStatusBlock As IO_STATUS_BLOCK, ByVal ShareAccess As Long, ByVal OpenOptions As Long) As Long
Dim lRetVal As Long
Hook_Manifest.HookStatus False
'Debug.Print ObjectAttrToName(ObjectAttributes)
'如果是Manifest文件就改掉
If LCase(Right$(ObjectAttrToName(ObjectAttributes), 9)) = LCase(".Manifest") Then
lRetVal = STATUS_OBJECT_NAME_NOT_FOUND '返回值改为对象不存在
Else
lRetVal = NtOpenFile(Filehandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions)
End If
Hook_Manifest.HookStatus True
NtOpenFileCallback = lRetVal
End Function
'得到文件名称
Private Function ObjectAttrToName(ObjectAttr As OBJECT_ATTRIBUTES) As String
Dim bytCode() As Byte
Dim dwName As Long
Dim dwLength As Integer
CopyMemory dwLength, ByVal ObjectAttr.ObjectName, 2
If dwLength > 0 Then
CopyMemory dwName, ByVal ObjectAttr.ObjectName + 4, 4
ReDim bytCode(dwLength - 1)
CopyMemory bytCode(0), ByVal dwName, dwLength
ObjectAttrToName = StrConv(StrConv(bytCode, vbUnicode), vbFromUnicode)
ObjectAttrToName = Replace(ObjectAttrToName, "\??\", "")
End If
Erase bytCode
End Function
'删除Hook
Sub UnHook()
Set Hook_Safer = Nothing
Set Hook_IFEO = Nothing
Set Hook_Manifest = Nothing
End Sub
'mod_RunEXE,运行EXE
Option Explicit
Private Declare Function NtClose Lib "NTDLL.DLL" (ByVal ObjectHandle As Long) As Long
Private Declare Function CreateProcessInternalA Lib "kernel32" (ByVal hToken As Long, _
ByVal lpApplicationName As String, _
ByVal lpCommandLine As String, _
lpProcessAttributes As Any, _
lpThreadAttributes As Any, _
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As Long, _
lpEnvironment As Any, _
ByVal lpCurrentDriectory As String, _
lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION, _
ByVal hNewToken As Long) As Long
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
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
'运行EXE程序
Public Function RunExe(ByVal szFileName As String) As Boolean
Dim lRet As Long
Dim lp As PROCESS_INFORMATION
Dim si As STARTUPINFO
si.cb = Len(si)
lRet = CreateProcessInternalA(0&, vbNullString, szFileName, ByVal 0&, ByVal 0&, 0, &H80, ByVal 0&, vbNullString, si, lp, 0&)
If lRet <> 0 Then
NtClose lp.hThread
NtClose lp.hProcess
RunExe = True
End If
End Function
'类模块clsHookInfo
Option Explicit
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private mbytOldCode(5) As Byte
Private mbytNewCode(5) As Byte
Private mlngFunAddr As Long
Private mhProcess As Long
Public Function HookApi(ByVal strDllName As String, ByVal strFunName As String, ByVal lngFunAddr As Long, ByVal hProcess As Long) As Boolean
Dim hModule As Long, dwJmpAddr As Long
mhProcess = hProcess
hModule = LoadLibrary(strDllName)
If hModule = 0 Then
HookApi = False
Exit Function
End If
mlngFunAddr = GetProcAddress(hModule, strFunName)
If mlngFunAddr = 0 Then
HookApi = False
Exit Function
End If
CopyMemory mbytOldCode(0), ByVal mlngFunAddr, 6
mbytNewCode(0) = &HE9
dwJmpAddr = lngFunAddr - mlngFunAddr - 5
CopyMemory mbytNewCode(1), dwJmpAddr, 4
HookStatus True
HookApi = True
End Function
Public Function HookStatus(ByVal blnIsHook As Boolean) As Boolean
If blnIsHook Then
If WriteProcessMemory(mhProcess, ByVal mlngFunAddr, mbytNewCode(0), 5, 0) <> 0 Then HookStatus = False '拦截
Else
If WriteProcessMemory(mhProcess, ByVal mlngFunAddr, mbytOldCode(0), 5, 0) <> 0 Then HookStatus = False '恢复
End If
End Function
Private Sub Class_Terminate()
HookStatus False
End Sub
2012-10-4