• DLL注入——使用远程线程


    1.简介

    从根本上来说,DLL注入技术要求目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL。由于我们不能轻易地控制别人进程中的线程,因此这种方法要求我们在目标进程中创建一个新的线程,Windows提供了创建远程线程的函数。

    1. HANDLE CreateRemoteThread(
    2. [in] HANDLE hProcess,
    3. [in] LPSECURITY_ATTRIBUTES lpThreadAttributes,
    4. [in] SIZE_T dwStackSize,
    5. [in] LPTHREAD_START_ROUTINE lpStartAddress,
    6. [in] LPVOID lpParameter,
    7. [in] DWORD dwCreationFlags,
    8. [out] LPDWORD lpThreadId
    9. );
    • hProcess:用于创建线程的进程的句柄。
    • lpThreadAttributes:指向SECURITY_ATTRIBUTES结构的指针,该结构确定返回的句柄是否可以被子进程继承。
    • dwStackSize:堆栈的初始大小,以字节为单位。如果此参数为零,则新线程使用默认大小。
    • lpStartAddress:指向线程要执行的应用程序定义函数的指针。
    • lpParameter:指向要传递给线程的变量的指针。
    • dwCreationFlags:控制线程创建的标志。
    • lpThreadId:指向接收线程标识符的变量的指针。如果该参数为NULL,则不返回线程标识符。

    2.步骤

    • 用VirtualAllocEx函数在远程进程的地址空间中分配一块内存。
    • 用WriteProcessMemory函数把DLL的路径名复制到第一步分配的内存中。
    • 用GetProcAddress函数来得到LoadLibraryW或LoadLibraryA函数的实际地址(在Kernel32.dll中)
    • 用CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用正确的LoadLibrary函数并在参数中传入第一步分配的内存地址。这时,DLL已经被注入到远程进程的地址空间中,DLL的DllMain函数会收到DLL_PROCESS_ATTACH通知并且执行我们想要执行的代码。
    • VirtualFreeEx来释放第1步分配的内存。
    • 用GetProcAddress函数来得到FreeLibrary函数的实际地址(在Kernel32.dll中)
    • 用CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用FreeLibrary函数并在参数中传入远程DLL的HMODULE。

    步骤1、2、3原理简介:

    字符串“D://remoteDll.dll”,位于调用进程的空间地址中,我们把这个地址传给新创建的远程线程,远程线程再把它传给LoadLibrary,当LoadLibrary去访问这个内存地址的时候,DLL的路径字符串并不在那里,远程进程的线程就很可能引发访问违规。为了解决这个问题,我们需要把DLL的字符串存放到远程进程的地址空间中去,所以会使用VirtualAllocEx和WriteProcessMemory方法。

    3.示例

    3.1先做一个动态库remoteDll.dll

    此库的功能是,一旦被注入到进程的地址空间中,就报告该进程正在使用的所有DLL,并将打印信息写入到本地文件D://out.txt中。

    1. const char* filepath = "D://out.txt";
    2. BOOL APIENTRY DllMain( HMODULE hModule,
    3. DWORD ul_reason_for_call,
    4. LPVOID lpReserved
    5. )
    6. {
    7. switch (ul_reason_for_call)
    8. {
    9. case DLL_PROCESS_ATTACH:
    10. {
    11. FILE* fp = freopen(filepath, "w", stdout);
    12. PBYTE pb = NULL;
    13. MEMORY_BASIC_INFORMATION mbi;
    14. while (VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi))
    15. {
    16. int nlen;
    17. char szModName[MAX_PATH];
    18. if (mbi.State == MEM_FREE)
    19. mbi.AllocationBase = mbi.BaseAddress;
    20. if ((mbi.AllocationBase == hModule) || (mbi.AllocationBase != mbi.BaseAddress) || (mbi.AllocationBase == NULL))
    21. nlen = 0;
    22. else
    23. {
    24. nlen = GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, szModName, _countof(szModName));
    25. }
    26. if (nlen > 0)
    27. {
    28. char szBuf[MAX_PATH] = { 0 };
    29. wsprintfA(szBuf, "\n%p-%s", mbi.AllocationBase, szModName);
    30. printf("%s\n", szBuf);
    31. }
    32. pb += mbi.RegionSize;
    33. }
    34. fclose(fp);
    35. }
    36. break;
    37. case DLL_THREAD_ATTACH:
    38. case DLL_THREAD_DETACH:
    39. case DLL_PROCESS_DETACH:
    40. break;
    41. }
    42. return TRUE;
    43. }

    3.2写一个测试程序

    新建一个带界面的程序

    改了一下窗口界面,这里加了一个菜单项注入。

    点击注入出现弹窗,如下图所示。

    输入进程ID,点击确定即可,下面是注入的回调函数。

    1. // “注入”框的消息处理程序。
    2. INT_PTR CALLBACK Inject(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    3. {
    4. UNREFERENCED_PARAMETER(lParam);
    5. switch (message)
    6. {
    7. case WM_INITDIALOG:
    8. {
    9. return (INT_PTR)TRUE;
    10. }
    11. case WM_COMMAND:
    12. if (LOWORD(wParam) == IDOK)
    13. {
    14. //获取ID值
    15. DWORD processID = GetDlgItemInt(hDlg, IDC_EDIT1, NULL, FALSE);
    16. if (processID == 0)
    17. {
    18. processID = GetCurrentProcessId();
    19. }
    20. if (InjectLib(processID, TEXT("D://remoteDll.dll")))
    21. {
    22. printf("inject ok");
    23. EjectLib(processID, TEXT("D://remoteDll.dll"));
    24. }
    25. else
    26. {
    27. printf("inject failed");
    28. }
    29. EndDialog(hDlg, LOWORD(wParam));
    30. return (INT_PTR)TRUE;
    31. }
    32. else if (LOWORD(wParam) == IDCANCEL)
    33. {
    34. EndDialog(hDlg, LOWORD(wParam));
    35. return (INT_PTR)TRUE;
    36. }
    37. break;
    38. }
    39. return (INT_PTR)FALSE;
    40. }

    InjectLib函数如下:

    1. BOOL WINAPI InjectLib(DWORD processID, PCWSTR pszLibFile)
    2. {
    3. BOOL bOk = FALSE;
    4. HANDLE hProcess = NULL, hThread = NULL;
    5. PWSTR pszLibFileRemote = NULL;
    6. __try
    7. {
    8. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
    9. PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, processID);
    10. if (hProcess == NULL)
    11. __leave;
    12. int cch = 1 + lstrlenW(pszLibFile);
    13. int cb = cch * sizeof(wchar_t);
    14. pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
    15. if (pszLibFileRemote == NULL)
    16. __leave;
    17. if (!WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, cb, NULL))
    18. __leave;
    19. FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
    20. if (pfnThreadRtn == NULL)
    21. __leave;
    22. hThread = CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)pfnThreadRtn, pszLibFileRemote, 0, NULL);
    23. if (hThread == NULL)
    24. __leave;
    25. WaitForSingleObject(hThread, INFINITE);
    26. bOk = TRUE;
    27. }
    28. __finally
    29. {
    30. if (pszLibFileRemote != NULL)
    31. VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);
    32. if (hThread != NULL)
    33. CloseHandle(hThread);
    34. if (hProcess != NULL)
    35. CloseHandle(hProcess);
    36. }
    37. return bOk;
    38. }

    EjectLib函数如下:

    1. BOOL WINAPI EjectLib(DWORD processID, PCWSTR pszLibFile)
    2. {
    3. BOOL bOk = FALSE;
    4. HANDLE hthSnapshot = NULL;
    5. HANDLE hProcess = NULL, hThread = NULL;
    6. PWSTR pszLibFileRemote = NULL;
    7. __try
    8. {
    9. //获取指定进程的快照,以及这些进程使用的堆、模块和线程。
    10. hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);
    11. if (hthSnapshot == INVALID_HANDLE_VALUE)
    12. __leave;
    13. MODULEENTRY32W me = { sizeof(me) };
    14. BOOL bFound = FALSE;
    15. BOOL bMoreMods = Module32FirstW(hthSnapshot, &me); //检索与进程关联的第一个模块的信息。
    16. for (; bMoreMods; bMoreMods = Module32NextW(hthSnapshot, &me)) //检索与进程或线程关联的下一个模块的信息
    17. {
    18. bFound = (_wcsicmp(me.szModule, pszLibFile) == 0) || (_wcsicmp(me.szExePath, pszLibFile));
    19. if (bFound)
    20. break;
    21. }
    22. if (!bFound)
    23. __leave;
    24. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
    25. PROCESS_VM_OPERATION, FALSE, processID);
    26. if (hProcess == NULL)
    27. __leave;
    28. FARPROC pfnThreadRtn = GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
    29. if (pfnThreadRtn == NULL)
    30. __leave;
    31. //me.modBaseAddr 在所属进程的上下文中模块的基地址。
    32. hThread = CreateRemoteThread(hProcess, NULL, 0, (PTHREAD_START_ROUTINE)pfnThreadRtn, me.modBaseAddr, 0, NULL);
    33. if (hThread == NULL)
    34. __leave;
    35. WaitForSingleObject(hThread, INFINITE);
    36. bOk = TRUE;
    37. }
    38. __finally
    39. {
    40. if (hthSnapshot != NULL)
    41. CloseHandle(hthSnapshot);
    42. if (hThread != NULL)
    43. CloseHandle(hThread);
    44. if (hProcess != NULL)
    45. CloseHandle(hProcess);
    46. }
    47. return bOk;
    48. }

    3.3执行程序

    打开一个记事本程序,找到PID,如下图所示,PID = 20752。

    运行3.2中的程序,输入这个PID,点击确定注入成功。

    3.4运行结果

    注入之后,在本地文件out.txt中,可以看见正在该记事本程序正在使用的DLL。

  • 相关阅读:
    gqday1
    深度解读js中数组的findIndex方法
    LCP 01.猜数字
    YOLOv8改进PAN结构:Lowlevel Feature Alignment,集特征对齐、信息融合和信息注入于一体,增强模型对不同尺寸物体的检测能力
    react文件预览插件react-file-viewer
    33个常用JavaScript功能已封装成方法
    二、JumpServer堡垒机管理员手册
    面试官:如何避免缓存脏读
    微前端的初探索
    快速排序原理JAVA和Scala实现-函数式编程的简洁演示
  • 原文地址:https://blog.csdn.net/wzz953200463/article/details/126591449