远线程注入原理是利用Windows 系统中CreateRemoteThread()这个API,其中第4个参数是准备运行的线程,我们可以将LoadLibrary()填入其中,这样就可以执行远程进程中的LoadLibrary()函数,进而将我们自己准备的DLL加载到远程进程空间中执行。

函数原型:

HANDLE
WINAPI
CreateRemoteThread(
_In_ HANDLE hProcess, //远程线程的句柄
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全属性
_In_ SIZE_T dwStackSize, //栈大小
_In_ LPTHREAD_START_ROUTINE lpStartAddress, //进程处理函数
_In_opt_ LPVOID lpParameter, //进程参数
_In_ DWORD dwCreationFlags, //默认创建后的状态
_Out_opt_ LPDWORD lpThreadId //所创建的线程的ID
);

注入过程:

1.提权

2.根据进程ID打开对方进程OpenProcess,得到进程句柄

3.根据进程句柄在目标进程中申请内存VirtualAllocEx

4.在目标进程中刚刚申请的内存空间中写入所需参数(Dll的完整路径)WriteProcessMemory

5.用GetProcAddress得到LoadLibraryW的模块加载地址

6.启动远程线程CreateRemoteThread,并在第四参数传入该线程需要执行的函数名(即loadlibrary)

BOOL  InjectDllByRemoteThread(ULONG32 ulProcessID, WCHAR* wzDllFullPath)
{
HANDLE ProcessHandle = NULL;
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ulProcessID); if (ProcessHandle==NULL)
{
return FALSE;
} WCHAR* VirtualAddress = NULL; ULONG32 ulDllLength = (ULONG32)_tcslen(wzDllFullPath) + ; VirtualAddress = (WCHAR*)VirtualAllocEx(ProcessHandle, NULL,
ulDllLength * sizeof(WCHAR),
MEM_COMMIT, PAGE_READWRITE); if (VirtualAddress==NULL)
{
CloseHandle(ProcessHandle);
return FALSE;
} // 在目标进程的内存空间中写入所需参数(模块名)
if (!WriteProcessMemory(ProcessHandle, VirtualAddress, (LPVOID)wzDllFullPath, ulDllLength * sizeof(WCHAR), NULL))
{
VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
CloseHandle(ProcessHandle);
return FALSE;
} LPTHREAD_START_ROUTINE FunctionAddress = NULL; FunctionAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW"); HANDLE ThreadHandle = INVALID_HANDLE_VALUE;
//启动远程线程
ThreadHandle = ::CreateRemoteThread(ProcessHandle, NULL, , FunctionAddress, VirtualAddress, , NULL);
if (ThreadHandle==FALSE)
{
VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
CloseHandle(ProcessHandle);
return FALSE;
} // 等待远程线程结束
WaitForSingleObject(ThreadHandle, INFINITE);
// 清理
VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
CloseHandle(ThreadHandle);
CloseHandle(ProcessHandle);
return TRUE;
}
BOOL EnableDebugPrivilege()
{ HANDLE TokenHandle = NULL;
TOKEN_PRIVILEGES TokenPrivilege;
LUID uID; //打开权限令牌
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
{
return FALSE;
} if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID))
{ CloseHandle(TokenHandle);
TokenHandle = INVALID_HANDLE_VALUE;
return FALSE;
} TokenPrivilege.PrivilegeCount = ;
TokenPrivilege.Privileges[].Attributes = SE_PRIVILEGE_ENABLED;
TokenPrivilege.Privileges[].Luid = uID; //在这里我们进行调整权限
if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
CloseHandle(TokenHandle);
TokenHandle = INVALID_HANDLE_VALUE;
return FALSE;
} CloseHandle(TokenHandle);
TokenHandle = INVALID_HANDLE_VALUE;
return TRUE;
}

但是如此有个问题,如果目标进程为32位,但是注入了一个64的Dll则会出问题。

为此我们应当判断目标进程的位数。

我们可以通过解析exe文件(magic数)判断进程是x64还是x86。

根据PE知识,所有的PE文件必须以一个DOS MZ header开始,其实它是一个IMAGE_DOS_HEADER类型的结构,

//DOS头
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number ‘MZ’
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[]; // Reserved words
LONG e_lfanew; // File address of new exe header NT头偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

这是PE文件的大致结构,而我们需要的magic成员在_IMAGE_NT_HEADERS结构体中的选项头中_IMAGE_OPTIONAL_HEADER

_IMAGE_DOS_HEADER结构体的最后一个元素e_lfanew 便是指向_IMAGE_NT_HEADERS的偏移

//NT头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //‘PE’
IMAGE_FILE_HEADER FileHeader; //PE文件头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //PE选项头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
//选项头
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; //我们需要的成员
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

所以判断目标进程的代码为:

enum TargetType
{
WOW_86,
WOW_64,
WOW_ERROR
}; //通过解析exe文件(magic数)判断进程是x64还是x86
TargetType GetWowByReadFile(ULONG32 ulProcessID)
{
HANDLE ProcessHandle = INVALID_HANDLE_VALUE;
ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ulProcessID); if (ProcessHandle == NULL)
{
return WOW_ERROR;
}
//获得Exe模块基地址
ULONG64 ulModuleBaseAddress = (ULONG64)GetModuleBaseAddressByProcessHandle(ProcessHandle); if (ulModuleBaseAddress == NULL)
{
CloseHandle(ProcessHandle);
return WOW_ERROR;
} IMAGE_DOS_HEADER DosHeader = { };
//读取Dos头
if (ReadProcessMemory(ProcessHandle, (PVOID)ulModuleBaseAddress, &DosHeader, sizeof(IMAGE_DOS_HEADER), NULL) == FALSE)
{
CloseHandle(ProcessHandle);
return WOW_ERROR;
} WORD wMagic = ;
//模块加载基地址+Dos头部e_lfanew成员(PE头相对于文件的偏移 4字节)+标准PE头+4字节
if (ReadProcessMemory(ProcessHandle, (PVOID)(ulModuleBaseAddress + DosHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)), &wMagic, sizeof(WORD), NULL) == FALSE)
{
CloseHandle(ProcessHandle);
return WOW_ERROR;
} CloseHandle(ProcessHandle);
if (wMagic == 0x20b)//x64
{
return WOW_64;
}
else if (wMagic == 0x10b)//x86
{
return WOW_86;
}
else
{
return WOW_ERROR;
}
}

结合这个功能,主函数为:

int main()
{
if (EnableDebugPrivilege() == FALSE)
{
return ;
}
ULONG32 ulProcessID = ;
printf("Input A ProcessID to Inject:\r\n");
scanf_s("%d", &ulProcessID, sizeof(ULONG32)); DWORD iOk = GetWowByReadFile(ulProcessID);
switch (iOk)
{
case WOW_64:
if (InjectDllByRemoteThread(ulProcessID, L"InjectDll.dll"))
{
printf("Inject Success!\r\n");
break;
}
case WOW_86:
if (InjectDllByRemoteThread(ulProcessID, L"InjectTest32.dll"))
{
printf("Inject Success!\r\n");
break;
}
default:
break;
}
return ;
}

Dll注入:X86/X64 远程线程CreateRemoteThread 注入的更多相关文章

  1. 使用远程线程来注入DLL

    使用远程线程来注入DLL DLL注入技术要求我们目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL (1)用OpenProcess函数打开目标进程(2)用VirtualAllocEx ...

  2. 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。

    windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...

  3. Dll注入技术之远程线程注入

    DLL注入技术之远线程注入 DLL注入技术指的是将一个DLL文件强行加载到EXE文件中,并成为EXE文件中的一部分,这样做的目的在于方便我们通过这个DLL读写EXE文件内存数据,(例如 HOOK EX ...

  4. windows:shellcode 远程线程hook/注入(一)

    https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode.这么做有个前提条件:目标线程 ...

  5. 『开源重编译』System.Data.SQLite.dll 自适应 x86 x64 AnyCPU 重编译

    背景: > System.Data.SQLite.dll 程序集 不能良好的支持 AngCPU 格式 System.Data.SQLite.dll 在 适应 x86 和 x64 有三个方案: & ...

  6. windows:shellcode 远程线程hook/注入(三)

    今天介绍第三种远程执行shellcode的思路:函数回调: 1.所谓回调,简单理解: windows出厂时,内部有很多事务的处理无法固化(无法100%预料外部会遇到哪些情况),只能留下一堆的接口,让开 ...

  7. windows:shellcode 远程线程hook/注入(五)

    前面几篇文章介绍了通过APC注入.进程注入.windows窗口处理函数回调.kernercallback回调执行shellcode,今天继续介绍通过heap Spray(翻译成中文叫堆喷射)执行she ...

  8. windows:shellcode 远程线程hook/注入(二)

    https://www.cnblogs.com/theseventhson/p/13218651.html   上次分享了基本的远程注入方法,遗留了一个问题:shellcode执行完后怎么回到线程su ...

  9. windows:shellcode 远程线程hook/注入(四)

    https://www.cnblogs.com/theseventhson/p/13236421.html  这里介绍了利用回调函数执行shellcode的基本原理:这里介绍另外一种利用回调执行she ...

随机推荐

  1. vue中computed与methods的异同

    在vue.js中,有methods和computed两种方式来动态当作方法来用的 如下: 两种方式在这种情况下的结果是一样的 写法上的区别是computed计算属性的方式在用属性时不用加(),而met ...

  2. C#面向对象三大特性之三:多态

    比起前面的封装和继承,多态这个概念不是那么好理解.我们还是从一个事例开始: 公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat).狗(Dog).羊(Sheep),这些动物都有共同的特性, ...

  3. DES的雪崩效应分析

    明文固定,密钥改变一个字节 假定明文为11111111(00000001 00000001 00000001 00000001 00000001 00000001 00000001 00000001) ...

  4. luogu3224 永无乡(动态开点,权值线段树合并)

    luogu3224 永无乡(动态开点,权值线段树合并) 永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示.某些 ...

  5. JavaScript 性能优化

    加载和执行 1. </body>闭合标签之前,将所有的<script> 标签放在页面底部,确保在脚步执行之前页面已经完成渲染. 2. 合并脚本.下载单个 100KB 的文件将比 ...

  6. centos6.6上安装beef

    https://rvm.io/https://rvm.io/rvm/securityhttps://www.runoob.com/ruby/ruby-installation-unix.htmlhtt ...

  7. checkbox,不选中传值

    根据W3C的规则未选中的checkbox和禁用的控件不是有效控件,不会被POST.因此如果要未选中的checkbox表示值0的话,就不得不曲线完成了. 最近研究Zend Framework时候,发现其 ...

  8. Jmeter_拦截Excel文件输出流到本地

    一般而言,对于页面的“导出”操作,主要经历如下两个操作:①根据数据库的内容,将文件导出到应用服务器上:②将服务器上的文件下载到本地电脑: Jmeter同LoadRunner类似,只能记录服务端与客户端 ...

  9. linux下apache无法启动之(httpd not running, trying to st)

    这突然接手的服务器,本来是没什么事的,可是因为机房的问题,需要将服务器迁回来,结果可想而知,关机重启了,其中有一台估计诚心给我过不去,待配置好ip并重启了服务后,发现apache无法正常启动了! 先还 ...

  10. 分别使用http,express,koa请求第三方接口

    今天又再次恶补了一下http的内容,确切地说是node.js里面的http的内容,啊,百度了半天express怎么请求第三方接口,结果发现自己买的入门书籍都有这个内容.舍近求远,我真是醉了.还有百度上 ...