目标:

改动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功能的更多相关文章

  1. (菜鸟要飞系列)五,基于Asp.Net MVC5的后台管理系统(添加数据表的分页功能)

    献上代码 ) { List<UserModel> arrayUserModel = new List<UserModel>(); string strText = Reques ...

  2. Angular实现动态添加删除表单输入框功能

    <div class="form-group form-group-sm" *ngFor="let i of login"> <label c ...

  3. 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 ...

  4. 如何手动添加Windows服务和如何把一个服务删除

    windows 手动添加服务方法一:修改注册表 在注册表编辑器,展开分支"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services" ...

  5. 注册表的作用、bat文件中REG ADD命令添加注册表项以及bat

    注册表的用途与设置 注册表是windows的核心,里面储存着大量的系统信息,说白了就是一个庞大的数据库.如果你不懂什么是数据库,那没关系,不影响你了解注册表,不过最好对数据库有所了解.注册表里面所有的 ...

  6. windows 手动添加服务

    windows 手动添加服务方法一:修改注册表 在注册表编辑器,展开分支"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services" ...

  7. Windows服务的手动添加和删除方法

    Windows服务的手动添加和删除方法 服务,是指执行指定系统功能的程序.例程或进程,以便支持其他程序,尤其是低层(接近硬件)程序.其实,服务就是一种特殊的应用程序,它从服务启动开始就一直处于运行状态 ...

  8. PE格式第五讲,手工添加节表

    PE格式第五讲,手工添加节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 首先我们要用汇编编写一段汇编代码,用来生成 ...

  9. C/C++ 手工实现IAT导入表注入劫持

    DLL注入有多种方式,今天介绍的这一种注入方式是通过修改导入表,增加一项导入DLL以及导入函数,我们知道当程序在被运行起来之前,其导入表中的导入DLL与导入函数会被递归读取加载到目标空间中,我们向导入 ...

随机推荐

  1. HDU_3071 Gcd & Lcm game 【素数分解 + 线段树 + 状压】

    一.题目  Gcd & Lcm game 二.分析 非常好的一题. 首先考虑比较暴力的做法,肯定要按区间进行处理,对于$lcm$和$gcd$可以用标准的公式进行求,但是求$lcm$的时候是肯定 ...

  2. Bi-shoe and Phi-shoe LightOJ - 1370(数论+素数筛)

    题目链接:https://vjudge.net/problem/LightOJ-1370 题意:给你N个欧拉函数值,找出每一个大于等于该欧拉函数值的数,并且要求相加和最小. 题解:因为素数i的欧拉函数 ...

  3. 《C++反汇编与逆向分析技术揭秘》--认识启动函数,找到用户入口

    <C++反汇编与逆向分析>和<程序员的自我修养>都是以VC6的代码作为例子讲解的.这里是在vs2017下,CRT代码有些区别,但整体流程上都是初始化环境,设置参数,最后转到用户 ...

  4. python3 多线程爬虫模板

    原文:https://www.jianshu.com/p/06ae2373f560 1 import threading # 多线程模块 2 import queue # 队列模块 3 import ...

  5. for what? while 与 until 差在哪?-- Shell十三问<第十三问>

    for what? while 与 until 差在哪?-- Shell十三问<第十三问> 最后要介绍的是 shell script 设计中常见的"循环"(loop). ...

  6. 不想eject,还咋修改create-react-app的配置?

    一.先抛问题 许多刚开始接触create-react-app框架的同学,不免都会有个疑问:如何在不执行eject操作的同时,修改create-react-app的配置.今天胡哥就来带大家一起来看看这个 ...

  7. 【Git基本命令】

    [基本指令] git init :使目标文件夹变成一个仓库 git add <文件名,含后缀> : 告诉git我要添加文件了 git commit -m "<提交说明> ...

  8. 五、python学习-面向对象

    1.面对对象程序开发基础(oop) 面对对象:高内聚 低耦合 面向过程: 优点:效率高,执行速度快 缺点:维护性,移植性差,表达不出一类的语义 面向对象: 优点:可读性,可移植性,可维护性高 缺点:执 ...

  9. 破解class文件的第一步:深入理解JAVA Class文件

    摘要: java定义了一套与操作系统,硬件无关的字节码格式,这个字节码就是用java class文件来表示的,java class文件内部定义了虚拟机可以识别的字节码格式,这个格式是平台无关性的. j ...

  10. Day12_62_线程的生命周期

    线程的生命周期 要实现多线程,必须在主线程中创建新的线程对象. 任何线程一般都具有五种状态,即创建,就绪,运行,阻塞,终止(消亡) 新建状态:在程序中创建了一个新的线程对象后,新的线程对象便处于新建状 ...