WindowsAPI查缺补漏-进程
WindowsAPI查缺补漏-进程
创建进程
ShellExecute
打开i可执行文件、文档文件、网址等。
1 | HINSTANCE ShellExecute( |
lpOperation可以是:
枚举值 | 含义 |
---|---|
open | 由关联的默认程序打开lpFile文件/文件夹 |
explore | 资源管理器打开lpFIle文件夹 |
edit | 用编辑器(记事本)打开lpFile指定的文档,不是文档则调用失败 |
打印lpFile指定的文件,不是文档文件则失败 | |
find | 从lpDirectory目录开始搜索 |
执行成功返回大于32的HINSTANCE类型值,失败返回:
枚举值 | 含义 |
---|---|
0 | 操作系统内存或资源不足 |
ERROR_FILE_NOT_FOUND | 找不到指定文件 |
ERROR_PATH_NOT_FOUND | 找不到指定路径 |
ERROR_BAD_FORMAT | .exe文件无效 |
SE_ERR_ACCESSDENIED | 操作系统拒绝对指定文件的访问 |
SE_ERR_ASSOCINCOMPLETE | 文件名关联不完整或无效 |
SE_ERR_DLLNOTFOUND | 找不到指定动态链接库 |
SE_ERR_FNF | 找不到指定文件 |
SE_ERR_NOASSOC | 没有关联的应用程序,或文件不可打印 |
SE_ERR_OOM | 没有足够的内存来完成操作 |
SE_ERR_PNF | 找不到指定路径 |
SE_ERR_SHARE | 发生共享冲突 |
WinExec
运行指定程序,已废弃用CreateProcess
。
1 | UINT WINAPI WinExec( |
CreateProcess
为指定名称的可执行文件创建进程以及进程的主线程。
1 | BOOL WINAPI CreateProcess( |
dwCreationFlags中创建标志可以是:
枚举值 | 含义 |
---|---|
DEBUG_PROCESS | 父进程调试子进程及子进程生成的所有进程,子进程发生特定事件时通知父进程 |
DEBUG_ONLY_THIS_PROCESS | 同DEBUG_PROCESS,但孙进程不通知父进程 |
CREATE_SUSPENDED | 新进程的主线程处于挂起状态,用ResumeThread 恢复运行 |
EXTENDED_STARTUPINFO_PRESENT | 使用扩展启动信息创建进程 |
CREATE_UNICODE_ENVIRONMENT | lpEnvironment参数使用Unicode字符 |
CREATE_DEFAULT_ERROR_MODE | 新进程不继承调用进程错误模式,使用默认错误模式 |
DETACHED_PROCESS | CUI进程时不使用父进程控制台窗口,可用AllocConsole 创建新的控制台。 |
CREATE_NEW_CONSOLE | CUI进程时新建一个控制台窗口 |
CREATE_NO_WINDOW | CUI进程时不创建控制台窗口 |
dwCreationFlags中进程优先级可以是:
枚举值 | 含义 |
---|---|
REALTIME_PRIORITY_CLASS | 实时 |
HIGH_PRIORITY_CLASS | 高 |
ABOVE_NORMAL_PRIORITY_CLASS | 高于标准 |
NORMAL_PRIORITY_CLASS | 标准 |
BELOW_NORMAL_PRIORITY_CLASS | 低于标准 |
IDLE_PRIORITY_CLASS | 低 |
lpStartupInfo中STARTUPINFO或STARTUPINFOEX结构为:
1 | typedef struct _STARTUPINFO { |
dwFlags常用标志有:
枚举值 | 有效字段 |
---|---|
STARTF_USESIZE | dwXSize dwYSize |
STARTF_USESHOWWINDOW | wShowWindow |
STARTF_USEPOSITION | dwX dwY |
STARTF_USECOUNTCHARS | dwXCountChars dwYCountChars |
STARTF_USEFILLATTRIBUTE | dwFillAttribute |
STARTF_USESTDHANDLES | hStdInput hStdOutput hStdError |
对于lpProcessInformation参数的PROCESS_INFORMATION结构记录新进程的信息:
1 | typedef struct _PROCESS_INFORMATION { |
因为系统打开进程内核对象和线程内核对象时,句柄的计数会加1,所以获取后要及时关闭句柄。
例如运行记事本并打开D:\Text.txt文件:
1 | TCHAR szCommandLine[MAX_PATH] = TEXT("Notepad D:\\Test.txt"); |
GetStartupInfo
获取当前进程STARTINFO结构:
1 | VOID WINAPI GetStartupInfo( |
GetModuleHandle
获取调用进程中指定模块的模块句柄(基地址):
1 | HMODULE WINAPI GetModuleHandle( |
当lpModuleName设置为NULL时获取调用进程的可执行文件模块加载基地址,即使在DLL中也返回可执行文件模块基地址。
GetModuleFileName
获取当前进程已加载模块完整路径,已废弃用GetModuleFileNameEx
。
1 | DWORD WINAPI GetModuleFileName( |
GetModuleFileNameEx
后去当前/另一个进程中已加载模块完整路径:
1 | DWORD WINAPI GetModuleFileNameEx( |
需要对hProcess的PROCESS_QUERY_INFORMATION和PROCESS_VM_READ访问权限。
函数成功则返回复制到缓冲区中字符个数,不含终止空字符,失败返回0。
WaitForInputIdle
创建子进程后,CreateProcess
马上返回,但子进程加载和初始化需要时间。用WaitForInputIdle
挂起调用线程直到指定进程初始化完毕或函数等待超时返回。
1 | DWORD WINAPI WaitForInputIdle( |
返回值有:0等待成功、WAIT_TIMEOUT超时时间已过、WAIT_FAILED发生错误。
GetMappedFileName
检查指定内存地址是否位于指定进程地址空间中,并返回进程所对应可执行文件名:
1 | DWORD GetMappedFileName( |
返回的文件名称路径为本机系统路径格式如\Device\HarddiskVolume2\,已废弃用GetModuleFileNameEx
/QueryFullProcessImageName
。
1 | GetModuleFileNameEx(pi.hProcess, NULL, szImageFile, _countof(szImageFile)); |
多进程间共享内核对象
GetCurrentProcess/GetCurrentThread
俩大抽象。
获取当前进程/调用线程句柄,但获取的都是伪句柄。当前进程伪句柄为0xFFFFFFFF,调用线程伪句柄为0xFFFFFFFE。伪句柄不能由子进程继承,不使用时不用关闭。
1 | HANDLE WINAPI GetCurrentProcess(VOID); |
GetCurrentProcessId/GetCurrentThreadId
获取当前进程/调用线程的ID:
1 | DWORD WINAPI GetCurrentProcessId(VOID); |
GetProcessId/GetThreadId
获取指定进程/线程的ID:
1 | DWORD WINAPI GetProcessId( |
OpenProcess
通过进程ID获取进程句柄:
1 | HANDLE WINAPI OpenProcess( |
成功打开一个进程句柄大致进程对象引用计数加1,不需要时用CloseHandle
关闭。
dwDesiredAccess指定访问权限,常用的有:
枚举值 | 允许的操作 |
---|---|
PROCESS_QUERY_INFORMATION | 获取进程相关信息,如访问令牌、退出码、进程优先级 |
PROCESS_SET_INFORMATION | 设置进程相关信息,如进程优先级 |
PROCESS_SUSPEND_RESUME | 挂起或恢复进程 |
PROCESS_TERMINATE | 用TerminateProcess 终止进程 |
PROCESS_DUP_HANDLE | 用DuplicateHandle 复制句柄 |
PROCESS_VM_OPERATION | 修改进程地址空间,如写入内存、修改页面保护属性 |
PROCESS_VM_READ | 对进程地址空间读取 |
PROCESS_VM_WRITE | 对进程地址空间写入 |
PROCESS_CREATE_PROCESS | 创建进程 |
PROCESS_CREATE_THREAD | 创建线程 |
SYNCHRONIZE | 调用等待函数等待进程终止 |
PROCESS_ALL_ACCESS | 所有可能的访问权限 |
当打开系统空闲进程或csrss等系统关键进程,函数调用也失败。
GetProcessIdOfThread
根据线程句柄获取所在进程的ID:
1 | DWORD WINAPI GetProcessIdOfThread( |
DuplicateHandle
复制内核对象句柄:
1 | BOOL WINAPI DuplicateHandle( |
dwOptions有组合:
枚举值 | 含义 |
---|---|
DUPLICATE_CLOSE_SOURCE | 复制后关闭源句柄,内核对象引用计数不变 |
DUPLICATE_SAME_ACCESS | 复制访问权限,忽略dwDesiredAccess参数 |
可以用于32位和64位进程之间复制句柄。
进程终止
ExitProcess
终止调用进程及其所有线程:
1 | VOID WINAPI ExitProcess( |
该函数不返回,因为进程已终止。
TerminateProcess
无条件终止调用进程或其他进程及其所有线程:
1 | BOOL WINAPI TerminateProcess( |
其中hProcess需要对该进程的PROCESS_TERMINATE访问权限。
GetExitCodeProcess
获取一个进程的退出码:
1 | BOOL WINAPI GetExitCodeProcess( |
其中hProcess需要对该进程的PROCESS_QUERY_INFORMATION访问权限。
该函数立即返回,当进程尚未终止时退出码为STILL_ACTIVE。
进程间通信
WM_COPYDATA法
注意SendMessage
将指定消息发送到若干个窗口,直到窗口过程处理完消息后函数才返回。PostMessage
将消息投递到一个线程的消息队列后立即返回,由线程分发。注意PostMessage
、SendNotifyMessage
、SendMessageCallback
的wParam和lParam参数不能传递指针,这些函数立即返回后指针指向的内存会被释放,GetLastError
返回ERROR_MESSAGE_SYNC_ONLY。
用SendMessage
、SendNotifyMessage
、SendMessageCallback
、PostMessage
、PostThreadMessage
发送WM_COPYDATA消息,wParam为目标进程窗口句柄,lParam为指向COPYDATASTRUCT结构的指针,目标进程窗口过程处理完WM_COPYDATA后应返回TRUE。
1 | typedef struct tagCOPYDATASTRUCT { |
对于lpData,目标进程不可以修改共享内存数据v,应提前把数据v复制到自己所属进程的缓冲区中。
FindWindow
查找具有指定窗口类名和窗口标题的窗口:
1 | HWND WINAPI FindWindow( |
参数不区分大小写。
lpClassName可以是用RegisterClass
或RegisterClassEx
注册的窗口类名称,也可以是任何预定义的控件类名称,如对话框窗口类名为#32770。
StringCchPrintf
微软建议使用的格式化字符串,之前学了这里不讲:
1 | HRESULT StringCchPrintf( |
例子
发送方:
1 |
|
接收方:
1 |
|
数据结构定义:
1 |
|
匿名管道法
CreatePipe
创建一个匿名管道:
1 | BOOL WINAPI CreatePipe( |
管道读写用WriteFile
和ReadFile
,句柄hReadPipe和hWritePipe需要用CloseHandle
关闭。因为用于父进程与子进程之间传输数据,创建匿名管道时lpPipeAttributes要设置为可继承,如:
1 | HANDLE hReadPipe, hWritePipe; |
例子
1 |
|
命名管道法
略。
邮件槽法
邮件槽是一种进程间单向通信机制,创建并拥有邮件槽的进程称为邮件槽服务器,邮件槽服务器从邮件槽中读取数据,其他进程可打开邮件槽并写入数据,这些进程称为邮件槽客户端。
CreateMailslot
创建一个指定名称的邮件槽内核对象:
1 | HANDLE WINAPI CreateMailslot( |
指定名称邮件槽已存在时GetLastError
返回ERROR_ALREADY_EXISTS。
用CreateFile
打开邮件槽,用WriteFile
向邮件槽写入数据,用ReadFile
读取数据。读取数据前应用GetMailslotInfo
确定邮件槽中是否由消息。不需要读写数据时用CloseHandle
关闭邮件槽句柄。
GetMailslotInfo
确定邮件槽中是否有消息:
1 | BOOL WINAPI GetMailslotInfo( |
例子
服务端:
1 |
|
客户端:
1 |
|
进程枚举
TlHelp32法
CreateToolhelp32Snapshot
捕获系统中当前正在运行的所有进程的快照,得到一个进程列表:
1 | HANDLE WINAPI CreateToolhelp32Snapshot( |
dwFlags可以是组合:
枚举值 | 枚举对象 |
---|---|
TH32CS_SNAPPROCESS | 系统所有进程,忽略th32ProcessID |
TH32CS_SNAPTHREAD | 系统所有线程,忽略th32ProcessID |
TH32CS_SNAPHEAPLIST | th32ProcessID指定进程中所有堆 |
TH32CS_SNAPMODULE | th32ProcessID指定进程中所有模块 |
TH32CS_SNAPMODULE32 | 64位进程中32位模块 |
TH32CS_SNAPALL | 上面所有 |
TH32CS_INHERIT | 快照句柄可继承 |
Process32First/Process32Next
获取快照中第一个/其他进程信息,直到Process32Next
返回FALSE:
1 | BOOL WINAPI Process32First( |
其中PROCESSENTRY32结构为:
1 | typedef struct tagPROCESSENTRY32 { |
szExeFile返回的只是文件名不带路径。
QueryFullProcessImageName
获取一个进程对应可执行文件的完整路径:
1 | BOOL WINAPI QueryFullProcessImageName( |
dwFlags一般为0,表示普通格式,设置为PROCESS_NAME_NATIVE表示本机系统路径格式,如\Device\HarddiskVolumn3\Windows\System32\svchost.exe。
还有一种更高效的替代方法:用GetModuleFileNameEx
,hModule为NULL表示获取hProcess进程的可执行文件完整路径。
OpenProcessToken
打开与进程关联的访问令牌以获得一个访问令牌句柄:
1 | BOOL OpenProcessToken( |
不用时用CloseHandle
关闭。
LookupPrivilegeValue
获取指定特权名称在系统中的本地唯一标识符LUID:
1 | BOOL LookupPrivilegeValue( |
AdjustTokenPrivileges
启用访问令牌句柄中指定的特权名称:
1 | BOOL AdjustTokenPrivileges( |
其中TOKEN_PRIVILEGES结构:
1 | typedef struct _TOKEN_PRIVILEGES { |
例子
注意Windows中不存在暂停和恢复进程的概念,暂停一个进程中所有线程时,指定TH32CS_SNAPTHREAD标志获取线程列表,并改用Thread32First
和Thread32Next
枚举。同理枚举模块时指定TH32CS_SNAPMODULE并使用Module32First
和Module32Next
,枚举堆时指定TH32CS_SNAPHEAPLIST并使用Heap32First
heHeap32Next
。
1 |
|
EnumProcesses法
EnumProceses
系统维护正在运行的进程的列表,用这个获取进程ID。
1 | BOOL EnumProcesses( |
NtQueryInformationProcess/ZwQueryInformationProcess
获取指定进程信息:
1 |
|
其中ProcessInformationClass可以是:
枚举值 | 含义 | ProcessInformation类型 | 上层封装 |
---|---|---|---|
ProcessBasicInformation | 是否正在被调试的进程环境快PEB结构、进程ID、父进程ID等 | PROCESS_BASIC_INFORMATION | CheckRemoteDebuggerPresent 、GetProcessId |
ProcessDebugPort | 该进程调试器的端口号,非0表示在Ring3调试器控制下运行 | DWORD_PTR | CheckRemoteDebuggerPresent 、IsDebuggerPresent |
ProcessWow64Information | 非0表示该进程正在WOW64环境中运行,0反之 | ULONG_PTR | IsWow64Process |
ProcessImageFileName | 该进程文件名称字段 | UNICODE_STRING | QueryFullProcessImageName 、GetProcessImageFileName |
ProcessBreakOnTermination | 非0表示系统关键进程,0反之,需要PROCESS_QUERY_LIMITED_INFORMATION权限 | ULONG | IsProcessCritical |
对于PROCESS_BASIC_INFORMATION结构有:
1 | typedef struct _PROCESS_BASIC_INFORMATION { |
系统关键进程
LoadLibrary
将指定模块加载到调用进程地址空间中:
1 | HMODULE LoadLibrary( |
注意该函数只是一个宏,实际为LoadLibraryA
或LoadLibraryW
。
LoadLibraryEx
同LoadLibrary
,可指定加载选项:
1 | HMODULE WINAPI LoadLibraryEx( |
当dwFlags为0时同LoadLibrary
,常用选项有:
枚举值 | 含义 |
---|---|
DONT_RESOLVE_DLL_REFERENCES | lpLibFileName指定的为DLL模块时系统不会调用其DllMain ,也不会加载其引用的其他模块 |
LOAD_LIBRARY_AS_DATAFILE | 把lpLibFileName指定的模块作为数据文件映射到调用进程虚拟地址空间,映射后没有可执行属性,函数返回模块句柄,可与LOAD_LIBRARY_AS_IMAGE_RESOURCE一同使用 |
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | 同LOAD_LIBRARY_AS_DATAFILE,但以独占访问模式打开模块文件。 |
LOAD_LIBRARY_AS_IMAGE_RESOURCE | 把lpLibFileName指定的模块作为数据文件映射到调用进程虚拟地址空间,并对模块中相对虚拟地址RVA修复使用户可直接使用模块中虚拟地址而不必根据映射到的地址进行转换 |
FreeLibrary
释放模块并减少引用次数:
1 | BOOL FreeLibrary( |
GetProcAddress
获取其中一个函数地址:
1 | typedef INT_PTR (FAR WINAPI *FARPROC)(); |
RtlSetProcessIsCritical
未公开API,将一个进程设置为系统关键进程,强制结束将导致系统崩溃。这玩意儿也不知道咋用,暂且定义如下:
1 | NTSTATUS RtlSetProcessIsCritical( |
例子
1 |
|
进程环境块PEB
1 | typedef struct _PEB { |
Ldr字段
1 | typedef struct _PEB_LDR_DATA { |
其中LIST_ENTRY作双向链表头和节点定义分别为:
1 | typedef struct _LIST_ENTRY { |
LIST_ENTRY通过CONTAINING_RECORD
计算LDR_DATA_TABLE_ENTRY结构地址:
1 |
其中LDR_DATA_TABLE_ENTRY有:
1 | typedef struct _LDR_DATA_TABLE_ENTRY { |
例如枚举指定进程已加载模块信息:
1 | PROCESS_BASIC_INFORMATION pbi = { 0 }; |
ProcessParameters字段
1 | typedef struct _RTL_USER_PROCESS_PARAMETERS { |
在x64下可以修改,但没法进行伪装。
进程调试
进程空间读写
ReadProcessMemory
读指定进程地址空间地址处内存数据:
1 | BOOL WINAPI ReadProcessMemory( |
WriteProcessMemory
向指定进程地址空间地址处写入数据:
1 | BOOL WINAPI WriteProcessMemory( |
例子
1 |
|
获取模块基地址
EnumProcessModules法
获取进程中每个模块句柄:
1 | BOOL WINAPI EnumProcessModules( |
其中第一个模块句柄就是主程序模块的句柄。例如获取主程序模块基地址:
1 | HMODULE hModule; |
VirtualQueryEx法
通过GetThreadContext
获取目标进程主线程环境,context.Eax
寄存器为程序入口点地址。再用VirutalQueryEx
查询进程虚拟地址空间页面信息,其中MEMORY_BASIC_INFORMATION.AllocationBase
是空间区域/可执行模块基地址。
1 | TCHAR szCommandLine[MAX_PATH] = TEXT("ThreeThousandYears.exe"); // 目标程序 |
GetSystemInfo法
以暂停模式启动目标进程后,可执行模块本身已经在内存中映射,但依赖的DLL模块还没有映射。用GetSystemInfo
查询进程地址空间最小/大内存地址、页面大小,从最小内存地址lpMinAppAddress
开始递增查找第一个MEM_IMAGE类型已提交页面即属于可执行模块空间区域。MEMORY_BASIC_INFORMATION.AllocationBase
是空间区域/可执行模块基地址。
1 | //变量继承VirtualQueryEx法 |
调试API
DebugActiveProcess
使进程进入被调试状态:
1 | BOOL WINAPI DebugActiveProcess( |
DebugActiveProcessStop
停止堆指定进程的调试:
1 | BOOL DebugActiveProcessStop( |
WaitForDebugEvent
等待正在调试的进程发生调试事件:
1 | BOOL WINAPI WaitForDebugEvent( |
其中DEBUG_EVENT有:
1 | typedef struct _DEBUG_EVENT { |
不同调试事件类型有:
调试事件类型 | 含义 |
---|---|
EXCEPTION_DEBUG_EVENT | 被调试进程发生异常事件或被调试进程开始执行第一条指令 |
CREATE_THREAD_DEBUG_EVENT | 被调试进程创建了一个新线程,主线程除外 |
CREATE_PROCESS_DEBUG_EVENT | 被调试进程被创建但还未开始运行,或正在运行的进程被附加到调试器 |
EXIT_THREAD_DEBUG_EVENT | 被调试进程中有线程结束 |
EXIT_PROCESS_DEBUG_EVENT | 被调试进程退出 |
LOAD_DLL_DEBUG_EVENT | 被调试进程加载一个DLL,或系统根据导入表加载DLL,或被调试进程用LoadLibrary 加载DLL |
UNLOAD_DLL_DEBUG_EVENT | 一个DLL从被调试进程中卸载时 |
OUTPUT_DEBUG_STRING_EVENT | 被调试进程用DebugOutputString 时 |
RIP_EVENT | 调试发生错误 |
对于CREATE_THREAD_DEBUG_EVENT,u.CreateProcessInfo
字段有CREATE_PROCESS_DEBUG_INFO结构:
1 | typedef struct _CREATE_PROCESS_DEBUG_INFO { |
对于EXCEPTION_DEBUG_EVENT,u.Exception
字段有EXCEPTION_DEBUG_INFO结构:
1 | typedef struct _EXCEPTION_RECORD { |
其中ExceptionCode异常代码有:
枚举值 | 含义 |
---|---|
EXCEPTION_BREAKPOINT | 遇到断点,如int 3 断点 |
EXCEPTION_SINGLE_STEP | 跟踪陷阱或单步中断。单步中断指当发现TF位为1时触发异常并暂停,系统自动将TF置0。每条指令都单步中断时需要手动TF置1 |
EXCEPTION_ACCESS_VIOLATION | 线程试图读取或写入对其没有适当访问权限的虚拟地址 |
注:标志寄存器前16位分别为:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
CF | PF | AF | ZF | SF | TF | IF | DF | OF |
ExceptionsFlags为异常标志,为0表示程序可继续执行的异常;为EXCEPTION_NONCONTINUABLE表示程序不可继续执行的异常,这时继续执行会发生EXCEPTION_NONCONTINUABLE_EXCEPTION异常。
当发生嵌套异常时,ExceptionRecord指向EXCEPTION_RECORD结构,包含另一个异常的异常信息;没发生嵌套异常则为NULL。
ExceptionInformation数组为描述异常的附加参数,该字段通常为NULL。NumberParameters为ExceptionInformation数组元素个数,最多EXCEPTION_MAXIMUM_PARAMETERS个,通常为0。目前这两个字段只有EXCEPTION_ACCESS_VIOLATION和EXCEPTION_IN_PAGE_ERROR用到了。
对于EXCEPTION_ACCESS_VIOLATION,ExceptionInformation[0]为0表示试图读取非法地址,为1表示试图写入非法地址,为8表示数据执行保护DEP检测到线程执行没可执行权限内存页中的代码。ExceptionInformation[1]为不可访问数据的内存地址。对于EXCEPTION_IN_PAGE_ERROR的ExceptionInformation前两个元素同上,ExceptionInformation[2]表示导致异常的NTSTATUS代码。
ContinueDebugEvent
恢复被调试进程运行:
1 | BOOL WINAPI ContinueDebugEvent( |
dwProcessId和dwThreadId从DEBUG_EVENT中返回即可。有常用dwContinueStatus:
枚举值 | 含义 |
---|---|
DBG_CONTINUE | 当指定线程发生EXCEPTION_DEBUG_EVENT则停止所有异常处理并继续执行该线程,将异常标记为已处理;发生其他调试事件则继续执行 |
DBG_EXCEPTION_NOT_HANDLED | 当指定线程发生EXCEPTION_DEBUG_EVENT则使用被调试进程结构化异常处理程序处理,否则进程终止;发生其他调试事件则继续执行 |
DBG_REPLY_LATER | 继续执行指定线程,再次触发相同异常 |
例子
常见的调试器处理调试事件:
1 | DEBUG_EVENT debugEvent; |
例子
内存读写:
1 |
|
例子
UPX脱壳:
1 |
|
线程环境
GetThreadContext/SetThreadContext
获取/设置线程环境:
1 | BOOL WINAPI GetThreadContext( |
其中CONTEXT在x64下为:
1 | typedef struct DECLSPEC_ALIGN(16) DECLSPEC_NOINITALL _CONTEXT { |
根据线程环境标志ContextFlag可多选,分为:
枚举值 | 所含寄存器 | 含义 |
---|---|---|
CONTEXT_CONTROL | SegSs, Rsp, SegCs, Rip, EFlags | 控制寄存器 |
CONTEXT_INTEGER | Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, R8-R15 | 整数寄存器 |
CONTEXT_SEGMENTS | SegDs, SegEs, SegFs, SegGs | 段寄存器 |
CONTEXT_FLOATING_POINT | Xmm0-Xmm15 | 浮点寄存器 |
CONTEXT_DEBUG_REGISTERS | Dr0-Dr3, Dr6-Dr7 | 调试寄存器 |
注意用这俩函数前应暂停线程,获取/设置相关寄存器值后再恢复线程运行,防止函数执行到一半被Windows切走。例如更改Rip等值时需要目标线程的THREAD_GET_CONTEXT和TRHEAD_SET_CONTEXT权限,如:
1 | CONTEXT Context; |
Spy++
WindowFromPoint
获取指定坐标处窗口的窗口句柄,可能不准确:
1 | HWND WINAPI WindowFromPoint( |
GetParent
获取指定窗口的父窗口句柄:
1 | HWND WINAPI GetParent( |
GetWindow
获取与指定窗口具有指定关系的窗口句柄:
1 | HWND WINAPI GetWindow( |
uCmd可以有:
枚举值 | 含义 |
---|---|
GW_HWNDFIRST | 同一级别中Z顺序最高的窗口 |
GW_HWNDLAST | 同一级别中Z顺序最低的窗口 |
GW_HWNDNEXT | 同一级别中下一个Z顺序窗口 |
GW_HWNDPREV | 同一级别中上一个Z顺序窗口 |
GW_CHILD | Z顺序最高的,即第一个子窗口 |
GetWindowThreadProcessId
根据窗口句柄获取创建该窗口的进程/线程ID:
1 | DWORD WINAPI GetWindowThreadProcessId( |
例子
1 |
|
自删除
用ping实现延迟5秒,第一个del删除该.exe文件,第二个del %0删除该批处理。
1 |
|