此篇文章说是原创有些牵强。就像题目所说的,更多的是对前人方法的总结。写作的初衷倒也不是技术方面的研究,不过是工作的需求罢了。
方法中涉及到一些函数需要提权,其实我一直以为网上那个标准的提权函数没什么用,直到这次写程序我才知道原来有的时候是真的需要提权的。现附上一份比较好看的提权代码,也方便自己以后使用。
BOOL AdjustProcessPrivilege(HANDLE hProcess, LPCTSTR lpPrivilegeName, DWORD dwPrivilegeAttribute)
{
BOOL bRetValue = FALSE;
HANDLE hProcessToken;
//如果hProcess是NULL,说明调用者想要调整当前进程的权限,使用GetCurrentProcess获得的进程句柄无需关闭
HANDLE hOpenProcess = (hProcess != NULL) ? hProcess : GetCurrentProcess();
//打开进程令牌,期望的权限为可以调整权限和查询,得到进程令牌句柄
if(OpenProcessToken(hOpenProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken) != FALSE)
{
LUID stPrivilegeLuid;
//通过权限名称,查找指定的权限的LUID值
if(LookupPrivilegeValue(NULL, lpPrivilegeName, &stPrivilegeLuid) != FALSE)
{
//设置新的权限
TOKEN_PRIVILEGES stNewTokenPrivilege;
stNewTokenPrivilege.PrivilegeCount = 1;
stNewTokenPrivilege.Privileges[0].Luid = stPrivilegeLuid;
stNewTokenPrivilege.Privileges[0].Attributes = dwPrivilegeAttribute;
//调整权限
if(AdjustTokenPrivileges(hProcessToken, FALSE, &stNewTokenPrivilege, sizeof(stNewTokenPrivilege), NULL, NULL) != FALSE)
{
bRetValue = TRUE;
}
}
//关闭进程令牌句柄
CloseHandle(hProcessToken);
}
return bRetValue;
}
调用方法如下:
if(AdjustProcessPrivilege((HANDLE)NULL, SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED) == FALSE ||
AdjustProcessPrivilege((HANDLE)NULL, SE_TAKE_OWNERSHIP_NAME, SE_PRIVILEGE_ENABLED) == FALSE ||
AdjustProcessPrivilege((HANDLE)NULL, SE_SECURITY_NAME, SE_PRIVILEGE_ENABLED) == FALSE ||
AdjustProcessPrivilege((HANDLE)NULL, SE_AUDIT_NAME, SE_PRIVILEGE_ENABLED) == FALSE)
{
printf(("Adjust process privilege failed!/n"));
}
整个提权流程实现起来比较傻瓜化,很符合我的喜好。
好了,步入正体。开始总结如何遍历一个进程中所打开的句柄。首先,介绍两个结构体。
typedef struct _SYSTEM_HANDLE
{
ULONG uIdProcess;
UCHAR ObjectType; // OB_TYPE_* (OB_TYPE_TYPE, etc.)
UCHAR Flags; // HANDLE_FLAG_* (HANDLE_FLAG_INHERIT, etc.)
USHORT Handle;
PVOID pObject;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG uCount;
SYSTEM_HANDLE aSH[];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
这两个结构体定义了系统句柄的信息。接下来,我们看代码:
void MyCloseHandle(DWORD pid)
{
//获取进程中的句柄
PSYSTEM_HANDLE_INFORMATION Info;
ULONG r;
CHAR Name[MAX_PATH];
HANDLE hProcess, hFile;
hHeap = GetProcessHeap();
Info = (PSYSTEM_HANDLE_INFORMATION)GetInfoTable(SystemHandleInformation);
if (Info)
{
for (r = 0; r < Info->uCount; r++)
{
if (Info->aSH[r].uIdProcess == pid)
{
hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, Info->aSH[r].uIdProcess);
if (hProcess)
{
if (DuplicateHandle(hProcess, (HANDLE)Info->aSH[r].Handle, GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
GetFileName(hFile, Name);
if(strstr(Name, "Intel") != NULL)
{
printf("PID=%d FileHandle %d FileName=%s
", Info->aSH[r].uIdProcess, Info->aSH[r].Handle, Name);
}
CloseHandle(hFile);
}
CloseHandle(hProcess);
}
}
}
HeapFree(hHeap, 0, Info);
}
printf("Duplicate Finish.
");
}
hHeap是一个HANDLE型的全局变量,由于用的地方多并且我比较懒,所以定义成了一个全局变量。最好还是作为局部变量从函数中传递。至于GetInfoTable留到下面再说。SystemHandleInformation则是用来获取系统中句柄信息的一个重要参数。参见结构体:
typedef enum _SYSTEMINFOCLASS
{
SystemBasicInformation, // 0x002C
SystemProcessorInformation, // 0x000C
SystemPerformanceInformation, // 0x0138
SystemTimeInformation, // 0x0020
SystemPathInformation, // not implemented
SystemProcessInformation, // 0x00C8+ per process
SystemCallInformation, // 0x0018 + (n * 0x0004)
SystemConfigurationInformation, // 0x0018
SystemProcessorCounters, // 0x0030 per cpu
SystemGlobalFlag, // 0x0004 (fails if size != 4)
SystemCallTimeInformation, // not implemented
SystemModuleInformation, // 0x0004 + (n * 0x011C)
SystemLockInformation, // 0x0004 + (n * 0x0024)
SystemStackTraceInformation, // not implemented
SystemPagedPoolInformation, // checked build only
SystemNonPagedPoolInformation, // checked build only
SystemHandleInformation, // 0x0004 + (n * 0x0010)
SystemObjectTypeInformation, // 0x0038+ + (n * 0x0030+)
SystemPageFileInformation, // 0x0018+ per page file
SystemVdmInstemulInformation, // 0x0088
SystemVdmBopInformation, // invalid info class
SystemCacheInformation, // 0x0024
SystemPoolTagInformation, // 0x0004 + (n * 0x001C)
SystemInterruptInformation, // 0x0000, or 0x0018 per cpu
SystemDpcInformation, // 0x0014
SystemFullMemoryInformation, // checked build only
SystemLoadDriver, // 0x0018, set mode only
SystemUnloadDriver, // 0x0004, set mode only
SystemTimeAdjustmentInformation, // 0x000C, 0x0008 writeable
SystemSummaryMemoryInformation, // checked build only
SystemNextEventIdInformation, // checked build only
SystemEventIdsInformation, // checked build only
SystemCrashDumpInformation, // 0x0004
SystemExceptionInformation, // 0x0010
SystemCrashDumpStateInformation, // 0x0004
SystemDebuggerInformation, // 0x0002
SystemContextSwitchInformation, // 0x0030
SystemRegistryQuotaInformation, // 0x000C
SystemAddDriver, // 0x0008, set mode only
SystemPrioritySeparationInformation,// 0x0004, set mode only
SystemPlugPlayBusInformation, // not implemented
SystemDockInformation, // not implemented
SystemPowerInfo, // 0x0060 (XP only!)
SystemProcessorSpeedInformation, // 0x000C (XP only!)
SystemTimeZoneInformation, // 0x00AC
SystemLookasideInformation, // n * 0x0020
SystemSetTimeSlipEvent,
SystemCreateSession, // set mode only
SystemDeleteSession, // set mode only
SystemInvalidInfoClass1, // invalid info class
SystemRangeStartInformation, // 0x0004 (fails if size != 4)
SystemVerifierInformation,
SystemAddVerifier,
SystemSessionProcessesInformation, // checked build only
MaxSystemInfoClass
} SYSTEMINFOCLASS, *PSYSTEMINFOCLASS;
通篇文章用到了很多系统未公开函数和结构体。其实这些东西网上都可以查到,我就不细说了。其实水平太菜,想说清楚也不太容易。
当找到属于目标进程中的句柄后,通过DuplicateHandle将该句柄再次打开,只不过这次打开为当前进程所用,以备获取该句柄的其它信息。接下来通过GetFileName获得该句柄的名称。当然这个函数也留到后面再说。我们先来看一下GetInfoTable。
PVOID GetInfoTable(IN ULONG ATableType)
{
ULONG mSize = 0x8000;
PVOID mPtr;
NTSTATUS status;
do
{
mPtr = HeapAlloc(hHeap, 0, mSize);
if (!mPtr) return NULL;
memset(mPtr, 0, mSize);
HMODULE hNtDLL = LoadLibrary("NTDLL.DLL");
if ( !hNtDLL )
{
return FALSE;
}
ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDLL, "ZwQuerySystemInformation");
if( ZwQuerySystemInformation == NULL)
{
return FALSE;
}
status = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
HeapFree(hHeap, 0, mPtr);
mSize = mSize * 2;
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (NT_SUCCESS(status)) return mPtr;
HeapFree(hHeap, 0, mPtr);
return NULL;
}
这个函数则是利用ZwQuerySystemInformation获取当前系统中的句柄信息。而ZwQuerySystemInformation则是一个未公开函数。
typedef NTSTATUS (WINAPI *ZWQUERYSYSTEMINFORMATION)(unsigned long, PVOID, ULONG, PULONG);
NTSTATUS
ZwQuerySystemInformation(
IN SYSTEMINFOCLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
然后我们再看GetFileName,看看是如何获取到句柄所对应的文件名称的。
void GetFileName(HANDLE hFile, PCHAR TheName)
{
HANDLE hThread;
PNM_INFO Info = (PNM_INFO)HeapAlloc(hHeap, 0, sizeof(NM_INFO));
Info->hFile = hFile;
hThread = CreateThread(NULL, 0, GetFileNameThread, Info, 0, NULL);
if (WaitForSingleObject(hThread, INFINITE) == WAIT_TIMEOUT) TerminateThread(hThread, 0);
CloseHandle(hThread);
memset(TheName, 0, MAX_PATH);
WideCharToMultiByte(CP_ACP, 0, Info->Info.FileName, Info->Info.FileNameLength >> 1, TheName, MAX_PATH, NULL, NULL);
HeapFree(hHeap, 0, Info);
}
其中,PNM_INFO结构体如下:
typedef struct _FILE_NAME_INFORMATION {
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
typedef struct _NM_INFO
{
HANDLE hFile;
FILE_NAME_INFORMATION Info;
WCHAR Name[MAX_PATH];
} NM_INFO, *PNM_INFO;
接下来这里通过创建线程GetFileNameThread来进一步获取句柄信息。
DWORD WINAPI GetFileNameThread(PVOID lpParameter)
{
PNM_INFO NmInfo = (PNM_INFO)lpParameter;
IO_STATUS_BLOCK IoStatus;
HMODULE hNtDLL = LoadLibrary("NTDLL.DLL");
if ( !hNtDLL )
{
return FALSE;
}
ZWQUERYINFORMATIONFILE ZwQueryInformationFile = (ZWQUERYINFORMATIONFILE)GetProcAddress(hNtDLL, "ZwQueryInformationFile");
if( ZwQueryInformationFile == NULL)
{
return FALSE;
}
ZwQueryInformationFile(NmInfo->hFile, &IoStatus, &NmInfo->Info, sizeof(NM_INFO) - sizeof(HANDLE), FileNameInformation);
return 0;
}
我们发现,函数中利用到了ZwQueryInformationFile来获取句柄详细信息。而ZwQueryInformationFile则又是一个未公开函数。
typedef NTSTATUS (WINAPI *ZWQUERYINFORMATIONFILE)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG,
FILE_INFORMATION_CLASS);
NTSTATUS
ZwQueryInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);
而FILE_INFORMATION_CLASS则是一个枚举类型。
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation=1,
FileFullDirectoryInformation,
FileBothDirectoryInformation,
FileBasicInformation,
FileStandardInformation,
FileInternalInformation,
FileEaInformation,
FileAccessInformation,
FileNameInformation,
FileRenameInformation,
FileLinkInformation,
FileNamesInformation,
FileDispositionInformation,
FilePositionInformation,
FileFullEaInformation,
FileModeInformation,
FileAlignmentInformation,
FileAllInformation,
FileAllocationInformation,
FileEndOfFileInformation,
FileAlternateNameInformation,
FileStreamInformation,
FilePipeInformation,
FilePipeLocalInformation,
FilePipeRemoteInformation,
FileMailslotQueryInformation,
FileMailslotSetInformation,
FileCompressionInformation,
FileCopyOnWriteInformation,
FileCompletionInformation,
FileMoveClusterInformation,
FileQuotaInformation,
FileReparsePointInformation,
FileNetworkOpenInformation,
FileObjectIdInformation,
FileTrackingInformation,
FileOleDirectoryInformation,
FileContentIndexInformation,
FileInheritContentIndexInformation,
FileOleInformation,
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
时间有点紧,总结的有些粗略。回头抽出时间来再完善吧。