反射式dll注入「建议收藏」

反射式dll注入「建议收藏」前不久实现了经典dll注入exe,实现注入到用户层面进程,比如notepad.exe和wps.exe。(像金山毒霸kxetray.exe有自我保护机制也注入不进去)。由于对会话隔离注入不太了解,怕搞坏主机,没有再去实现注入到系统进程中。最近在网上看到dll反射注入,想把学习总结的笔记给大家分享一下。首先什么是反射式注入?它和传统经典注入有什么区别呢?我这里作个比喻。经典式:在别人的内存里调用…

大家好,又见面了,我是你们的朋友全栈君。

  前不久实现了经典dll注入exe,实现注入到用户层面进程,比如notepad.exe和wps.exe。(像金山毒霸kxetray.exe有自我保护机制也注入不进去)。特此记录一下。
  首先什么是反射式注入?它和传统经典注入有什么区别呢?我这里作个比喻。

经典式
  在目标进程申请一段空间并放入恶意dll,创建远线程让目标进程调用LoadLiabrary函数加载这段内存
  先把病毒扔进邻居家,告诉邻居这是个炮仗(病毒dll),邻居误以为真,点燃导火索(创建线程执行),显然邻居被骗了
反射式:
   在别人的内存里调用自己编写的dll导出函数 ,自己dll导出函数里实现自我加载(加载PE的整个过程)
   先把病毒扔进邻居家,然后自己把它直接点燃(调用dll导出函数),病毒自己爆炸(dll内部实现自己从底层dll遍历加载函数),邻居浑然不知(没有通过系统LoadLibrary调用dll,没有记录)

区别是啥
  少了使用LoadLibrary的过程。

反射式注入dll的优点

   反射式注入方式并没有通过LoadLibrary等API来完成DLL的装载,DLL并没有在操作系统中”注册”自己的存在,因此ProcessExplorer等软件也无法检测出进程加载了该DLL。利用解密磁盘上加密的文件、网络传输等方式避免文件落地,DLL文件可以不一定是本地文件,可来自网络等,总之将数据写到缓冲区即可。
   由于它没有通过系统API对DLL进行装载,操作系统无从得知被注入进程装载了该DLL,所以检测软件也无法检测它。同时,由于操作流程和一般的注入方式不同,反射式DLL注入被安全软件拦截的概率也会比一般的注入方式低。

实现需求

一、注射器
  (将这个DLL文件写入目标进程的虚拟空间中)实现大部分和传统注入一样。但它要做一件很重要的事情,就是获得病毒dll里的一个最重要的导出函数,也即是我们要编写的反射加载自己的函数ReflectiveLoader的文件偏移。(如果你知道自己dll这个导出函数文件偏移多少其实也没必要再用程序读取)

二、编写一个邪恶的dll以及它的核心函数ReflectiveLoader
  要知道ReflectiveLoader函数运行时所在的DLL还没有被装载,它在运行时会受到诸多的限制,例如无法正常使用全局变量等。而且,由于我们无法确认我们究竟将DLL文件写到目标进程哪一处虚拟空间上,所以我们编写的ReflectiveLoader必须是地址无关的。

注射器实现

  1. 将待注入DLL读入自身内存(可以在磁盘上存放一份DLL的加密后的版本,然后将其解密之后储存在内存里)
  2. 利用VirtualAlloc和WriteProcessMemory在目标进程中写入待注入的DLL文件
  3.利用CreateRemoteThread等函数启动位于目标进程中的ReflectiveLoader(要首先获得ReflectiveLoader的文件偏移地址),获取的是DLL中反射加载函数在文件中的偏移,由于这种注入没有使用LoadLibraryA函数,所以DLL在内存中的状态和在磁盘中的状态是相同的,要想调用函数,我们就需要找到文件偏移。
  线程函数的地址=缓冲区的基地址+文件偏移

在这里插入图片描述

在这里插入图片描述
图中1264十六进制就是4F0

  4.通过DLL的导出表找到这个ReflectiveLoader并调用它
   将自身合适地展开到虚拟空间中。我们都知道在PE文件包含了许多节,而为了节省存储空间,这些节在PE文件中比较紧密地凑在一起的。而在广阔虚拟空间中,这些节就可以映射到更大的空间中去。(更不用说还存在着.bss这样的在PE文件中不占空间,而要在虚拟空间中占据位置的节)

ReflectiveLoader函数实现

实现中的问题:

  DLL中可能会用到其他DLL的函数,装载一个DLL还需要将这个DLL依赖的其他动态库装入内存,并修改DLL的IAT指向到合适的位置,这样对其他DLL函数的引用才能正确运作ReflectiveLoader的代码是地址无关的,但是该DLL的其他部分的代码却并不是这样的。在一份源代码编译、链接成为DLL时,编译器都是假设该DLL会加载到一个固定的位置,生成的代码也是基于这一个假设。在反射式注入DLL的时候,我们不太可能申请到这个预先设定好的地址,所以我们需要面对一个重定位(Rebasing)的问题。

解决方案

  1.ReflectiveLoader做的第一件事就是查找自身所在的DLL具体被写入了哪个位置
ULONG_PTR caller( VOID ) { return(ULONG_PTR)_ReturnAddress(); }
借助上文找到的地址,我们逐字节的向上遍历,当查找到符合PE格式的文件头之后,就可以认为找到了DLL文件在内存中的地址了。
Caller()函数:获得当前指令的下条指令的地址。

  2.我们需要的函数是kernel32.dll中的LoadLibraryA(), GetProcAddress(),VirtualAlloc()以及ntdll.dll中的NtFlushInstructionCache()函数。我们需要遍历已经加载的模块,从中找到我们需要的模块,获得以上几个函数的地址。
#define KERNEL32DLL_HASH 0x6A4ABC5B
#define NTDLLDLL_HASH 0x3CFA685D
#define LOADLIBRARYA_HASH 0xEC0E4E8E
#define GETPROCADDRESS_HASH 0x7C0DFCAA
#define VIRTUALALLOC_HASH 0x91AFCA54
#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8
// compute the hash values for this function name
dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
每一个线程都具有一个TEB结构,记录了相关线程的一些基本信息。线程运行时,其FS段寄存器记录了其TEB的位置。
获取PEB的方法:FS:[0x30]和GS:[0x60],前者为32位系统,后者为64位系统。
这里需要用到之前TEB定位与导出函数定位的知识。

  3. 分配一片用来装载DLL的空间。
虽然在ReflectiveLoader运行时,DLL文件已经在进程内存中了,但是要装载这个DLL,我们还需要更大的空间。借助在第2)步得到的函数VirtualAlloc(),我们可以分配一片更大的内存空间用于加载DLL。在PE头中的IMAGE_OPTIONAL_HEADER结构体中的SizeOfImage成员记载DLL被装载后的大小,我们按照这个大小分配内存即可。
uiBaseAddress=(ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage,
MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE );
uiBaseAddress记录了VirtualAlloc的返回值,也就是分配内存空间的起始地址。于是uiBaseAddress就成为了DLL被装载后的基地址。

  4.复制PE文件头和各个节
分配了用于装载的空间后,ReflectiveLoader将DLL文件的头部(也就是DOS文件头、DOS插桩代码和PE文件头)复制到新的空间的首部。再根据PE文件的节表将各个节复制到相应的位置中。

  5.根据节表加载节

  6. 处理DLL的导入表
被注入的DLL可能还依赖于其他的DLL,因此我们还需要装载这些被依赖的DLL,并修改本DLL的引入表,使这些被引入的函数能正常运行。
无论是以什么方式导入,我们都要需要找到对应的函数,然后将其地址填入FirstThunk指向的IMAGE_THUNK_DATA数组中。这个跑起来是IAT

  7.对DLL进行重定位
  由于基址改变,所以程序中的一些直接寻址等会出问题,所以要更改重定向表。
  被注入的DLL只有其ReflectiveLoader中的代码是故意写成地址无关、不需要重定位的,其他部分的代码则需要经过重定位才能正确运行。
  我们首先计算得到基地址的偏移量,也就是实际的DLL加载地址减去DLL的推荐加载地址。DLL推荐加载地址保存在NT可选印象头中的ImageBase成员中,而实际DLL加载地址则是我们在第3)步中函数VirtualAlloc()的返回值。然后我们将VirtualAddress和Typeoffset合力组成的地址所指向的双字加上这个偏移量,重定位就完成了。
(DWORD)(VirtualAddress + Typeoffset的低12位) += (实际DLL加载地址 – 推荐DLL加载地址)
  在完成所有的重定位后,我们最后调用第2)步得到的NtFlushInstructionCache()清除指令缓存以避免问题。
8. 调用DLL入口点
  至此,ReflectiveLoader的任务全部完成,最后它将控制权转交给DLL文件的入口点,这个入口点可以通过NT可选印象头中的AddressOfEntryPoint找到。一般地,它会完成C运行库的初始化,执行一系列安全检查并调用dllmain。

用到的底层C++语言编程

  dll遍历kernel32与ntdll的导出函数部分用汇编语言更加直接简短,这部分代码同样是大部分加壳程序的壳内核心代码,有时间一定会细心研究一下。
  _rotr函数,功能是value右循环移动shift位( unsigned int value, int shift );
  register 关键字请求“编译器”将局部变量存储于寄存器中——最快的关键字
  register 这个关键字请求编译器尽可能的将变量存在CPU内部寄存器,而不是通过内存寻址访问,以提高效率。注意是尽可能,不是绝对。
  数据从内存里拿出来先放到寄存器,然后CPU 再从寄存器里读取数据来处理,处理完后同样把数据通过寄存器存放到内存里,CPU 不直接和内存打交道。
内存 寄存器 CPU
  寄存器其实就是一块一块小的存储空间,只不过其存取速度要比内存快得多。CPU从寄存器里拿数据比在大内存里去寻找某个地址上的数据快多了。
  __readgsqword(0x60) 从GS寄存器里读取数值

查杀建议

  回想我们注射器实现的过程中所调用的函数,与正常的注入似乎没有太大的区别,像CreateRemoteProcess这种危险函数杀软抓的很严,可以被替换掉。整个过程没有发现LoadLibraryA函数。但这个样本有明显的特征:解析PE结构,所以当我们遇到这种样本的时候,可以考虑为反射式DLL注入。
  用IDA逆向程序时,解析PE结构的过程,编程中会出现很多+0xnumber 这里number代表十六进制数字。
  在磁盘发现某dll很可疑,如果该dll被注入到进程中已经在运行,删除或移动dll时会弹窗正在某某程序中使用,可以看到它是不是注入的dll。
  另外注入到进程的恶意Dll程序执行错误时崩溃并不会导致被注入程序实际的崩溃。

参考

来源https://bbs.pediy.com/thread-224241.htm

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145399.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • springboot的启动流程及原理_精馏的原理及流程

    springboot的启动流程及原理_精馏的原理及流程1.springboot的启动类入口@SpringBootApplication@ComponentScan(basePackages={“cn”})publicclassSpringBootDemo{publicstaticvoidmain(String[]args){SpringApplication.run(SpringBootDemo.class);}}可以看出,Annotation定义(@SpringBootApplicati

  • HTML5 新特性_CSS3新特性

    HTML5 新特性_CSS3新特性一.HTML5概念:1.什么是HTML5:(1)HTML5将成为HTML、XHTML以及HTMLDOM的新标准;(2)HTML5仍处于完善之中。然而,大部分现代浏览器已经具备了某些HTML5支持。2.HTML5的起步:(1)HTML5是W3C(WorldWideWebConsortium,万维网联盟)与WHATWG合作的结果(2)为HTML5建立的…

    2022年10月31日
  • Spring Cloud Security 整合 OAuth 2.0,从原理到实战一次说明白

    Spring Cloud Security 整合 OAuth 2.0,从原理到实战一次说明白若有收获,请记得分享和转发哦本篇文章介绍一下OAuth2.0相关的知识点,并且手把手带大家搭建一个认证授权中心、资源服务进行OAuth2.0四种授权模式的验证,案例源码详细,一梭子带大家了…

  • linux卸载javajdk_tomcat拒绝连接

    linux卸载javajdk_tomcat拒绝连接–卸载jdk–查看jdk安装包名称rpm-qa|grepjdk–卸载rpm-e`rpm-qa|grepjdk`(或rpm-e加上面rpm-qa|grepjdk显示的结果)–注释或删除环境变量vi/etc/profile#exportJAVA_HOME=/usr/java/jdk1.6.0_20#exportCLASSPATH=.;$…

  • java输出语句_java输入输出语句是什么

    java输出语句_java输入输出语句是什么在java中,输入语句为“Scanner对象.next()系列方法”,例“Scanner对象.nextLine()”表示输入字符串;输出语句为“System.out.println()”、“System.out.print()”等。对于经常上机刷题的来说,首先得解决输入输出方法,Java的输入输出流在Java学习过程的后面部分才会接触,但是我们可以掌握一些简单的,常用的输入输出方法输出流java常…

  • 【C/C++】C语言特性总结

    【C/C++】C语言特性总结已经有大约半年的时间没有碰C语言了,当时学习的时候记录了很多的笔记,但是都是特别混乱,后悔那个时候,不懂得写博客,这里凭借记忆和零零散散的笔记记录,尝试系统性地复习一下C语言。之前都是在Windows环境下学习,这次把重心放在Linux环境下,这次的复习源于基础,但是要高于基础。文章目录工具gcc编译器VS2019C语言编译过程C语言代码主体必要内容C语言数据类型关键字常量变量进制表示s…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号