手动添加导入表修改EXE功能
目标:
改动PE导入表,手工给HelloWorld增加一个功能,就是启动的时候写入一条开机启动项,C:\cmd0000000000000000000000000000.exe
实现方法:
直接在注册相关自启动里面添加一个开启启动文件路径就行了。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
NewValue C:\cmd0000000000000000000000000000.exe
代码逻辑相关:
RegCreateA()
RegSetValueExA()
RegClose()
对应汇编代码:
0001 68xxxxxxxx Push addr @hKey
0002 68xxxxxxxx Push addr szKey
0003 68xxxxxxxx Push HKEY_LOCAL_MACHINE
0004 E8xxxxxxxx Call RegCreateA
0005 6A27 Push 39
0006 68xxxxxxxx Push addr lpszValue
0007 6A01 Push REG_SZ
0008 6A00 Push NULL
0009 68xxxxxxxx Push addr lpszValueName
000A FF35 xxxxxxxx Push @hKey
000B E8xxxxxxxx Call RegSetValueExA
000C FF35 xxxxxxxx Push @hKey
000D E8xxxxxxxx Call RegClose
.
.
.
000E FF25xxxxxxxx JMP DWORD PTR [xxxxxxxx]
000F FF25xxxxxxxx JMP DWORD PTR [xxxxxxxx]
0010 FF25xxxxxxxx JMP DWORD PTR [xxxxxxxx]
细化指令:
(1)64xxxxxxxx push addr @hKey
如果数据在.data段中,而传递的是地址,比如addr @hKey,则push的指令码为64h,后面紧跟着8位的地址,该地址只是了数据所在位置的VA.。即@hKey所在的VA。关于这个值得计算方法如下:
获得程序默认装载地址=0x00400000
获得数据段.data的起始RVA=0x03000
假设数据段的安排如下(注意路径不是***cmd000000.....exe,但是长度一样)
对应字节码为:
根据数据段各变量的位置,从上述所列字节码中找到@hKey所在的位置为0x00403069,所以第一条指令字节码是68 69 30 40 00。
(2)68xxxxxxxx push addr sz1
采用同样的方式 68 0B 30 40 00
(3)68xxxxxxxx push HKEY_LOCAL_MACHINE
因为HKEY_LOCAL_MACHINE是一个双字节常亮,所以压栈的字节码为68h,其后是该常量的值。
So 68 02 00 00 80
(4)E8xxxxxxxx call RgeCreateKeyA
由于这是一个段内调用,所以call的指令字节码为E8,其后跟随的则是距离真正跳转指令的偏移。偏移这么计算:
首先确定HelloWorld.exe的指令长度为24h。
其次,他要跳转过自身后面的所有指令,如下所示:
这些指令加起来长度为26h,两者相加即为call到jmp的偏移量4Ah;所以第四条指令的字节码是 E8 4A 00 00 00。
(5) 6A27 PUSH 27H
如果往栈里推入的双子可以控制在一个字节内,那么需要使用6A指令。后面直接跟一个字节的数值。
根据上面的分析方法,不难得出6-9条指令的字节码依次为:
68 42 30 40 00
6A 01
6A 00
68 39 30 40 00。
(6)FF35xxxxxxxx push @hKey
当要讲数据段制定位置的双字压入栈时,需要使用指令FF35,其跟着制定位置的VA。其实该指令可以解释为: push dword prt ds:[xxxxxxxx]。
所以第十条指令字节码是FF 35 69 30 40 00。
(7)E8xxxxxxxx call RegSetValueExA
同第四条指令,该跳转地址紧跟在第四条指令跳转的后面。现在计算操作数,长度由一下部分组成(call 的话 是直接call的距离,不是直接call VA):
1)该指令下的其他指令如下,总计0Bh个字节
2)原Helloworld.exe指令,总计24个字节。
3)第四条跳转指令FF25xxxxxxxx,总共6h个字节
以上长度加起来后结果是35h。所以第是一条指令字节码为E8 35 00 00 00。
(8) FF35 xxxxxxxx Push @hKey
第十二条指令字节码是 FF 35 69 30 40 00。
(9)E8xxxxxxxx Call RegClose
同第四条指令,该跳转地址紧跟在是一条指令后,其长度的计算包含以下几个部分:
其后再无添加代码(0h)
原Helloworld指令(24h)
第四条指令的跳转指令FF25xxxxxxxx(6h)
第是一条指令的跳转指令FF25xxxxxxxx(6h)
以上长度加起来后的结果是30h。则第十三条指令字节为E8 30 00 00 00。
(10)跳转指令
跳转指令为FF25,后面跟着要跳转到的VA地址。从前面对导入表的分析看,这三个函数地址是IAT的前三个,也就是最后一个非零IMAGE_IMAPORT_DESCRIPTOR结构中字段FirstThunk只想的位置。其地址依次为00402000 00402004 00402008,所以,最后三个跳转指令依次为:(这个地方看不懂的话就去看下之前总结的导入表那章)
FF 25 00 20 40 00
FF 25 04 20 40 00
FF 25 08 20 40 00
综上所述,新增加代码的字节码如下:
与原Helloworld的字节码相比,代码部分增加了4Ch大小。指令字节码构造完成后,接下来的任务是分析此次修改导致的PE头部的变化。
PE头部变化
1.IMAGE_SECTION_HEADER(.data).VirtualSize
新增加的常量都定义在源码的数据段主要包括以下内容:
(上面路径我改成c下的cmdaaaa...aa.exe了)
共98个字节,所以.data接的尺寸要增加64h。
2.IMAGE_DATA_DIRECORY[IAT].size
由于新增加了一个IMAGE_IMPORT_DESCRIPTOR,所以在IAT中要加入新增加的三个入口函数地址;同时,增加一个结尾的0,共四个双字,16个字节,所以IMAGE_DATA_DIRECTOY[IAT].isize要多加10h。
3.IMAGE_DATA_DIRECOTRY[IT].VirtualAddress
由于导入表的起始地址紧跟在IAT后,IAT多增加的字节数也是导入表起始位置偏移的数量,所以该字段要后移16个字节,即IMAGE_DATA_DIRECORY[IT].VirtualAddress需要多加10h。
4.IMAGE_SECTION_HEADER(.rdata).VirtualSize
1)增加一个动态链接库,就增加一个IMAGE_IMPORT_DESCRIPTOR结构,大小14h。
2)OriginalFiresThunk部分,3个新增注册表操作函数名指针和一个0结尾的指针共10h。
3)“编号/名称”部分包含以下定义:
xxRegCreateKeyA\0xxRegSetValueExA\0xxRegCkiseKey\0advapi32.dll\0\0共61个字节(3Dh)。
4)FirstThrunk部分,3个新增注册表函数名指针和一个0结尾的指针共10h。
综上所述,IMAGE_SECTION_HEADER(.rdata).VirtualSize的值需要多加71h。
5)IMAGE_DATA_DIRECTORY[IT].isize
多了一个动态库,就多出一个IMAGE_IMPORT_DESCRIPTOR结构,该结构大小为14h。即IMAGE_DATA_DIRECTORY[IT].isize多增加14h。
6.IMAGE_SECTION_HEADER(.text).VritualSize
代码段前面已经说过,增加4Ch大小。
由于插入代码而使得PE头部发生变化如下:
上面按顺序依次是:
导入表地址 [IT].VirtualAddress
导入表大小 [IT].isize
数据目录数 [IAT].isize
代码段大小 .text
增加的一些导入表相关,就是上面4说的那些,只读数据 .rdata
数据段大小 .data
手工重组
完成了指令字节码构造,明确了PE头部需要更改的字段,接下来就是手工重组部分。由于新的代码和数据与原来的代码和数据加在一起没有超出文件对其要求得粒度,所以DOS头、PE头的大部分字段不需要修改。以下是手动修改过程:
1.数据段
首先,添加程序中用到的数据。定位到HelloWorld.exe文件的800h处,从第一个字节为0的地方复制以下数据:
为什么从800h开始,因为800h是数据段:
然后修改数据段大小:
到200h的地方找到0B改成6Dh
此时的exe还是可以正常运行的。
2.代码段
找到代码段在400h处:
原来是只有这些:
然后在代码段的开始部分加入我们新增的代码,就是最上面分析的那些代码:
之后再修改
橙色是新增功能代码段,黑色是源代码段,黑色原代码段中红色部分是修改部分,因为新产生的IA被安排在IAR的头部,所以其他的调用地址必须往后调整。在程序中,一共增加了三个函数(来自同一个动态链接库),所以按照导入表的要求,在IAT表头需要增加三个函数入口地址(12个字节)和一个全0(4字节)的地址,这样,原来的入口函数地址都要往后调整10h个字节,所以FF25 JMP的IAT都向后偏移10h。
然后把.text代码段长度修改下:
找到代码段长度位置:
到1B0h处把24h改为70h:
改之后代码段长度显示也就变了:
此时的程序无法运行,因为.rdata处还没改,导入表部分还没有改。上面直接偏移了两个原来函数JMP地址。那个地址是什么还不知道呢!所以目前无法运行。
3. .rdata段
这个地方相对较难,要细心些:
首先看下原始导入表数据字节码:
去FOA=600h地方去找:
原来的样子:
修改后的内容如下(带下划线为待定部分,一会会重新计算)
新增的内容,三个函数加一个0位,是4*4
之前有两个函数,Message,和ExitProcess分别只有一个函数,每个又有一个0位,所以是
2*4+2*4最后一共20h
共涉及到3个Dll加上一个0位,20*3+20=50h
对应IAT,3个函数(3+1)*4 一个函数(1+1)*4 所有的一共4*4+2*4+2*4=20h
以上列出了导入表的四个组成部分,只要知道了代码中调用函数的个数以及链接库的个数,INT、导入表项、IAT三个大部分的大小就是固定的了。实际上,导入表的重组只需要先将第4部分也就是函数编号、函数名和动态库这一部分根据编码设置好,剩下的其他部分的数据就可以按照数据结构自行构造了。
第三个导入表项IMAGE_IMPORT_DESCRIPTOR的OriginalFirstChunk值为00002070h,他是一个RVA,转换为文件偏移地址为0x00000670、FirstChunk的值为00002000h,也是一个RVA,对应的FOA为0x00000600。
文件中IAT和INT维护了两份值相同的双字数组。
000020C6H 来源于表4-2中函数RegCreateKeyA的VA地址。由于是RVA所以要减去基地址0x00400000h。另外两个双字用同样的方法填充。构造好的第三部分代码如下:
4.修改与数据目录表项和节表想相关字段
上面说过了:
5.运行测试:
开机启动项的注册表是加上了,但是没有之前的Messagebox弹窗了。
结果往后看,书中是给了解释的(一开始第一遍做没注意,以为做错了)。
修改之后:
这个地方别改错了,书上没有告诉具体细节,我改了好几次最后找到了,JMP顺序
402018,402010,402000,402004,402008改成402010,402018,402000,402004,402008
OK就这些,所有的都搞定了。这个写了好几天,今天又是一点多了。终于整完了,明天还要上班。酱紫。
手动添加导入表修改EXE功能的更多相关文章
- (菜鸟要飞系列)五,基于Asp.Net MVC5的后台管理系统(添加数据表的分页功能)
献上代码 ) { List<UserModel> arrayUserModel = new List<UserModel>(); string strText = Reques ...
- Angular实现动态添加删除表单输入框功能
<div class="form-group form-group-sm" *ngFor="let i of login"> <label c ...
- C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序
C#中缓存的使用 缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可: <%@ Outp ...
- 如何手动添加Windows服务和如何把一个服务删除
windows 手动添加服务方法一:修改注册表 在注册表编辑器,展开分支"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services" ...
- 注册表的作用、bat文件中REG ADD命令添加注册表项以及bat
注册表的用途与设置 注册表是windows的核心,里面储存着大量的系统信息,说白了就是一个庞大的数据库.如果你不懂什么是数据库,那没关系,不影响你了解注册表,不过最好对数据库有所了解.注册表里面所有的 ...
- windows 手动添加服务
windows 手动添加服务方法一:修改注册表 在注册表编辑器,展开分支"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services" ...
- Windows服务的手动添加和删除方法
Windows服务的手动添加和删除方法 服务,是指执行指定系统功能的程序.例程或进程,以便支持其他程序,尤其是低层(接近硬件)程序.其实,服务就是一种特殊的应用程序,它从服务启动开始就一直处于运行状态 ...
- PE格式第五讲,手工添加节表
PE格式第五讲,手工添加节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 首先我们要用汇编编写一段汇编代码,用来生成 ...
- C/C++ 手工实现IAT导入表注入劫持
DLL注入有多种方式,今天介绍的这一种注入方式是通过修改导入表,增加一项导入DLL以及导入函数,我们知道当程序在被运行起来之前,其导入表中的导入DLL与导入函数会被递归读取加载到目标空间中,我们向导入 ...
随机推荐
- Node.js 模块化你所需要知道的事
一.前言 我们知道,Node.js是基于CommonJS规范进行模块化管理的,模块化是面对复杂的业务场景不可或缺的工具,或许你经常使用它,但却从没有系统的了解过,所以今天我们来聊一聊Node.js模块 ...
- 一个mac软件合集的网站
https://github.com/jaywcjlove/awesome-mac/blob/master/README-zh.md
- linux下redis安装运行教程——redis系列
天没降大任于我,照样苦我心智,劳我筋骨. 安装运行的过程 由于官网太慢,csdn里的资源又要钱,所以呢,只能使用我自己本地以前下载的陈年..哦不,3.xredis安装包 资源已经放到百度云,需要的可以 ...
- Jmeter +Jenkins +Ant 集成发送邮件报告
[TOC] 一.什么是接口测试? 接口测试是测试系统组件间接口的一种测试.接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点.测试的重点是要检查数据的交换,传递和控制管理过程,以及系 ...
- Bits.java
package java.io; /** * Utility methods for packing/unpacking primitive values in/out of byte arrays ...
- javascript 最权威的知识点总结
JavaScript中如何检测一个变量是一个String类型?请写出函数实现typeof(obj) === "string"typeof obj === "string& ...
- 92反转链表II
# Definition for singly-linked list.# 这道题还是有点复杂的,但是是有套路的,套用反转链表的想法class ListNode: def __init__(self, ...
- Prometheus时序数据库-报警的计算
Prometheus时序数据库-报警的计算 在前面的文章中,笔者详细的阐述了Prometheus的数据插入存储查询等过程.但作为一个监控神器,报警计算功能是必不可少的.自然的Prometheus也提供 ...
- [GDKOI2021] 提高组 Day 1 总结
[ G D K O I 2021 ] 提 高 组 D a y 1 总 结 [GDKOI2021]~~ 提高组~~ Day~1~~ 总结 [GDKOI2021] 提高组 Day ...
- 201871030103-陈荟茹 实验二 个人项目―《D{0-1}KP问题》项目报告
项目 内容 课程班级博客链接 班级博客链接 这个作业要求链接 作业要求链接 我的课程学习目标 1.理解掌握软件设计的过程中的各个环节2.掌握github的使用,将自己的项目上传至githu中 这个作业 ...