|
本帖最后由 pusofalse 于 2009-11-25 04:20 编辑
; 另类方法操作注册表隐藏键值及逆向工程入门。
; 以HKLM\SAM\SAM键为例,枚举其子键。
; 思路:
; 枚举注册表键最终会调用到RegEnumKey API函数(定义于Advapi32.dll中)。先看下此函数的定义:
;; LONG WINAPI RegEnumKey(
;; __in HKEY hKey,
;; __in DWORD dwIndex,
;; __out LPTSTR lpName,
;; __in DWORD cchName
;; );
; hKey参数表示某个注册表键的句柄,比如要枚举HKLM\Software的子键,那么hKey就对应于HKLM\Software键句柄,此句柄通常返回于RegOpenKeyEx函数。
; dwIndex表示子键索引,以0为基始。
; lpName 表示一个内存中的缓存区指针。当函数完成调用后,会把子键的名称复制到此缓存区中。
; cchName代表缓存区lpName的长度,以字节为单位。因为注册表键的名称长度最大为255字节。所以调用时直接给此参数指定255即可。
; 再看下RegOpenKeyEx函数的定义:
;; LONG WINAPI RegOpenKeyEx(
;; __in HKEY hKey,
;; __in_opt LPCTSTR lpSubKey,
;; __reserved DWORD ulOptions,
;; __in REGSAM samDesired,
;; __out PHKEY phkResult
;; );
; hKey - 4个常用主键的常量句柄,每个键的定义如下:
; HKEY_CLASSES_ROOT - 0x80000000
; HKEY_CURRENT_USER - 0x80000001
; HKEY_LOCAL_MACHINE - 0x80000002
; HKEY_USERS - 0x80000003
; lpSubKey - 字符串型,指定主键下的子键名称,如果只需要打开主键,给参数留空即可。
; ulOptions - 函数保留参数,调用时必须为0。
; samDesired - 指定一个访问掩码,可以是以下任意值的组合:
; KEY_ALL_ACCESS - 0xF003F - 完全访问。
; KEY_CREATE_LINK - 0x20 - 系统保留。
; KEY_CREATE_SUB_KEY - 0x4 - 创建子键。
; KEY_ENUMERATE_SUB_KEYS - 0x8 - 枚举子键。
; KEY_EXECUTE - 0x20019 - 等同于KEY_READ。
; KEY_READ - 0x20019 - 查询键值的相关信息(写入时间、子键数量等信息)。
; KEY_NOTIFY 0x10 - 当键值被更改时,可以得到通知(实时监控)。
; KEY_QUERY_VALUE - 0x1- 查询键值。
; KEY_SET_VALUE 0x2 - 更改键值。
; 如果要枚举某键的子键,给此参数指定KEY_ENUMERATE_SUB_KEYS (8),前提是访问控制列表允许此操作。
; phkResult - 用于保存打开的注册表键句柄。
; 注意RegOpenKeyEx函数的samDesired参数,指定一个访问掩码。当某个进程试图访问某个注册表键时,系统会先检查该进程所用的访问掩码是否与访问控制列表中所定义的访问权限相冲突,冲突则拒绝访问。问题就出在了这里,HKLM\SAM\SAM是受系统保护的,管理员没有权限访问此键。管理员于此键的权限只是读取或更改安全权限信息。所以枚举SAM键的子键,并不能用RegOpenKeyEx函数正确无误地得到其句柄。一个方法就是先更改其访问控制列表中的访问权限,这个可以使用setacl.exe或LSA库实现,但万一访问控制列表定义不正确,阻止了SYSTEM账户对此键的访问,那么系统就OVER了,所以忽略更改访问控制列表的方法。不能重新设置其访问权限,也不能直接调用RegOpenKeyEx得到其句柄,那么除此之外,难道没有实现方法了吗?NONONO,试想一下,HKLM\SAM\SAM,如此重要并且受系统保护的键,在lsass.exe进程中必有对此键的操作句柄(lsass.exe进程相关信息及系统作用请参考搜索引擎),比如lsass.exe会实时监控SAM键的值是否被更改,或在创建帐户之后向里面写入新的账户信息,以此等等,这些操作都要用到SAM键的句柄。既然如此,那么找到lsass.exe中的SAM键句柄,然后copy到自己进程中,将句柄转换为己用,那么是否等效于自己的进程调用RegOpenKeyEx函数呢,答案是肯定的。什么?答案是肯定的!
; 这里又会用到另外2个API函数,NtQuerySystemInformation、DuplicateHandle。
; NtQuerySystemInformation - 查询系统中的各种信息,其中包括了句柄信息。
; DuplicateHandle - 将其他进程中的句柄复制到自身进程中,将句柄转换为己用。
; 这是2个强大至极的函数,试想你如果用NtQuerySystemInformation找到了svchost.exe中的一个合法的socket网络传输句柄,并且用DuplicateHandle复制到自身进程中,那么会怎样?逆向工程或者木马技术必会用到的2个API函数。
; NtQuerySystemInformation (Ntdll.dll) 定义如下:
;; NTSTATUS WINAPI NtQuerySystemInformation(
;; __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
;; __inout PVOID SystemInformation,
;; __in ULONG SystemInformationLength,
;; __out_opt PULONG ReturnLength
;; );
; SystemInformationClass - 指定要查询的信息,提供了50多个选项。此处要用到它的16号功能 —— 枚举系统中的所有句柄。
; SystemInformation - 指定一个缓存区指针,函数完成调用后将复制句柄信息到此缓存区中。
; SystemInformationLength - 指定缓存区SystemInformation长度,以字节为单位。
; ReturnLength - 当缓存区过小时,此值被设为所需的长度大小。
; DuplicateHandle (Kernel32.dll) 定义:
;; BOOL WINAPI DuplicateHandle(
;; __in HANDLE hSourceProcessHandle,
;; __in HANDLE hSourceHandle,
;; __in HANDLE hTargetProcessHandle,
;; __out LPHANDLE lpTargetHandle,
;; __in DWORD dwDesiredAccess,
;; __in BOOL bInheritHandle,
;; __in DWORD dwOptions
;; );
; hSourceProcessHandle - 指定源进程句柄。
; hSourceHandle - 指定要复制的句柄的值,必须确实存在于hSourceProcessHandle中。
; hTargetProcessHandle - 指定目标进程句柄。
; lpTargetHandle - 用于存放复制之后得到的句柄值。
; dwDesiredAccess - 指定访问掩码,如果dwOptions中包含2,此参数应设为0。
; bInheritHandle - 指定复制之后的句柄是否能够否被hTargetProcessHandle进程的子进程继承。
; dwOptions - 可以是以下任意值的组合:
; DUPLICATE_CLOSE_SOURCE - 1 - 复制的同时关闭源句柄。
; DUPLICATE_SAME_ACCESS - 2 - 使用源进程所用的访问权限掩码。
; NtQuerySystemInformation中,当SystemInformationClass设为16号信息时,SystemInformation缓存区的定义如下:
;; ULONG NumberHandles 句柄数量。
;; SYSTEM_HANDLE_INFORMATION HandleInfo ;;SYSTEM_HANDLE_INFORMATION结构,用于存放每个句柄的对象信息。
;; SYSTEM_HANDLE_INFORMATION结构定义:
;; ULONG ProcessID; 句柄所属的进程的标识ID
;; UCHAR ObjectTypeNumber; 句柄对象类型,文件?进程?OR 注册表键?
;; UCHAR Flags; 句柄标识,具体请参考MSDN - SetHandleInformation。
;; USHORT Handle; 对象句柄的数值
;; PVOID Object; 对象句柄所指的内核对象地址
;; ACCESS_MASK GrantedAccess; 创建句柄时所准许的对象的访问权
; 以上,看到有ProcessID,你想到了什么?
; 系统有各种各样的句柄,文件、进程、线程、socket、令牌、注册表键、互斥量等(注,窗口、图标等句柄属于用户句柄,不在此类内核句柄范畴内),那么如何知道句柄是否对应注册表键HKLM\SAM\SAM呢?此处又会用到的API函数NtQueryObject:
; NtQueryObject (Ntdll.dll) 查询句柄对象信息,定义:
;; NtQueryObject(
;; __in_opt HANDLE Handle,
;; __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
;; __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
;; __in ULONG ObjectInformationLength,
;; __out_opt PULONG ReturnLength
;; );
; Handle - 句柄值。
; ObjectInformationClass - 指定要查询的句柄信息,此处用到它的1号功能——查询句柄所指向的对象名称。
; ObjectInformation - 内存指针,用于存放查询到的信息。
; ObjectInformationLength - ObjectInformation缓存区长度,单位为字节。
; ReturnLength - 当ObjectInformation长度小于实际长度时,此值被设为所需长度。
; 当ObjectInformationClass为1时,ObjectInformation指向UNICODE_STRING结构。LSA库中的_LsaInitializeBufferW函数提供了将UNICODE_STRING结构中的数据转换为实际数据的功能。
; 枚举HKLM\SAM\SAM键的子键,重新整理下思路:
; 因为HKLM\SAM\SAM键的特殊,并不能够通过使用 直接调用RegOpenKeyEx函数 或 重新设置访问控制列表 的方法正确无误地返回其句柄。
; 那么,余下的方法只有2个:
; 1、将自身进程提升至SYSTEM权限。
; 2、
; ① NtQuerySystemInformation枚举系统中的所有句柄。
; ② NtQueryObject找到lsass.exe所打开的SAM键句柄。
; ③ DuplicateHandle将其复制到自身进程中转为自身进程可以使用的句柄,相当于自身进程调用RegOpenKeyEx得到其句柄。
; ④ 将此转换后的句柄直接传递到RegEnumKey枚举SAM子键。
; 此实例中,用第2种方法。将此思路代码化:
#include <LocalSecurityAuthority.au3>
; 提升至DEBUG权限,打开lsass.exe句柄所需。
Local $aPriv[1][2] = [[$SE_DEBUG_NAME, 2]]
$hToken = _OpenProcessToken(-1)
_AdjustTokenPrivileges($hToken, $aPriv)
_LsaCloseHandle($hToken)
; 返回系统中的句柄链表。
$pBuffer = _NtQuerySystemInformation(16)
; 获取句柄数量。
$tNum = DllStructCreate("long NumberHandles", $pBuffer)
$iNum = DllStructGetData($tNum, "NumberHandles")
$pBuffer += 4 ; 偏移4字节,指向第一个SYSTEM_HANDLE_INFO。
; 之所以+4,是因为一个long型结构在内存中占4个字节。
Const $tagSYSTEM_HANDLE_INFO = "ulong PID;ubyte objType;ubyte Flags;ushort Handle;ptr Object;dword AccessMask"
$fFlags = 0
$iLsassPID = ProcessExists("lsass.exe")
$hLsass = _OpenProcess($iLsassPID)
; 枚举句柄
For $i = 1 To $iNum
$tBuffer = DllStructCreate($tagSYSTEM_HANDLE_INFO, $pBuffer)
$pBuffer += DllStructGetSize($tBuffer) ; 指向下一个SYSTEM_HANDLE_INFO
$iPid = DllStructGetData($tBuffer, "PID")
If $iPid <> $iLsassPID Then ContinueLoop ; 判断句柄所属的进程。
$hHandle = DllStructGetData($tBuffer, "Handle")
$hDup = _DuplicateHandle($hLsass, $hHandle, -1) ; 复制到自身进程中。
; 获取句柄所指的对象名称
$pName = _NtQueryObject($hDup, 1)
$sName = _LsaInitializeBufferW($pName, 1)
_HeapFree($pName)
If $sName <> "\Registry\Machine\SAM\SAM" Then
_LsaCloseHandle($hDup)
ContinueLoop ; 判断是否是SAM键句柄。
EndIf
If bitAND(DllStructGetData($tBuffer, "AccessMask"), 8) <> 8 Then
_LsaCloseHandle($hDup)
ContinueLoop ; 判断是否有枚举子键的权限,8 = KEY_ENUMERATE_SUB_KEYS
EndIf
$fFlags = 1
ExitLoop
Next
_HeapFree($pBuffer)
_LsaCloseHandle($hLsass)
If $fFlags = 0 Then Exit ; 没有找到SAM键句柄。
Local $sSubKey, $iIndex
While _RegEnumKey($hDup, $iIndex, $sSubKey)
$iIndex += 1
Msgbox(0, "", $sName & "\" & $sSubKey)
WEnd
; 如果要对SAM进行写操作,应该确保AccessMask中包含KEY_SET_VALUE掩码。
; 主体分析及代码,完。
; 如上所说,NtQuerySystemInformation、DuplicateHandle是2个强大至极的函数,能够做的事情远不止逆向分析。保护文件、进程与端口映射、穿透防火墙的数据传输、隐藏进程、等等,很多强大的功能都会用到这2个API。
Func _NtQuerySystemInformation($iClass)
Local $pBuffer, $iResult
$pBuffer = _HeapAlloc(0x1000)
$iResult = DllCall("Ntdll.dll", "dword", "NtQuerySystemInformation", "int", $iClass, _
"ptr", $pBuffer, "dword", 0x1000, "dword*", 0)
_HeapFree($pBuffer)
$pBuffer = _HeapAlloc($iResult[4])
$iResult = DllCall("Ntdll.dll", "dword", "NtQuerySystemInformation", "int", $iClass, _
"ptr", $pBuffer, "dword", $iResult[4], "dword*", 0)
Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $pBuffer)
EndFunc ;==>_NtQuerySystemInformation
Func _RegEnumKey($hRegKey, $iIndex, ByRef $sName)
Local $iResult
$iResult = DllCall("Advapi32.dll", "long", "RegEnumKey", "hWnd", $hRegKey, _
"dword", $iIndex, "str", "", "dword", 256)
$sName = $iResult[3]
Return SetError($iResult[0], 0, $iResult[0] = 0)
EndFunc ;==>_RegEnumKey
Func _DuplicateHandle($hSrc, $hHandle, $hTarget, $iAccess = 0, $iFlags = 2, $fInherit = False)
Local $iResult
If $hSrc = -1 Then $hSrc = _GetCurrentProcess()
If $hTarget = -1 Then $hTarget = _GetCurrentProcess()
$iResult = DllCall("Kernel32.dll", "int", "DuplicateHandle", "hWnd", $hSrc, _
"hWnd", $hHandle, "hWnd", $hTarget, "hWnd*", 0, _
"dword", $iAccess, "int", $fInherit, "dword", $iFlags)
Return SetError(_GetLastError(), 0, $iResult[4])
EndFunc ;==>_DuplicateHandle
Func _NtQueryObject($hHandle, $iClass)
Local $iResult, $pBuffer
$iResult = DllCall("Ntdll.dll", "dword", "NtQueryObject", "hWnd", $hHandle, _
"int", $iClass, "ptr", 0, "ulong", 0, "ulong*", 0)
$pBuffer = _HeapAlloc($iResult[5])
$iResult = DllCall("Ntdll.dll", "dword", "NtQueryObject", "hWnd", $hHandle, _
"int", $iClass, "ptr", $pBuffer, "ulong", $iResult[5], "ulong*", 0)
Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $pBuffer)
EndFunc ;==>_NtQueryObject
Func _GetCurrentProcess()
Local $iResult = DllCall("Kernel32.dll", "hWnd", "GetCurrentProcess")
Return $iResult[0]
EndFunc ;==>_GetCurrentProcess |
评分
-
查看全部评分
|