WINDOWS+PE权威指南读书笔记(26)

目录

EXE 捆绑器

基本思路:

EXE 执行调度机制:

控制进程同步运行实例分析:

字节码转换工具 hex2db:

hex2db 源代码:

运行测试:

执行调度程序 _host.exe:

主要代码:

数据结构分析:

宿主程序 host.exe:

宿主程序的功能:

宿主程序的状态:

遍历文件:

释放文件:

宿主程序主函数:

EXE 捆绑器 bind.exe:

绑定列表定位:

捆绑步骤及主要代码:

测试运行:

小结:


EXE 捆绑器

本书 8.4 节,也就是前面的 "延迟加载导入编程" 目录中介绍了一种通过附加资源文件的方式对文件进行捆绑的例子。本章使用补丁技术编写一个小工具,该工具可以将某个目录中的所有相关文件 (含子目录中的文件) 捆绑在一起,捆绑前还可以定义要运行的 EXE 序列。

EXE 捆绑器允许用户将多个可执行文件及相关文件捆绑到一起,通过运行捆绑程序实现多个可执行文件的依次执行。

基本思路:

EXE 捆绑是指将两个或多个可执行文件和非执行文件捆绑到一起的技术。

捆绑的目的有三个:

口 减少某个待发布系统的独立文件的个数。

口 隐藏某些特殊用途的文件。

口 实现一些特殊功能,如本案例中通过捆绑实现了多个程序的顺序执行。

EXE 捆绑器结合 EXE 加锁器 (本书第 20 章讲述的内容) 可以用于加密文件夹或文件。因为在捆绑过程中,可以指定被捆绑的文件中哪些需要运行,所以,该捆绑器还可以用于运行多个 PE 文件的批处理,通过依次运行多个文件 (结合本书第 19 章讲述的软件安装自动化技术) 则可以实现多个补丁的自动安装。

捆绑的实现方法有很多种,其中最常见有两种方法:

1) 通过编写一个新的 EXE 程序,把要捆绑的所有文件以资源的方式进行,或者直接写在文件末尾来进行,运行时只需将文件提取出来依次运行即可。

2) 通过直接修改第一个 PE 程序,将其他文件作为资源或直接写在文件末尾,通过在第一个 PE 程序中设置补丁程序的方式,依次运行多个程序。

本章将讲解第一种捆绑方法:

以下是本章要用到的几个程序,以及每个程序的相关说明:

口 hex2db:将字节码转变为汇编语言数据定义语句的小工具。

口 _host.exex:调度执行程序的模板,该文件最终嵌入到宿主程序中。

口 hostexe:宿主程序。是补丁程序的目标,用于存储 _host.exe 和其他要捆绑的文件。

口 bind.exe:捆绑器。负责执行捆绑,类似于进阶部分的补丁工具。

EXE 执行调度机制:

大部分情况下,被捆绑的文件集合中只有一个 EXE 文件是主运行文件。但也有例外,比如有的用户会将多个程序捆绑起来,然后依次安装这些程序以提高计算机的安全性能,这时候就要用到 EXE 执行调度。EXE 执行调度就是在捆绑前,指定补丁程序中要安装的程序的运行顺序,当被捆绑的程序被释放出来后,还要有一个进程专门负责调度这些程序按照先后顺序依次运行。

要实现 EXE 同步执行,需要使用两个 Windows API 函数,它们分别是:

口 CreateProcess (创建进程函数)

口 WaitForSingleObject (等待指定进程结束)

控制进程同步运行实例分析:

本节根据对操作系统进程管理的理解,介绍使用 Windows API 函数实现控制进程同步运行的实例,以帮助大家更加深入地理解多个应用程序依次被执行调度的过程。

代码清单 18-1 简单地模拟了捆绑被释放以后,各个程序的同步运行效果:

主程序通过函数 CreateThread 把调度函数 _RunThread 当成一个线程来运行 (行76)。调度函数首先通过 CreateProcess 打开一个程序 (行54) ,然后,调用函数 WaitForSingleObject 等待进程结束 (行 38)。第一个程序结束后,继续使用 CreateProcess 打开第二个程序,使用 WaitForSingleObject 等待第二个程序的结束,依次类推。

执行程序后,首先打开记事本程序,无论你如何操作,均会等待记事本程序退出后 (选择菜单 “文件”|“退出” 选项或者直接选择标题栏最右端的关闭按钮退出记事本),才打开第二个程序 “helloworld.exe”。

字节码转换工具 hex2db:

执行调度的代码会以字节码定义的方式 (使用伪指令 db 语句) 嵌入到源码中。这些字节码可以通过 FlexHex 复制获得,但转换起来特别麻烦。为了方便后续的开发,本节编写一个小工具 hex2db,该工具实现了将文件中的字节码转换为汇编语言字节定义的方式。

例如,文件中的以下字节码:

00 01 02 03 04 A5

利用小工具 hex2db 最终转换为:

db 00h,01h,02h,03h,04h, 0A5h

数据定义该语句包括三部分:

口 前置空格。本例中有四个空格。

口 数据定义伪指令 db。db 和数据之间有一个空格。

口 数据。以逗号分隔,如果某个字节的高八位超过 0ah,则在该字节前添加一个 “0”。

hex2db 源代码:

hex2db 的编写思路与第 2 章的小工具 PEDump 雷同,两者都是以控制输出格式为核心。

hex2db 的源代码见代码清单 18-2:

运行测试:

编译链接执行文件,打开文件 1.txt,查看对 PE 文件 _hostexe 的执行结果(节选) 如下:

注意:

使用小工具 hex2db 生成的数据定义语句外的最后一行末尾有一个逗号,在将结果引入汇编代码时,必须将该过号去掉,否则编译源文件时会出现错误。

执行调度程序 _host.exe:

前面 "EXE 执行调度机制" 目录中通过一个程序模拟了多个应用程序的执行调度过程。接下来就要编写本章通用的执行调度程序 _hostexe。该程序可以对同步运行更多的应用程序,且定义上会更灵活。

主要代码:

主要代码见代码清单 18-3:

与 18.2.2 小节的例子不同,在线程函数 _RunThread 中,要调度的程序不再是固定的某个 PE 文件,而是通过遍历绑定列表数据结构得到的由用户定义的程序。

将 _hostasm 编译,链接以后,生成可执行文件。使用上一节开发的小工具 hex2db.exe,将可执行代码 _host.exe 转换为汇编语言里的字节码定义语句,保存放在 C:\1.txt 中,以便在后面想整体将可执行代码嵌入到汇编源代码时使用。

数据结构分析:

本章开发的 EXE 捆绑器最大能捆绑 100 个文件,由常量 TOTAL_FILE_COUNT 定义。每个捆绑文件都对应一个结构,用来说明文件的名称、所处的位置、是否加入到最终的可执行序列标志等。

该结构的详细定义如下:

函数参数的解释如下:

1) inExeSequence: 标志字节。如果是 0,则表示该捆绑文件是一般文件,不参与释放后的执行调度过程; 如果是 1,则表示该文件为 PE 文件,且参与释放后的执行调度过程。

2) dwFileOff: 该文件字节码在宿主程序中的偏移。指出了文件在宿主程序中的位置。

3) dwFileSize: 文件的大小。

4) name: 要绑定的文件的名字,含子目录。

特别提示:

BinderFileStructname 不是绝对路径,而是当前路径下的相对路径。该路径中包含子目录,可能的表达形式如 pic\background.gif,指当前目录下的 pic 子目录中的 background.gif 图片文件,子目录允许嵌套。(就是相对目录了啦)

宿主程序 host.exe:

下面将开发宿主程序,即 EXE 捆绑器最终生成的携带了捆绑文件的可执行程序。

宿主程序的功能:

宿主程序 host.exe 是 EXE 捆绑器的核心 PE 文件,它具备以下三个功能:

1) 存储所有要捆绑的文件,包括可运行的文件和不可运行的文件,这些文件将通过程序补丁的方式存储到宿主程序的最后一节。

2) 按原目录结构释放所有文件。

3) 具备调度程序执行的功能。该部分功能由释放的 _host.exe 来完成。而 _host.exe 的添加方法则是直接将指令字节码添加到宿主程序的源程序中。

和 _host.exe 程序一样,宿主程序 host.exe 中也定义了一套绑定列表的数据结构。这两个程序维护了同样的数据实例,_host.exe 的绑定列表中每个字段的值都来自于宿主程序。

举个简单例子,如果文件夹中有以下 5 个文件待绑定:

口 A1.exe

口 A2.exe

口 Config.ini

口 dat\abc.dat (注意含子目录)

口 db\abc.mdb (注意含子目录 )

其中 A1、A2 为可执行程序,要求绑定后的程序在运行时先执行 A1,然后执行 A2。

以下数字模拟了宿主程序的绑定列表可能的数据排列方式:(对应的是前面的数据结构)

宿主程序维护了这样的一套数据,用它来释放捆绑文件,嵌入到宿主程序源代码中的 _host.exe 字节码中也维护了这样的一套数据,用它来执行调度。

宿主程序的状态:

由于宿主程序完成了对捆绑文件的存储,所以,在捆绑文件前后不同时期,宿主程序存在不同的状态。

如图 18-1 所示:

如图所示,捆绑前,宿主程序是轻身的,与捆绑文件没有任何的关联; 捆绑后 (执行前),宿主程序已经包含了所有的捆绑文件数据; 执行时,宿主程序会释放捆绑的所有文件。所以,除了宿主程序不发生任何变化外,磁盘上还多出了 _host.exe 和所有绑定的文件。

_host.exe 为进程调度指挥长,释放出的可执行文件就是在它的调度指挥下实现了顺序执行的。该程序包含在捆绑前的宿主程序中。那么,对文件的捆绑工作由谁来完成呢? 答案是由捆绑器来完成。捆绑器类似于 PE 进阶部分的补丁工具。

遍历文件:

为了确认当前目录要捆绑的文件,程序需要首先遍历当前目录下的文件和文件夹,获取当前目录下所有的文件名称,以及每个文件的大小。

遍历文件的代码见代码清单 18-4:

函数 _FindFile 是个递归函数,入口参数是目录名。行6一78是一个循环,通过调用函数 FindNextFile 获得下一个文件或文件夹。行 69 ~ 73 判断 : 得到的如果是一个子文件夹,则继续调用函数 _FindFile 遍历下一个文件夹的内容,如果是文件,则调用函数 _ProcessFile 输出文件名及长度。

释放文件:

文件的释放比较容易。在宿主程序中,根据功能的不同分类,共有两种文件需要释放;

第一种:只有一个文件,它就是进程调度程序 _host.exe。该文件是事先通过将程序字节码 (前面生成的 C\1.txt 文件) 写和到数据段的方法嵌入宿主程序的。

另一种:是捆绑文件,这些文件是通过后面介绍的打补丁方法,把所有相关文件的数据附加到宿主文件的最后一个节来实现的。

1. 释放 _host.exe

由于进程调度程序所有的字节码均写入到了宿主程序的数据段中,数据定义如下:

所以,释放该文件非常容易,只需要将这些字节码原样写回文件即可,代码如下:

2. 释放捆绑文件:

补丁工具在打补丁时,将所有待捆绑的文件的相关信息记录到宿主程序的绑定列表中。这些信息包括: 每个文件在宿主中的偏移、文件大小和释放后所在目录位置及文件名。

所以,释放捆绑文件时,只需根据绑定列表的描述释放每一个文件即可,具体包括以下四步:

步骤1:确定捆绑文件在宿主中的偏移。

步骤2:确定捆绑文件的大小。

步骤3:确定该文件释放以后的绝对路径。

步骤4:执行释放操作。

释放捆绑文件的主要代码见代码清单 18-5:

行 47 一 94 根据绑定列表结构 BinderFileStruct 中定义的值,对每一个文件进行释放处理。因为文件在宿主程序中的起始地址和大小都有记录,文件内容唾手可得。

程序首先根据 BinderFileStructname 一次性完成文件所在目录的创建操作 (如果目录存在嵌套则循环创建名字中所有嵌套的子目录),然后在目录中用结构指定的名称新建文件,并调用函数 writeToFile 将宿主程序中指定位置、指定大小数据写入,从而完成对捆绑文件的释放工作。

宿主程序主函数:

宿主程序的主函数只有三个调用,这三个调用很明晰地展示出该函数的三个主要作用,它们依次是:

口 writeToFile (用以释放 _host.exe 文件)

口 releaseFiles (用以释放捆绑的文件)

口 _RunThread (用以调度执行程序的线程函数,执行的是 _host.exe 文件)

主函数代码如下所示:

EXE 捆绑器 bind.exe:

EXE 捆绑器的主要任务是,将要捆绑的文件附加到宿主程序 host.exe 文件的最后一节。这类似于 PE 进阶部分讲的补丁工具 bind.exe。除了打补丁,捆绑器还要完成对宿主程序 host.exe 中的两套绑定列表数据的修正,以便用于后期的释放捆绑文件和调度运行程序。要完成对补丁列表数据的修正,首先需要完成对绑定列表数据的定位。

绑定列表定位:

绑定列表在 host.exe 和要释放的 _host.exe 中均保留了一份,那么这个位置在宿主程序 host.exe 的哪个偏移处呢? 使用十六进制编辑器 FlexHex 打开 host.exe,查找 .data 中两个 0xFFFFFFFFFFFF 双字的位置(标志变量,仅做标志作用),其后紧跟着的就是绑定列表数据结构。

如下所示:(根目录由 c:\ql 改成 ql,所以位置与原文件不一样)

通过查找可以找到这两套绑定列表数据所在文件的偏移:

口 由 host.exe 维护的第一套绑定列表起始位置: 13a9h

口 由 _host.exe 维护的第二套绑定列表起始位置: 8be2h

最终运行时,绑定列表的数据结构排列看起来类似以下字节码所示:

加框部分是捆绑的文件个数,每个文件都由结构 BinderFileStruct 定义。结构中包含的字段在前面已经进行了介绍,如文件路径、是否可执行、文件长度、所在位置等。

捆绑步骤及主要代码:

捆绑器使用了本书第 17 章介绍的,在 PE 最后一节附加数据的方法;由于不需要调整指令指针的值,所以相对简单。

以下是对捆绑步骤的简单描述:

步骤1:打开宿主程序,将宿主程序映射到内存文件。获取要捆绑目录下所有文件的长度,并按照文件对齐粒度对齐。将现有宿主程序的大小加上对齐后的大小,重新映射宿主程序。

步骤2:将宿主程序,要捆绑的文件复制到新映射的内存文件中,记录每个文件的相对位置,同时,将这些信息写入宿主程序中的两处捆绑列表所在位置。

步骤3:修改最后一节的相关参数。

捆绑过程的主要代码见代码清单 18-6:

测试运行:

为了查看程序执行效果,请按照以下步骤进行测试:

步骤1:编译 _hostasm,生成 _host.exe 可执行代码。

步骤2:编译 hex2db.asm,运行 hex2db,生成 _host.exe 的字节码 1.txt。

步骤3:编译 host.asm,将上一步生成的 _host.exe 字节码加入到数据段中。

步骤4:通过十六进制查看器查看生成的 host.exe 字节码,查找两个捆绑表所在位置。记录这个位置并将结果写到 bind.asm 中 。

步骤5:编译 bind.asm 并运行,生成最终的 D:\桌面\host.exe 程序。

图 18-2 是对本节中部分文件进行捆绑的示意图。其中各按钮的解释如下:

开始测试:

小结:

本章通过在目标 PE 文件未尾追加补丁的方式 (参见第 17 章),演示了一种将用户指定目录下的所有文件捆绑在一起的编程方法。实例中的补丁程序完成了释放宿主程序、释放相关捆绑文件、运行调度程序、实施绑定列表中指定可运行 PE 文件的批量同步运行。

EXE 捆绑器可应用于对指定 目录或文件进行加密,也可用于对批量 EXE 文件的执行,特别是在升级多个补丁工具时比较有用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值