GHOST系统之家 - Windows系统光盘下载网站!
当前位置:GHOST系统之家>电脑问题 > 网络安全编程:远程线程编程

网络安全编程:远程线程编程

来源:Ghost系统之家浏览:时间:2022-10-11 11:50:12

windows操作系统下,为了避免各个进程相互影响,每个进程地址空间都是被隔离的。所谓“远程线程”,并不是跨计算机的,而是跨进程的。简单来说,就是进程A要在进程B中创建一个线程,这就叫远程线程。

远程线程被木马、外挂等程序广泛使用,反病毒软件中也离不开远程线程的技术。技术应用的两面性取决于自己的个人行为意识,良性的技术学习对自己的人生发展是非常有好处的,就算谈不上好处,至少不会给自己带来不必要的麻烦。

关于远程线程的知识,本文介绍3个例子,分别是DLL的注入、卸载远程DLL和不依赖DLL进行代码注入。

1. DLL远程注入

木马或病毒编写的好坏取决于其隐藏的程度,而不在于其功能的多少。无论是木马还是病毒,都是可执行程序。如果它们是EXE文件的话,那么在运行时必定会产生一个进程,就很容易被发现。为了不被发现,在编写木马或病毒时可以选择将其编写为DLL文件。DLL文件的运行不会单独创建一个进程,它的运行被加载到进程的地址空间中,因此其隐蔽性相对较好。DLL文件如果不被进程加载又如何在进程的地址空间中运行呢?方式是强制让某进程加载DLL文件到其地址空间中去,这个强制的手段就是现在要介绍的远程线程。

创建远程线程的函数CreateRemoteThread()的定义如下:

该函数的功能是创建一个远程的线程。我们把CreateThread()函数和CreateRemoteThread()函数进行比较。对于CreateThread()函数来说,CreateRemoteThread()函数比其多了一个hProcess参数,该参数是指定要创建线程的进程句柄。其实CreateThread()函数的内容实现就是依赖于CreateRemoteThread()函数来完成的。CreateThread()函数的代码实现如下:

在上面的代码中,NtGetCurrentProcess()函数的功能是获得当前进程的句柄。

CreateRemoteThread()函数是给其他进程创建线程使用的,其第一个参数是指定某进程的句柄,获取进程的句柄使用API函数OpenProcess(),该函数需要提供PID作为参数。

除了hProcess参数以外,剩余的关键参数就只有lpStartAddress和lpParameter两个了。lpStartAddress指定线程函数的地址,lpParameter指定传递给线程函数的参数。前面提到,每个进程的地址空间是隔离的,那么新创建的线程函数的地址也应该在目标进程中,而不应该在调用CreateRemoteThread()函数的进程中。同样,传递给线程函数的参数也应该在目标进程中。

如何让线程函数的地址在目标进程中呢?如何让线程函数的参数也可以传递到目标进程中呢?在讨论这个问题以前,先来考虑线程函数要完成的功能。这里主要完成的功能是注入一个DLL文件到目标进程中,那么线程函数的功能就是加载DLL文件。加载DLL文件使用的是LoadLibrary()函数。LoadLibrary()函数的定义:

比较两个函数可以发现,除了函数的返回值类型和参数类型以外,其函数格式是相同的。这里只考虑其相同的部分。因为其函数的格式相同,首先调用约定相同,都是WINAPI(也就是__stdcall方式);其次函数个数相同,都只有一个。那么,可以直接把LoadLibrary()函数作为线程函数创建到指定的进程中。LoadLibrary()的参数是欲加载的DLL文件的完整路径,只要在CreateRemoteThread()函数中赋值一个指向DLL文件完整路径的指针给LoadLibrary()函数即可。这样使用CreateRemoteThread()函数就可以创建一个远程线程了。不过,还有两个问题没有解决,首先是如何将LoadLibrary()函数的地址放到目标进程空间中让CreateRemoteThread()调用,其次是传递给LoadLibrary()函数的参数也需要在目标进程空间中,并且要通过CreateRemoteThread()函数指定给LoadLibrary()函数。

首先解决第1个问题,即如何将LoadLibrary()函数的地址放到目标进程空间中。LoadLibrary()函数是系统中的Kernel32.dll的导出函数,Kernel32.dll这个DLL文件在任何进程中的加载位置都是相同的,也就是说,LoadLibrary()函数的地址在任何进程中的地址都是相同的。因此,只要在进程中获得LoadLibrary()函数的地址,那么该地址在目标进程中也可以使用。CreateRemoteThread()函数的线程地址参数直接传递LoadLibrary()函数的地址即可。

其次解决第2个问题,即如何将欲加载的DLL文件完整路径写入目标进程中。这需要借助WriteProcessMemory()函数,其定义如下:

该函数的功能是把lpBuffer中的内容写到进程句柄是hProcess进程的lpBaseAddress地址处,写入长度为nSize。

参数说明如下。

hProcess:该参数是指定进程的进程句柄。

lpBaseAddress:该参数是指定写入目标进程内存的起始地址。

lpBuffer:该参数是要写入目标进程内存的缓冲区起始地址。

nSize:该参数是指定写入目标内存中的缓冲区的长度。

lpNumberOfBytesWritten:该参数用于接收实际写入内容的长度。

该函数的功能非常强大,比如在破解方面,用该函数可以实现一个“内存补丁”;在开发方面,该函数可以用于修改目标进程中指定的值(比如游戏修改器可以修改游戏中的钱、红、蓝等)。

使用该函数可以把DLL文件的完整路径写入到目标进程的内存地址中,这样就可以在目标进程中用LoadLibrary()函数加载指定的DLL文件了。解决了上面的两个问题,还有第3个问题需要解决。WriteProcessMemory()函数的第2个参数是指定写入目标进程内存的缓冲区起始地址。这个地址在目标进程中,那么这个地址在目标进程的哪个位置呢?目标进程中的内存块允许把DLL文件的路径写进去吗?

第3个要解决的问题是如何确定应该将DLL文件的完整路径写入目标进程的哪个地址。对于目标进程来说,事先是不会准备一块地址让用户进行写入的,用户能做的是自己在目标进程中申请一块内存,然后把DLL文件的路径进行写入,写入在目标进程新申请到的内存空间中。在目标进程中申请内存的函数是VirtualAllocEx(),其定义如下:

VirtualAllocEx()函数的参数说明如下。

hProcess:该参数是指定进程的进程句柄。

lpAddress:该参数是指在目标进程中申请内存的起始地址。

dwSize:该参数是指在目标进程中申请内存的长度。

flAllocationType:该参数指定申请内存的状态类型。

flProtect:该参数指定申请内存的属性。

该函数的返回值是在目标进程申请到的内存块的起始地址。

到此,关于编写一个DLL注入的所有知识都已经具备了。现在开始编写一个DLL注入的工具,其界面如图1所示。

图1 DLL注入/卸载器

该工具有2个作用,分别是注入DLL和卸载被注入的DLL。关于卸载被注入的DLL的功能,将在后面进行介绍。在界面上要求输入两部分内容,第1部分是欲注入的DLL文件的完整路径(一定要是完整路径),第2部分是进程的名称。

首先看一下关于界面的操作,代码如下:

代码中调用了另外两个函数,第1个是由进程名获得PID的函数,第2个是用于DLL注入的函数。GetProcId()函数的代码如下:

InjectDll()函数的代码如下:

InjectDll()函数有 2 个参数,分别是目标进程的 ID 值和要被注入的 DLL 文件的完整路径。在代码中获得的不是LoadLibrary()函数的地址,而是 LoadLibraryA()函数的地址。在系统中其实没有 LoadLibrary()函数,有的只是LoadLibraryA()和 LoadLibraryW()两个函数。这两个函数分别针对 ANSI 字符串和 UNICODE 字符串。而LoadLibrary()函数只是一个宏。在编写程序的时候,直接使用该宏是可以的。如果要获取 LoadLibrary()函数的地址,就要明确指定是获取LoadLibraryA()还是 LoadLibraryW()。

LoadLibrary()宏定义如下:

只要涉及字符串的函数,都会有相应的ANSI版本和UNICODE版本;其余不涉及字符串的函数,没有ANSI版本和UNICODE版本的区别。

为了测试DLL加载是否成功,在代码的DllMain()函数中加入如下代码:

现在测试一下注入的效果,如图2和图3所示。

图2 DLL文件被注入成功的提示

图3 查看进程中的DLL列表确认被装载成功

在图2中,弹出的对话框是DLL程序在DLL_PROCESS_ATTACH时出现的。其所在的进程为notepad.exe。从图2中可以看出,弹出提示框的标题处是notepad.exe进程的路径。图3是用工具查看进程中所加载的DLL文件列表,可以看出,通过注入工具注入的DLL文件已经被加载到notepad.exe的进程空间中。

如果要对系统进程进行注入的话,由于进程权限的关系是无法注入成功的。在打开目标进程时用到了OpenProcess()函数,由于权限不够,会导致无法打开进程并获得进程句柄。通过调整当前进程的权限,可以打开系统进程并获得进程句柄。如果在Win8或更高版本上运行注入程序的话,需要选中注入工具单击右键,选择“以管理员身份运行”才可以完成注入。

2. 卸载被注入的DLL文件

DLL注入如果应用在木马方面,危害很大,这里完成一个卸载被注入DLL的程序。卸载被注入DLL程序的思路和注入的思路是一样的,而且代码的改动也非常小。区别在于现在的功能是卸载,而不是注入。

DLL卸载使用的API函数是FreeLiabrary(),其定义如下:

该函数的参数是要卸载的模块的句柄。

FreeLibrary()函数使用的模块句柄可以通过Module32First()和Module32Next()两个函数获取。在使用Module32First()和Module32Next()两个函数的时候,需要用到MODULEENTRY32结构体,该结构体中保存了模块的句柄。MODULEENTRY32结构体的定义如下:

该结构体中的hModule为模块的句柄,szModule为模块的名称,szExePath是完整的模块的名称(所谓完整,包括路径和模块名称)。

卸载远程进程中DLL模块的代码如下:

卸载远程进程中DLL的实现代码比DLL注入的代码要简单,这里就不做过多的介绍了。

3. 无DLL的代码注入

DLL文件的注入与卸载都完成了,整个注入与卸载的过程其实就是让远程线程执行一次LoadLibrary()函数或FreeLibrary()函数。远程线程装载一个DLL文件,通过DllMain()调用DLL中的具体功能代码,这样注入DLL后就可以让DLL做很多事情了。是否可以不依赖DLL文件直接向目标进程写入要执行的代码,以完成特定的功能呢?答案是可以。

要在目标进程中完成一定的功能,就需要使用相关的API函数,不同的API函数实现在不同的DLL中。Kernel32.dll文件在每个进程中的地址是相同的,但是并不代表其他DLL文件在每个进程中的地址都是一样的。这样,在目标进程中调用API函数时,必须使用LoadLibrary()函数和GetProcAddress()函数动态调用用到的每个API函数。把想要使用的API函数及API函数所在的DLL文件都封装到一个结构体中,直接写入目标进程的空间中。同时也直接把要在远程执行的代码也写入目标进程的内存空间中,最后调用CreateRemoteThread()函数即可将其运行。

通过实现一个简单的例子让远程线程弹出一个提示对话框,但是不借助于DLL。本程序所使用的API函数在前面都已经介绍过了。根据前面的步骤先来定义一个结构体,其定义如下:

该结构体中保存了LoadLibraryA()、GetProcAddress()、GetModuleHandle()和GetModuleFileName()四个API函数的地址。这四个API函数都属于Kernel32.dll的导出函数,因此可以在注入前进行获取。User32Dll中保存“User32.dll”字符串,因为MessageBoxA()函数是由User32.dll的导出函数。Str中保存的是通过MessageBoxA()函数弹出的字符串。

注入代码类似于前面介绍的注入代码,不过需要在注入代码中定义一个结构体变量,并进行相应的初始化,代码如下:

上面的注入代码除了对结构体变量初始化外,还将线程函数代码写入目标进程空间的内存中。线程函数的代码如下:

上面就是无DLL注入的全部代码,编译连接并运行它。启动一个记事本程序来进行测试,可惜报错了。问题出在哪里呢?VC6的默认编译是Debug版本,这样会加入很多调试信息。而某些调试信息并不存在于代码中,而是在其他DLL模块中。这样,当执行到调试相关的代码时会访问不存在的DLL模块中的代码,就导致了报错。

将以上代码使用Release方式进行编译连接,然后可以无误地执行,如图4所示。

图4 Release方式下编译注入成功

编译的Debug版也可以进行无DLL的注入,只是实现起来略有不同。

推荐系统

  • 番茄花园Ghost Win7 x64 SP1稳定装机版2022年7月(64位) 高速下载

    番茄花园Ghost Win7 x64 SP1稳定装机版2022年7月(64位) 高速下载

    语言:中文版系统大小:3.91GB系统类型:Win7

    欢迎使用 番茄花园 Ghost Win7 x64 SP1 2022.07 极速装机版 专业装机版具有更安全、更稳定、更人性化等特点。集成最常用的装机软件,集成最全面的硬件驱动,精心挑选的系统维护工具,加上独有人性化的设计。是电脑城、个人、公司快速装机之首选!拥有此系统

  • 番茄花园 Windows 10 极速企业版 版本1903 2022年7月(32位) ISO镜像快速下载

    番茄花园 Windows 10 极速企业版 版本1903 2022年7月(32位) ISO镜像快速下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    番茄花园 Windows 10 32位极速企业版 v2022年7月 一、系统主要特点: 使用微软Win10正式发布的企业TLSB版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过程中自动激活

  • 新萝卜家园电脑城专用系统 Windows10 x86 企业版 版本1507 2022年7月(32位) ISO镜像高速下载

    新萝卜家园电脑城专用系统 Windows10 x86 企业版 版本1507 2022年7月(32位) ISO镜像高速下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    新萝卜家园电脑城专用系统 Windows10 x86企业版 2022年7月 一、系统主要特点: 使用微软Win10正式发布的企业TLSB版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过程

  • 笔记本&台式机专用系统 Windows10 企业版 版本1903 2022年7月(32位) ISO镜像快速下载

    笔记本&台式机专用系统 Windows10 企业版 版本1903 2022年7月(32位) ISO镜像快速下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    笔记本台式机专用系统 Windows 10 32位企业版 v2022年7月 一、系统主要特点: 使用微软Win10正式发布的企业TLSB版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过

  • 笔记本&台式机专用系统 Windows10 企业版 版本1903 2022年7月(64位) 提供下载

    笔记本&台式机专用系统 Windows10 企业版 版本1903 2022年7月(64位) 提供下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    笔记本台式机专用系统 Windows10 64专业版 v2022年7月 一、系统主要特点: 使用微软Win10正式发布的专业版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过程中自动

  • 雨林木风 Windows10 x64 企业装机版 版本1903 2022年7月(64位) ISO镜像高速下载

    雨林木风 Windows10 x64 企业装机版 版本1903 2022年7月(64位) ISO镜像高速下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    新雨林木风 Windows10 x64 企业装机版 2022年7月 一、系统主要特点: 使用微软Win10正式发布的企业TLSB版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过程中自动激活

  • 深度技术 Windows 10 x64 企业版 电脑城装机版 版本1903 2022年7月(64位) 高速下载

    深度技术 Windows 10 x64 企业版 电脑城装机版 版本1903 2022年7月(64位) 高速下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    深度技术 Windows 10 x64 企业TLSB 电脑城装机版2022年7月 一、系统主要特点: 使用微软Win10正式发布的企业TLSB版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过程

  • 电脑公司 装机专用系统Windows10 x64 企业版2022年7月(64位) ISO镜像高速下载

    电脑公司 装机专用系统Windows10 x64 企业版2022年7月(64位) ISO镜像高速下载

    语言:中文版系统大小:3.98GB系统类型:Win10

    电脑公司 装机专用系统 Windows10 x64 企业TLSB版2022年7月一、系统主要特点: 使用微软Win10正式发布的企业TLSB版制作; 安装过程全自动无人值守,无需输入序列号,全自动设置; 安装完成后使用Administrator账户直接登录系统,无需手动设置账号。 安装过