向其他进程注入DLL通常的做法是通过调用CreateRemoteThread这个API在目标进程内创建一个远程线程,用这个线程来调用LoadLibraryALoadLibraryW(下文统称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位进程注入64位DLL到64位进程显然更好。

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

Wow64环境下32位程序注入64位DLL到64位进程

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

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

1234567891011121314151617181920212223242526272829
NTSTATUSNTAPIRtlCreateUserThread(    _In_ HANDLE processHandle,    _In_ SECURITY_DESCRIPTOR* securityDescriptor,    _In_ BOOLEAN createSuspended,    _In_ ULONG stackZeroBits,    _Inout_opt_ size_t* stackReserved,    _Inout_opt_ size_t* stackCommit,    _In_ const void* startAddress,    _In_ void* startParameter,    _Inout_ HANDLE* threadHandle,    _Inout_opt_ CLIENT_ID* clientID    );

NTSTATUSNTAPILdrLoadDll(    _In_opt_ PWSTR SearchPath,    _In_opt_ PULONG LoadFlags,    _In_ PUNICODE_STRING Name,    _Out_opt_ PVOID *BaseAddress    );

VOIDNTAPIRtlExitUserThread(    _In_ NTSTATUS Status    );

使用RtlCreateUserThread创建远程线程,在远程线程中调用LdrLoadDll加载要注入的DLL文件,最后在远程线程中调用RtlExitUserThread退出线程。

为了在远程线程中调用两个函数(LdrLoadDll、RtlExitUserThread),需要将要执行的x64代码写入目标进程,然后让远程线程执行这段代码,在这之前需要了解一些预备知识。可以看MSDN中的这篇文章。通过这个篇文章我们知道了。

  • 在调用约定上Windows在x64进行了统一,也就是说不管你有没有显式指定调用约定,指定了何种调用约定,最终编译后都使用__fastcall这一种调用约定。
  • 在参数传递上对于Integer类型(含指针)前4个参数通过RCXRDXR8R9寄存器传递,其他参数通过栈传递。

LdrLoadDll有4个参数都是指针,RtlExitUserThread只有1个参数是Integer类型。为这两个函数传递参数只通过寄存器就足够了。
当然我们不需要自己去写汇编代码再将汇编代码转成机器码,首先先写下面这样一段代码。

12345678910
typedef unsigned long long DWORD64;

typedef DWORD64 (*Func4_Type)(DWORD64, DWORD64, DWORD64, DWORD64);typedef DWORD64 (*Func1_Type)(DWORD64);

void ThreadProc(void*){    ((Func4_Type)(0x1234567890123456))(0x1111111111111111, 0x2222222222222222, 0x3333333333333333, 0x4444444444444444);    ((Func1_Type)(0x6543210987654321))(0x5555555555555555);}

然后使用VC编译器将其编译成x64的代码,再反汇编它。

VS2013 Debug 反汇编的结果

跟据内存地址,容易得到下面的机器码与汇编代码的对应关系。

1
0x48 0x89 0x4c 0x24 0x08                           mov       qword ptr [rsp+8],rcx
0x57                                               push      rdi
0x48 0x83 0xec 0x20                                sub       rsp,20h
0x48 0x8b 0xfc                                     mov       rdi,rsp
0xb9 0x08 0x00 0x00 0x00                           mov       ecx,8
0xb8 0xcc 0xcc 0xcc 0xcc                           mov       eac,0CCCCCCCCh
0xf3 0xab                                          rep stos  dword ptr [rdi]
0x48 0x8b 0x4c 0x24 0x30                           mov       rcx,qword ptr [__formal]
0x49 0xb9 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44  mov       r9,4444444444444444h
0x49 0xb8 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33  mov       r8,3333333333333333h
0x48 0xba 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22  mov       rdx,2222222222222222h
0x48 0xb9 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11  mov       rcx,1111111111111111h
0x48 0xb8 0x56 0x34 0x12 0x90 0x78 0x56 0x34 0x12  mov       rax,1234567890123456h
0xff 0xd0                                          call      rax
0x48 0xb9 0x55 0x55 0x55 0x55 0x55 0x55 0x55 0x55  mov       rcx,5555555555555555h
0x48 0xb8 0x21 0x43 0x65 0x87 0x09 0x21 0x43 0x65  mov       rax,6543210987654321h
0xff 0xd0                                          call      rax

只要在运行的时候根据获取到的函数地址和参数地址替换对应机器码然后将机器码写入目标进程,创建线程执行这段代码就能够实现Wow64进程注入DLL到64位进程了。
完整的实现代码如下(VS2012编译通过,Windows 8 x64测试注入成功)。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
#include <memory>#include <string>#include <Windows.h>

#include "wow64ext.h"

enum class InjectResult {    OK,    Error_OpenProcess,    Error_VirtualAllocEx,    Error_GetProcAddress,    Error_WriteProcessMemory,    Error_CreateRemoteThread};

template<typename Res, typename Deleter>class ScopeResource {    Res res;    Deleter deleter;    ScopeResource(const ScopeResource&) {}public:    Res get() const {        return this->res;    }    ScopeResource(Res res, Deleter deleter) : res(res), deleter(deleter) {}    ~ScopeResource() {        this->deleter(this->res);    }};

InjectResult Wow64InjectWin64(DWORD dwProcessId, const std::wstring& filename){    DWORD dwDesiredAccess = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ;    auto closeProcessHandle = [](HANDLE hProcess) {        if(hProcess != NULL) CloseHandle(hProcess);    };    ScopeResource<HANDLE, decltype(closeProcessHandle)> targetProcessHandle(OpenProcess(dwDesiredAccess, FALSE, dwProcessId), closeProcessHandle);    if(targetProcessHandle.get() == NULL) {        return InjectResult::Error_OpenProcess;    }    unsigned char injectCode[] = {        0x48, 0x89, 0x4c, 0x24, 0x08,                               // mov       qword ptr [rsp+8],rcx        0x57,                                                       // push      rdi        0x48, 0x83, 0xec, 0x20,                                     // sub       rsp,20h        0x48, 0x8b, 0xfc,                                           // mov       rdi,rsp        0xb9, 0x08, 0x00, 0x00, 0x00,                               // mov       ecx,8        0xb8, 0xcc, 0xcc, 0xcc, 0xcc,                               // mov       eac,0CCCCCCCCh        0xf3, 0xab,                                                 // rep stos  dword ptr [rdi]        0x48, 0x8b, 0x4c, 0x24, 0x30,                               // mov       rcx,qword ptr [__formal]        0x49, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       r9,0        0x49, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       r8,0        0x48, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rdx,0        0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rcx,0        0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rax,0        0xff, 0xd0,                                                 // call      rax        0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rcx,0        0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov       rax,0        0xff, 0xd0                                                  // call      rax    };

    size_t parametersMemSize = sizeof(DWORD64) + sizeof(_UNICODE_STRING_T<DWORD64>) + (filename.size() + 1) * sizeof(wchar_t);    auto freeInjectCodeMem = [&targetProcessHandle, &injectCode](DWORD64 address) {        if(address != 0) VirtualFreeEx64(targetProcessHandle.get(), address, sizeof(injectCode), MEM_COMMIT | MEM_RESERVE);    };    ScopeResource<DWORD64, decltype(freeInjectCodeMem)> injectCodeMem(VirtualAllocEx64(targetProcessHandle.get(), NULL, sizeof(injectCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE), freeInjectCodeMem);    auto freeParametersMem = [&targetProcessHandle, parametersMemSize](DWORD64 address) {        if(address != 0) VirtualFreeEx64(targetProcessHandle.get(), address, parametersMemSize, MEM_COMMIT | MEM_RESERVE);    };    ScopeResource<DWORD64, decltype(freeParametersMem)> parametersMem(VirtualAllocEx64(targetProcessHandle.get(), NULL, parametersMemSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE), freeParametersMem);    if (injectCodeMem.get() == 0 || parametersMem.get() == 0) {        return InjectResult::Error_VirtualAllocEx;    }    DWORD64 ntdll64 = GetModuleHandle64(L"ntdll.dll");    DWORD64 ntdll_LdrLoadDll = GetProcAddress64(ntdll64, "LdrLoadDll");    DWORD64 ntdll_RtlExitUserThread = GetProcAddress64(ntdll64, "RtlExitUserThread");    DWORD64 ntdll_RtlCreateUserThread = GetProcAddress64(ntdll64, "RtlCreateUserThread");    if(ntdll_LdrLoadDll == 0 || ntdll_RtlExitUserThread == 0 || ntdll_RtlCreateUserThread == 0) {        return InjectResult::Error_GetProcAddress;    }    std::unique_ptr<unsigned char[]> parameters(new unsigned char[parametersMemSize]);    std::memset(parameters.get(), 0, parametersMemSize);    _UNICODE_STRING_T<DWORD64>* upath = reinterpret_cast<_UNICODE_STRING_T<DWORD64>*>(parameters.get() + sizeof(DWORD64));    upath->Length = filename.size() * sizeof(wchar_t);    upath->MaximumLength = (filename.size() + 1) * sizeof(wchar_t);    wchar_t* path = reinterpret_cast<wchar_t*>(parameters.get() + sizeof(DWORD64) + sizeof(_UNICODE_STRING_T<DWORD64>));    std::copy(filename.begin(), filename.end(), path);    upath->Buffer = parametersMem.get() + sizeof(DWORD64) + sizeof(_UNICODE_STRING_T<DWORD64>);

    union {        DWORD64 from;        unsigned char to[8];    } cvt;

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

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

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

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

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

    DWORD64 hRemoteThread = 0;    struct {      DWORD64 UniqueProcess;      DWORD64 UniqueThread;    } client_id;

    X64Call(ntdll_RtlCreateUserThread, 10,        (DWORD64)targetProcessHandle.get(), // ProcessHandle        (DWORD64)NULL,                      // SecurityDescriptor        (DWORD64)FALSE,                     // CreateSuspended        (DWORD64)0,                         // StackZeroBits        (DWORD64)NULL,                      // StackReserved        (DWORD64)NULL,                      // StackCommit        injectCodeMem.get(),                // StartAddress        (DWORD64)NULL,                      // StartParameter        (DWORD64)&hRemoteThread,            // ThreadHandle        (DWORD64)&client_id);               // ClientID    if(hRemoteThread != 0) {        CloseHandle((HANDLE)hRemoteThread);        return InjectResult::OK;    }    return InjectResult::Error_CreateRemoteThread;}

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

32位程序注入64位DLL到64位进程相关推荐

  1. win7 64 php5ts.dll,windows7 64位 php5.3安装php_mongo.dll方法

    windows 7 64位系统安装php mongodb 模块时需要选择64位的dll才能正确使用目前最新支持的是php_mongo-1.3.2RC1-5.3-vc9-x86_64.dll 这个dll ...

  2. PWN入门(5)32位程序与64位程序和构造ROP链

    简介 "pwn"这个词的源起以及它被广泛地普遍使用的原因,源自于魔兽争霸某段讯息上设计师打字时拼错而造成的,原先的字词应该是"own"这个字,因为 'p' 与 ...

  3. 32位程序和64位程序这些区别你知道吗?

    我们在编写C/C++程序时,32位程序和64位程序的代码有何区别?如何编写既可以编译成32位程序又可以编译成64位程序的代码? 代码上的区别 实际上,对于32位程序和64位程序来说,代码上的区别不大, ...

  4. 64位Ubuntu系统兼容32位程序

    0 前言 使用Ubuntu16.04(64bit)进行嵌入式开发时,默认情况下无法正常运行32位的交叉编译工具链,因此需要安装额外兼容包. 1 运行32位程序 若只是需要在Ubuntu16.04(64 ...

  5. (转)移植32位程序64位系统

    64个系统已经为32位的应用程序准备了运行32位程序的环境,这个环境就是WOW64的. WOW64 是Windows -32-on-Windows-64 的缩写.它为现有的 32 位应用程序提供了 3 ...

  6. 软件工程师,不了解64位程序开发你就out了

    概述 □ 64位Windows系统下也能运行32位程序,是因为有一个WOW64子系统.它能将32位应用程序的API调用转换成对原生64位系统的调用.正是因为WOW64的存在,32位应用程序在64位系统 ...

  7. 32位dll注入到64位程序_你用的32位还是64位?有什么区别呢?

    (给程序员零距离加星标,了解项目开发.) 粉丝福利:小编会从今天留言中抽选3名小伙伴赠送现金红包,感谢大家一直以来的支持!文末见喽!通过前面两篇文章的学习,外甥对电脑的产生了各种好奇,差点把自己电脑拆 ...

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

    http://blog.poxiao.me/p/wow64-process-inject-dll-into-x64-process/#Wow64环境下32位进程注入64位进程 DLL注入 向其他进程注 ...

  9. MSVCP120D.dll引起的32位和64位程序运行区别

    本人的第一篇技术博客,如有遗漏请见谅. 在没有装vs2013的电脑上(装了vcredist_x86),一个64位应用程序在运行时,弹出0xc000007b错误.而装了vs2013,可以运行.让人摸不着 ...

最新文章

  1. bp神经网络算法步流程_基于遗传算法的BP神经网络优化算法模板
  2. 【Linux笔记(001) 】-- centos7 系统目录结构与文件
  3. Cuboid特征提取算法
  4. Linux 5.4 rc1发布: 正式支持Leez P710开发板(转载)
  5. CTF-压缩包密码CRC爆解工具安装
  6. 杭州中联高级技术培训中心CCNA实验手册
  7. html5 css3中的一些笔记
  8. 光栅衍射主极大个数_大学物理——光的干涉和衍射(二)
  9. 22-React JSX语法
  10. 为什么蚂蚁金服架构师建议从Zookeeper开始提升技术水平?
  11. Java 并发编程AQS基本介绍
  12. [ACM_动态规划] hdu 1176 免费馅饼 [变形数塔问题]
  13. The operation cannot be completed. See the details.
  14. css3的高级而有用且很少人知道的属性和样式
  15. 嵌入式操作系统开发板中的串口编程——光敏电阻数据读取
  16. typedef void far *LPVOID 的具体定义
  17. gitlab服务: kex_exchange_identification: Connection closed by remote host
  18. 录像机中码流类型中定时、事件、网传代表什么意思?
  19. 自动点击网页脚本---selenium库使用
  20. 如何布置环境?自己这边打版了,后台怎么打版才能创建活动?

热门文章

  1. firefox PAC代理
  2. cucumber的hooks
  3. 基于阿里云服务器环境搭建到项目上线系列文章之四——安装composer
  4. zookeeper知识点扫盲
  5. jdb java_java jdb命令详解
  6. liblinear简介
  7. Java并发编程实战读书笔记一
  8. MapReduce词频统计编程
  9. 计算机SCI期刊审稿人,一般关注论文的那些问题? - 易智编译EaseEditing
  10. 文件描述符与inode的关系