#include <WinAPI.au3>
#include <LocalSecurityAuthority.au3>
Const $tagCLIENT_ID = "long UniqueProcessId;long UniqueThreadId"
Const $tagOBJECT_ATTR = "long Size;long RootDirectory;long objName;long Attr;long SecurDescr;long SecurQualityofService"
Const $tagSYSTEM_HANDLE_INFO = "ulong Pid;ubyte ObjType;ubyte Flags;ushort Handle;ptr object;dword GrantedAccess"
Const $tagFILE_NAME_INFO = "ulong Length;wchar FileName[528]"
Func _ProtectFile($sFile, $iProcessId = Default, $fReadable = True)
Local $aPrivilege[1][2] = [[$SE_DEBUG_NAME, 2]]
Local $iResult, $hFile, $hSource, $hTarget, $hDup, $iError, $hToken
$sFile = _GetFullPathName($sFile) ; 获取文件完整路径
If Not FileExists($sFile) Then Return SetError(2, 1, 0)
If $iProcessId = Default Then $iProcessId = "Winlogon.exe" ; 默认挂接到winlogon.exe进程
$iProcessId = ProcessExists($iProcessId) ; 获取进程PID
If $iProcessId = 0 Then Return SetError(2, 2, 0)
If $fReadable = True Then ; 打开文件句柄
$hFile = _WinAPI_CreateFile($sFile, 2, 2, 2, 1, 0) ; 挂接到宿主进程后,文件可以访问
Else
$hFile = _WinAPI_CreateFile($sFile, 2) ; 挂接到宿主进程后,文件不能访问
EndIf
If $hFile < 1 Then Return SetError(_WinAPI_GetLastError(), 0, 0)
$hToken = _OpenProcessToken(-1) ; 提升到DEBUG权限,打开进程句柄所需
If Not _IsPrivilegeEnabled($hToken, $SE_DEBUG_NAME) Then
_AdjustTokenPrivileges($hToken, $aPrivilege)
EndIf
_NtClose($hToken) ; 关闭令牌句柄
$hSource = _NtOpenProcess(@AutoItPid) ; 打开本进程句柄
If $hSource= 0 Then Return SetError(@error, 0, 0)
$hTarget = _NtOpenProcess($iProcessId) ; 宿主进程句柄
If $hTarget = 0 Then Return SetError(@error, 0, 0)
$hDup = _DuplicateHandle($hSource, $hFile, $hTarget, 0, 0, 3) ; 将本进程中打开的文件句柄挂接到目标进程中。
$iError = @error
_NtClose($hFile) ; 关闭所有打开的句柄
_NtClose($hDup)
_NtClose($hSource)
_NtClose($hTarget)
If $hDup = 0 Then Return SetError($iError, 0, 0)
Return 1
EndFunc ;==>_ProtectFile
Func _UnprotectFile($sFile, $iProcessId)
Local $aPrivilege[1][2] = [[$SE_DEBUG_NAME, 2]], $hToken
Local $iError, $tNum, $iNum, $sPath, $pBuffer, $tBuffer, $hHandle, $iPid
Local $iResult, $pHandleList, $tHandleList, $iType, $hDup, $hSrc, $hTarget
$iProcessId = ProcessExists($iProcessId) ; 获取进程PID
If $iProcessId = 0 Then Return SetError(2, 0, 0)
$sFile = _GetFullPathName($sFile) ; 获取文件完整路径
$iType = _GetFileHandleType() ; 不同的系统,文件句柄类型的值将不同。获取文件句柄所对应的值,方法是本进程中打开NUL设
备,然后与枚举到的句柄相比较,就可以知道文件句柄所对应的值了。
If $iType = -1 Then Return SetError(24, 0, 0)
$pHandleList = _NtQuerySystemInformation(0x10) ; 枚举系统中的所有句柄,返回long + SYSTEM_HANDLE_INFO指针。
$iError = @error
$tNum = DllStructCreate("long NumberOfHandles", $pHandleList) ; 获取句柄数量。
$iNum = DllStructGetData($tNum, "NumberOfHandles")
If $iNum < 1 Then Return SetError($iError, 0, 0)
_FreeVariable($tNum)
$pHandleList += 4 ; 指针地址右移2位,指向SYSTEM_HANDLE_INFO结构。
$hToken = _OpenProcessToken(-1) ; 提升至DEBUG权限,打开进程句柄所需。
If Not _IsPrivilegeEnabled($hToken, $SE_DEBUG_NAME) Then
_AdjustTokenPrivileges($hToken, $aPrivilege)
EndIf
_NtClose($hToken)
$hTarget = _NtOpenProcess(-1) ; 打开当前进程句柄
$hSrc = _NtOpenProcess($iProcessId) ; 打开宿主进程句柄
If $hSrc = 0 Then Return SetError(@error, 0, 0)
For $i = 1 to $iNum ; 枚举句柄,获得目标进程中句柄的值。
$tHandleList = DllStructCreate($tagSYSTEM_HANDLE_INFO, $pHandleList)
$pHandleList += DllStructGetSize($tHandleList); 指针偏移DllStructGetSize($tHandleList)字节,指向下一句柄。
If DllStructGetData($tHandleList, "Pid") <> $iProcessId Then ContinueLoop ; 判断句柄所属的进程PID,不是目标进程则跳
过。
If DllStructGetData($tHandleList, "objType") <> $iType Then ContinueLoop ; 判断句柄类型,不是文件句柄则跳过。
$hHandle = DllStructGetData($tHandleList, "Handle") ; 宿主中的句柄值。
$hDup = _DuplicateHandle($hSrc, $hHandle, $hTarget, 0, 0, 0) ; 先复制到当前进程中。
$pBuffer = _NtQueryInformationFile($hDup, 9) ; 根据文件句柄获取文件名,返回FILE_NAME_INFO指针。
If @error Then
_NtClose($hDup)
_HeapFree($pBuffer)
ContinueLoop
EndIf
_NtClose($hDup)
$tBuffer = DllStructCreate($tagFILE_NAME_INFO, $pBuffer)
$sPath = DllStructGetData($tBuffer, "FileName") ; 获取句柄所对应的文件名。
$iPid = DllStructGetData($tHandleList, "Pid") ; 获取句柄所属的进程PID。
_HeapFree($pBuffer)
; 判断是否是指定文件,并是否位于指定进程中。
If StringTrimLeft($sFile, 2) = $sPath AND $iPid = $iProcessId Then
$hSrc = _NtOpenProcess($iProcessId) ; 再次打开宿主进程。
$hDup = _DuplicateHandle($hSrc, $hHandle, $hTarget, 0, 0, 3) ; 卸载句柄,3 -- 复制到目标进程的同时,关闭源
句柄。
If $hDup <> 0 Then
$iResult = _NtClose($hSrc) & _NtClose($hDup) & _NtClose($hTarget) & _HeapFree($pHandleList)
Return SetExtended(_FreeVariable($tBuffer), Not _FreeVariable($tHandleList))
Else
$iResult = _NtClose($hSrc) & _NtClose($hTarget) & _HeapFree($pHandList)
Return SetError(@error, _FreeVariable($tBuffer), _FreeVariable($tHandleList))
EndIf
EndIf
Next
$iResult = _NtClose($hSrc) & _NtClose($hTarget) & _HeapFree($pHandleList)
Return SetError(2, _FreeVariable($tHandleList), _FreeVariable($tBuffer))
EndFunc ;==>_UnprotectFile
Func _GetFullPathName($sFile)
Local $iResult
$iResult = DllCall("Kernel32.dll", "int", "GetFullPathName", _
"str", $sFile, "dword", 260, "str", "", "str", "")
Return $iResult[3]
EndFunc ;==>_GetFullPathName
Func _NtOpenProcess($iPid, $iAccessMask = 0x1F0FFF)
Local $iResult, $tClientId, $tObjAttr
If $iPid = -1 Then $iPid = @AutoItPid
$iPid = ProcessExists($iPid)
If $iPid = 0 Then Return SetError(2, 0, 0)
$tClientId = DllStructCreate($tagCLIENT_ID)
$tObjAttr = DllStructCreate($tagOBJECT_ATTR)
DllStructSetData($tClientId, "UniqueProcessId", $iPid)
DllStructSetData($tObjAttr, "Size", DllStructGetSize($tObjAttr))
$iResult = DllCall("Ntdll.dll", "dword", "NtOpenProcess", _
"long*", 0, "dword", $iAccessMask, _
"ptr", DllStructGetPtr($tObjAttr), _
"ptr", DllStructGetPtr($tClientId))
_FreeVariable($tObjAttr)
_FreeVariable($tClientId)
Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[1])
EndFunc ;==>_NtOpenProcess
Func _NtClose($hHandle)
Local $iResult
$iResult = DllCall("Ntdll.dll", "long", "NtClose", "long", $hHandle)
Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $iResult[0] = 0)
EndFunc ;==>_NtClose
Func _DuplicateHandle($hSrcProcess, $hSrc, $hTarget, $iAccess, $iInherit = 0, $iOption = 0)
Local $iResult
$iResult = DllCall("Kernel32.dll", "int", "DuplicateHandle", _
"hWnd", $hSrcProcess, "hWnd", $hSrc, _
"hWnd", $hTarget, "long*", 0, _
"dword", $iAccess, "int", $iInherit, "int", $iOption)
Return SetError(_GetLastError(), 0, $iResult[4])
EndFunc ;==>_DuplicateHandle
Func _NtQuerySystemInformation($iClass)
Local $pBuffer, $iResult
$pBuffer = _HeapAlloc(0x800)
$iResult = DllCall("Ntdll.dll", "long", "NtQuerySystemInformation", _
"int", $iClass, "ptr", $pBuffer, "dword", 0x800, "long*", 0)
_HeapFree($pBuffer)
$pBuffer = _HeapAlloc($iResult[4])
$iResult = DllCall("Ntdll.dll", "long", "NtQuerySystemInformation", _
"int", $iClass, "ptr", $pBuffer, "dword", $iResult[4], "long*", 0)
Return SetError(_LsaNtStatusToWinError($iResult[0]), 0, $pBuffer)
EndFunc ;==>_NtQuerySystemInformation
Func _NtQueryInformationFile($hFile, $iClass)
Local $iResult, $pBuffer, $pStatus
$pBuffer = _HeapAlloc(532)
$pStatus = _HeapAlloc(12)
$iResult = DllCall("Ntdll.dll", "dword", "NtQueryInformationFile", "long", $hFile, _
"ptr", $pStatus, "ptr", $pBuffer, "ulong", 532, "int", $iClass)
Return SetError(_LsaNtStatusToWinError($iResult[0]), _HeapFree($pStatus), $pBuffer)
EndFunc ;==>_NtQueryInformationFile
Func _GetFileHandleType()
Local $iResult, $hFile, $pBuffer, $tBuffer, $tNum, $iType = -1
$hFile = _WinAPI_CreateFile("NUL", 2, 6, 6, 1, 0)
$pBuffer = _NtQuerySystemInformation(16)
$tNum = DllStructCreate("ulong Number", $pBuffer)
$iNum = DllStructGetData($tNum, "Number")
$pBuffer += 4
For $i = 1 to $iNum
$tSysHandle = DllStructCreate($tagSYSTEM_HANDLE_INFO, $pBuffer)
$iPid = DllStructGetData($tSysHandle, "Pid")
$hHandle = DllStructGetData($tSysHandle, "Handle")
If $iPid = @AutoItPid AND $hHandle = $hFile Then
$iType = DllStructGetData($tSysHandle, "objType")
ExitLoop
EndIf
$pBuffer += DllStructGetSize($tSysHandle)
Next
_NtClose($hFile)
_HeapFree($pBuffer)
_FreeVariable($tNum)
_FreeVariable($tSysHandle)
Return $iType
EndFunc ;==>_GetFileHandleType