龙马谷

 找回密码
 立即注册

QQ登录

只需一步,快速开始

龙马谷VIP会员办理客服QQ:82926983(如果临时会话没有收到回复,请先加QQ好友再发。)
1 [已完结] GG修改器新手入门与实战教程 31课 2 [已完结] GG修改器美化修改教程 6课 3 [已完结] GG修改器Lua脚本新手入门教程 12课
4 [已完结] 触动精灵脚本新手入门必学教程 22课 5 [已完结] 手游自动化脚本入门实战教程 9课 6 [已完结] C++射击游戏方框骨骼透视与自瞄教程 27课
7 [已完结] C++零基础UE4逆向开发FPS透视自瞄教程 29课 8 [已完结] C++零基础大漠模拟器手游自动化辅助教程 22课 9 [已完结] C++零基础开发DXF内存脚本辅助教程 32课
以下是天马阁VIP教程,本站与天马阁合作,赞助VIP可以获得天马阁对应VIP会员,名额有限! 点击进入天马阁论坛
1 [已完结] x64CE与x64dbg入门基础教程 7课 2 [已完结] x64汇编语言基础教程 16课 3 [已完结] x64辅助入门基础教程 9课
4 [已完结] C++x64内存辅助实战技术教程 149课 5 [已完结] C++x64内存检测与过检测技术教程 10课 6 [已完结] C+x64二叉树分析遍历与LUA自动登陆教程 19课
7 [已完结] C++BT功能原理与x64实战教程 29课 8 [已完结] C+FPS框透视与自瞄x64实现原理及防护思路
查看: 4623|回复: 0

Wow64(32位进程)注入DLL到64位进程

[复制链接]

14

主题

3

回帖

22

积分

编程入门

Rank: 1

龙马币
48

DLL注入

向其他进程注入DLL通常的做法是通过调用CreateRemoteThread这个API在目标进程内创建一个远程线程,用这个线程来调用LoadLibraryA或LoadLibraryW(下文统称LoadLibrary)以实现让目标进程加载指定的DLL文件。使用CreateRemoteThread创建一个远程线程需要传入一个线程过程函数的地址,并且这个函数地址是需要在目标进程中有效的。由于LoadLibrary是kernel32.dll的导出函数,所以对于运行在同一个系统上的同为32位的进程或同为64位的进程可以假定彼此进程内的

LoadLibrary函数的地址是相同的。并且CreateRemoteThread的线程过程函数和LoadLibrary的参数个数相同,且参数都是指针,因此通常都是直接将LoadLibrary作为CreateRemoteThread的过程函数。然后使用VirtualAllocEx在目标进程中分配内存,使用WriteProcessMemory往这块内存中写入DLL文件路径,将这块内存的地址作为线程过程函数(LoadLibrary)的参数。

在64位的Windows操作系统上32位进程中的LoadLibrary函数地址与64位进程的函数地址不同,因此如果想对64位进程注入DLL,简单的做法就是使用64位进程来执行注入工作。但是如果能让32位进程注入DLL到64位进程显然更好。

在一番Google之后找到了这篇文章。这篇文章的作者研究出来一种在Wow64进程中执行x64代码的方法,并且将其封装成了这个库。
本文就是介绍如何使用这个库实现Wow64环境下32位进程向64位进程注入DLL。

Wow64环境下32位进程注入64位进程
32位进程难以注入DLL进64位进程是由于两个进程内LoadLibrary的地址不同,32位进程无法知道64位进程的LoadLibrary函数地址。使用wow64ext这个库在Wow64环境下可以让32位进程获取到64位的ntdll.dll的导出函数(得到的地址与64进程的地址是一样的)。

本文使用ntdll中的这3个未文档的函数来注入DLL。

  1. NTSTATUS
  2. NTAPI
  3. RtlCreateUserThread(
  4.     _In_ HANDLE processHandle,
  5.     _In_ SECURITY_DESCRIPTOR* securityDescriptor,
  6.     _In_ BOOLEAN createSuspended,
  7.     _In_ ULONG stackZeroBits,
  8.     _Inout_opt_ size_t* stackReserved,
  9.     _Inout_opt_ size_t* stackCommit,
  10.     _In_ const void* startAddress,
  11.     _In_ void* startParameter,
  12.     _Inout_ HANDLE* threadHandle,
  13.     _Inout_opt_ CLIENT_ID* clientID
  14.     );

  15. NTSTATUS
  16. NTAPI
  17. LdrLoadDll(
  18.     _In_opt_ PWSTR SearchPath,
  19.     _In_opt_ PULONG LoadFlags,
  20.     _In_ PUNICODE_STRING Name,
  21.     _Out_opt_ PVOID *BaseAddress
  22.     );

  23. VOID
  24. NTAPI
  25. RtlExitUserThread( _In_ NTSTATUS Status );
复制代码

使用RtlCreateUserThread创建远程线程,在远程线程中调用LdrLoadDll加载要注入的DLL文件,最后在远程线程中调用RtlExitUserThread退出线程。
为了在远程线程中调用两个函数(LdrLoadDll、RtlExitUserThread),需要将要执行的x64代码写入目标进程,然后让远程线程执行这段代码,在这之前需要了解一些预备知识。
可以看MSDN中的这篇文章。通过这个篇文章我们知道了。
在调用约定上Windows在x64进行了统一,也就是说不管你有没有显式指定调用约定,指定了何种调用约定,最终编译后都使用__fastcall这一种调用约定。
在参数传递上对于Integer类型(含指针)前4个参数通过RCX、RDX、R8、R9寄存器传递,其他参数通过栈传递。

LdrLoadDll有4个参数都是指针,RtlExitUserThread只有1个参数是Integer类型。为这两个函数传递参数只通过寄存器就足够了。

当然我们不需要自己去写汇编代码再将汇编代码转成机器码,首先先写下面这样一段代码。

  1. typedef unsigned long long DWORD64;

  2. typedef DWORD64 (*Func4_Type)(DWORD64, DWORD64, DWORD64, DWORD64);
  3. typedef DWORD64 (*Func1_Type)(DWORD64);

  4. void ThreadProc(void*)
  5. {
  6.     ((Func4_Type)(0x1234567890123456))(0x1111111111111111, 0x2222222222222222, 0x3333333333333333, 0x4444444444444444);
  7.     ((Func1_Type)(0x6543210987654321))(0x5555555555555555);
  8. }
复制代码

然后使用VC编译器将其编译成x64的代码,再反汇编它。
VS2013 Debug 反汇编的结果
VS2013 Debug 反汇编的结果
跟据内存地址,容易得到下面的机器码与汇编代码的对应关系。

  1. 0x48 0x89 0x4c 0x24 0x08                           mov       qword ptr [rsp+8],rcx
  2. 0x57                                                          push      rdi
  3. 0x48 0x83 0xec 0x20                                   sub       rsp,20h
  4. 0x48 0x8b 0xfc                                            mov       rdi,rsp
  5. 0xb9 0x08 0x00 0x00 0x00                           mov       ecx,8
  6. 0xb8 0xcc 0xcc 0xcc 0xcc                             mov       eac,0CCCCCCCCh
  7. 0xf3 0xab                                                    rep stos  dword ptr [rdi]
  8. 0x48 0x8b 0x4c 0x24 0x30                           mov       rcx,qword ptr [__formal]
  9. 0x49 0xb9 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44  mov       r9,4444444444444444h
  10. 0x49 0xb8 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33  mov       r8,3333333333333333h
  11. 0x48 0xba 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22  mov       rdx,2222222222222222h
  12. 0x48 0xb9 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11  mov       rcx,1111111111111111h
  13. 0x48 0xb8 0x56 0x34 0x12 0x90 0x78 0x56 0x34 0x12  mov       rax,1234567890123456h
  14. 0xff 0xd0                                          call      rax
  15. 0x48 0xb9 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55  mov       rcx,5555555555555555h
  16. 0x48 0xb8 0x21 0x43 0x65 0x87 0x09 0x21 0x43 0x65  mov       rax,6543210987654321h
  17. 0xff 0xd0                                       call      rax
复制代码

只要在运行的时候根据获取到的函数地址和参数地址替换对应机器码然后将机器码写入目标进程,创建线程执行这段代码就能够实现Wow64进程注入DLL到64位进程了。

完整的实现代码如下(VS2012编译通过,Windows 8 x64测试注入成功)。

  1. #include <memory>
  2. #include <string>
  3. #include <Windows.h>
  4. #include "wow64ext.h"

  5. enum class InjectResult {
  6.     OK,
  7.     Error_OpenProcess,
  8.     Error_VirtualAllocEx,
  9.     Error_GetProcAddress,
  10.     Error_WriteProcessMemory,
  11.     Error_CreateRemoteThread
  12. };

  13. template<typename Res, typename Deleter>
  14. class ScopeResource {
  15.     Res res;
  16.     Deleter deleter;
  17.     ScopeResource(const ScopeResource&) {}
  18. public:
  19.     Res get() const {
  20.         return this->res;
  21.     }
  22.     ScopeResource(Res res, Deleter deleter) : res(res), deleter(deleter) {}
  23.     ~ScopeResource() {
  24.         this->deleter(this->res);
  25.     }
  26. };

  27. InjectResult Wow64InjectWin64(DWORD dwProcessId, const std::wstring& filename)
  28. {
  29.     DWORD dwDesiredAccess = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ;
  30.     auto closeProcessHandle = [](HANDLE hProcess) {
  31.         if(hProcess != NULL) CloseHandle(hProcess);
  32.     };
  33.     ScopeResource<HANDLE, decltype(closeProcessHandle)> targetProcessHandle(OpenProcess(dwDesiredAccess, FALSE, dwProcessId), closeProcessHandle);
  34.     if(targetProcessHandle.get() == NULL) {
  35.         return InjectResult::Error_OpenProcess;
  36.     }
  37.     unsigned char injectCode[] = {
  38.         0x48, 0x89, 0x4c, 0x24, 0x08,                               // mov       qword ptr [rsp+8],rcx
  39.         0x57,                                                       // push      rdi
  40.         0x48, 0x83, 0xec, 0x20,                                     // sub       rsp,20h
  41.         0x48, 0x8b, 0xfc,                                           // mov       rdi,rsp
  42.         0xb9, 0x08, 0x00, 0x00, 0x00,                               // mov       ecx,8
  43.         0xb8, 0xcc, 0xcc, 0xcc, 0xcc,                               // mov       eac,0CCCCCCCCh
  44.         0xf3, 0xab,                                                 // rep stos  dword ptr [rdi]
  45.         0x48, 0x8b, 0x4c, 0x24, 0x30,                               // mov       rcx,qword ptr [__formal]
  46.         0x49, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       r9,0
  47.         0x49, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       r8,0
  48.         0x48, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rdx,0
  49.         0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rcx,0
  50.         0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rax,0
  51.         0xff, 0xd0,                                                 // call      rax
  52.         0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rcx,0
  53.         0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rax,0
  54.         0xff, 0xd0                                                  // call      rax
  55.     };

  56.     size_t parametersMemSize = sizeof(DWORD64) + sizeof(_UNICODE_STRING_T<DWORD64>) + (filename.size() + 1) * sizeof(wchar_t);
  57.     auto freeInjectCodeMem = [&targetProcessHandle, &injectCode](DWORD64 address) {
  58.         if(address != 0) VirtualFreeEx64(targetProcessHandle.get(), address, sizeof(injectCode), MEM_COMMIT | MEM_RESERVE);
  59.     };
  60.     ScopeResource<DWORD64, decltype(freeInjectCodeMem)> injectCodeMem(VirtualAllocEx64(targetProcessHandle.get(), NULL, sizeof(injectCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE), freeInjectCodeMem);
  61.     auto freeParametersMem = [&targetProcessHandle, parametersMemSize](DWORD64 address) {
  62.         if(address != 0) VirtualFreeEx64(targetProcessHandle.get(), address, parametersMemSize, MEM_COMMIT | MEM_RESERVE);
  63.     };
  64.     ScopeResource<DWORD64, decltype(freeParametersMem)> parametersMem(VirtualAllocEx64(targetProcessHandle.get(), NULL, parametersMemSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE), freeParametersMem);
  65.     if (injectCodeMem.get() == 0 || parametersMem.get() == 0) {
  66.         return InjectResult::Error_VirtualAllocEx;
  67.     }
  68.     DWORD64 ntdll64 = GetModuleHandle64(L"ntdll.dll");
  69.     DWORD64 ntdll_LdrLoadDll = GetProcAddress64(ntdll64, "LdrLoadDll");
  70.     DWORD64 ntdll_RtlExitUserThread = GetProcAddress64(ntdll64, "RtlExitUserThread");
  71.     DWORD64 ntdll_RtlCreateUserThread = GetProcAddress64(ntdll64, "RtlCreateUserThread");
  72.     if(ntdll_LdrLoadDll == 0 || ntdll_RtlExitUserThread == 0 || ntdll_RtlCreateUserThread == 0) {
  73.         return InjectResult::Error_GetProcAddress;
  74.     }
  75.     std::unique_ptr<unsigned char[]> parameters(new unsigned char[parametersMemSize]);
  76.     std::memset(parameters.get(), 0, parametersMemSize);
  77.     _UNICODE_STRING_T<DWORD64>* upath = reinterpret_cast<_UNICODE_STRING_T<DWORD64>*>(parameters.get() + sizeof(DWORD64));
  78.     upath->Length = filename.size() * sizeof(wchar_t);
  79.     upath->MaximumLength = (filename.size() + 1) * sizeof(wchar_t);
  80.     wchar_t* path = reinterpret_cast<wchar_t*>(parameters.get() + sizeof(DWORD64) + sizeof(_UNICODE_STRING_T<DWORD64>));
  81.     std::copy(filename.begin(), filename.end(), path);
  82.     upath->Buffer = parametersMem.get() + sizeof(DWORD64) + sizeof(_UNICODE_STRING_T<DWORD64>);

  83.     union {
  84.         DWORD64 from;
  85.         unsigned char to[8];
  86.     } cvt;

  87.     // r9
  88.     cvt.from = parametersMem.get();
  89.     std::memcpy(injectCode + 32, cvt.to, sizeof(cvt.to));

  90.     // r8
  91.     cvt.from = parametersMem.get() + sizeof(DWORD64);
  92.     std::memcpy(injectCode + 42, cvt.to, sizeof(cvt.to));

  93.     // rax = LdrLoadDll
  94.     cvt.from = ntdll_LdrLoadDll;
  95.     std::memcpy(injectCode + 72, cvt.to, sizeof(cvt.to));

  96.     // rax = RtlExitUserThread
  97.     cvt.from = ntdll_RtlExitUserThread;
  98.     std::memcpy(injectCode + 94, cvt.to, sizeof(cvt.to));

  99.     if(FALSE == WriteProcessMemory64(targetProcessHandle.get(), injectCodeMem.get(), injectCode, sizeof(injectCode), NULL)
  100.         || FALSE == WriteProcessMemory64(targetProcessHandle.get(), parametersMem.get(), parameters.get(), parametersMemSize, NULL)) {
  101.         return InjectResult::Error_WriteProcessMemory;
  102.     }

  103.     DWORD64 hRemoteThread = 0;
  104.     struct {
  105.       DWORD64 UniqueProcess;
  106.       DWORD64 UniqueThread;
  107.     } client_id;

  108.     X64Call(ntdll_RtlCreateUserThread, 10,
  109.         (DWORD64)targetProcessHandle.get(), // ProcessHandle
  110.         (DWORD64)NULL,                      // SecurityDescriptor
  111.         (DWORD64)FALSE,                     // CreateSuspended
  112.         (DWORD64)0,                         // StackZeroBits
  113.         (DWORD64)NULL,                      // StackReserved
  114.         (DWORD64)NULL,                      // StackCommit
  115.         injectCodeMem.get(),                // StartAddress
  116.         (DWORD64)NULL,                      // StartParameter
  117.         (DWORD64)&hRemoteThread,            // ThreadHandle
  118.         (DWORD64)&client_id);               // ClientID
  119.     if(hRemoteThread != 0) {
  120.         CloseHandle((HANDLE)hRemoteThread);
  121.         return InjectResult::OK;
  122.     }
  123.     return InjectResult::Error_CreateRemoteThread;
  124. }
复制代码

这段代码在创建远程线程成功即认为注入成功,为了更加准确的判断是否注入成功可以在注入的机器码增加额外的代码来判断是否注入成功。




您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

龙马谷| C/C++辅助教程| 安卓逆向安全| 论坛导航| 免责申明|Archiver|
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表龙马谷立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2018-2021 All Right Reserved.
在线客服
快速回复 返回顶部 返回列表