反射式dll注入_反射式注入-程序员宅基地

技术标签: dll注入  反射式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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43312649/article/details/100094106

智能推荐

游戏中任务系统设计_游戏任务种类-程序员宅基地

文章浏览阅读8.5k次,点赞2次,收藏17次。1、任务分类 游戏中任务可以分为主线任务、支线任务、日常任务、周任务、节日活动任务,其中周任务和节日任务可以根据设定的日期进行开启关闭,属于重复性任务。2、游戏条件 各类任务开启条件:1、玩家等级 2、玩家攻击力3、前置任务等等 完成条件:玩家进行任务进度是否达成 任务关闭:1、玩家领取奖励后关闭任务。2、时间过期3..._游戏任务种类

ACM / ICPC 在线OJ(Online judge)_清华大学 acm网站-程序员宅基地

文章浏览阅读6.4k次,点赞8次,收藏35次。1. codeforces codeforces(这个网站每天会有比赛,一起打CF吧!)http://codeforces.com/problemset2. topcoder:http://www.topcoder.com/challenges/3. kattis 这个网站有历年的ICPC真题 https://open.kattis.com/4. 洛谷:..._清华大学 acm网站

基于语法-CreateFile_::createfilea中文打不开-程序员宅基地

文章浏览阅读599次。可行1:当E:\\a.txt 已经存在,且程序字符=Unicode的时候,下面代码可以正常#include <iostream>#include <windows.h>int main(){ HANDLE hFILE = (HANDLE)CreateFile(L"E:\\a.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (hFILE == INVALID__::createfilea中文打不开

数据库的四种安装部署方式_数据库部署-程序员宅基地

文章浏览阅读6.6k次,点赞2次,收藏13次。数据库安装文章目录数据库安装@[toc]实验环境方式1:使用MySQL仓库来安装部署MySQL一、添加MySQL仓库二、禁用默认的MySQL模块三、安装装MySQL四、启动MySQL服务器五、服务确认方式2:离线安装MySQL一、使用以下执行清理之前实验MySQL仓库安装的数据库:二、网上下载mysql的安装包三、将下载好的文件传入Redhat中的某个路径中四、解压安装包五、使用以下指令安装方式3:使用Docker部署MySQL一、准备Docker环境二、下载MySQL服务器Docker映像三、启动M._数据库部署

sqli-labs:less-32(宽字节注入)_sqli labs less32 宽字节注入-程序员宅基地

文章浏览阅读3k次。宽字节注入产生原因:1、mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个汉字(前一个 ascii 码大于 128 才能到汉字的范围)2、mysqli_real_escape_string() 函数转义在 SQL 语句中使用的字符串中的特殊字符。mysqli_real_escape_string(*connection,escapestring*)connection 必需。规定要使用的 MySQL 连接 escapestr..._sqli labs less32 宽字节注入

HTML5 游戏开发快速提升_html5游戏开发 slg游戏-程序员宅基地

文章浏览阅读3.1k次。带你快速掌握html5游戏开发基本功,助你快速进阶游戏开发_html5游戏开发 slg游戏

随便推点

华为电视鸿蒙安装第三方软件,新款华为智慧屏如何安装第三方软件?最详细的安装教程详解...-程序员宅基地

文章浏览阅读5.3k次。12月17日,华为终端官方微博公布了新品信息,预告新款华为智慧屏将于12月21日下午14时的华为全屋智能及智慧屏新品发布会上正式亮相。华为智慧屏宣传海报文案透露出此次新款华为智慧屏将在语音及智能交互上带来惊喜体验。且在此之前,华为消费者业务IoT产品线总裁支浩曾透露发布于年底的全新华为智慧屏系列将定位大众娱乐。另外,随着鸿蒙系统2.0的升级,华为智慧屏系列也正式成为首批搭载鸿蒙2.0的终端产品。根..._华为电视安装第三方软件

JAVA中创建的redis对象_详解Java在redis中进行对象的缓存-程序员宅基地

文章浏览阅读179次。详解Java在redis中进行对象的缓存发布时间:2020-08-30 07:16:04来源:脚本之家阅读:110作者:pj小小码农Java在redis中进行对象的缓存一般有两种方法,这里介绍序列化的方法,个人感觉比较方便,不需要转来转去。一、首先,在存储的对象上实现序列化的接口package com.cy.example.entity.system;import java.util.List;i..._java redis设置person对象

基于深度学习的雷达辐射源识别技术研究-程序员宅基地

文章浏览阅读7.6k次,点赞24次,收藏138次。基于深度学习的雷达辐射源识别技术研究1 摘要时频分析(参考博文)作为处理非平稳信号的有力工具,能够将一维的时域信号映射为时间和频率的联合分布。通过将信号转化为二维时频图像,从图像识别的角度完成辐射源信号的识别。而深度学习模型能够对信号或者图像进行自动的特征提取,省去了人工提取的步骤 ,逐渐成为雷达辐射源信号脉内特征提取的一种新方法。本文应用深度学习中的卷积神经网络来实现雷达辐射源信号的识别,主要工作如下:在研究信号时频分析的基础上,提出了基于时频图像和卷积神经网络的雷达辐射源信号识别方法 。该方法首_辐射源识别

org.springframework.web.client.HttpServerErrorException: 500 null_"{\"code\":500,\"data\":null,\"success\":false,\"m-程序员宅基地

文章浏览阅读2.6w次。跨服务调用api,api本身运行没有错误,也没有其他错误,报menuService 相关关联错误;解决:微服务运行需要关联多个其他微服务,所以要保证相关联的服务都启动了。..._"{\"code\":500,\"data\":null,\"success\":false,\"message\":\"org.springframework.web.s"

QT4和QT5信号和槽的区别_qt5和qt4信号槽区别-程序员宅基地

文章浏览阅读5.7k次。信号和槽signals:testSignalOne();testSignalOne(int params);testSignal(int params);piblic slots:testSlotOne();testSlotOne(int params);testSlot(int params);_qt5和qt4信号槽区别

数据包络分析DEA-程序员宅基地

文章浏览阅读57次。测量一些决策部门的生产效率的方法。简单说,现在有三个人,你可以叫他们DMU,a用一个甲生产了一个乙,b用两个甲生产了一个乙,c用三个甲生产了一个乙,显而易见a的效率最高,我们就把他定义为技术前沿面,他的效率为1。那么b就是1/2,c就是1/3。这样我们就可以计算出每个人的效率水平了。

推荐文章

热门文章

相关标签