目录

  • 预备知识
  • 实验目的
  • 实验环境
  • 实验步骤一
  • 实验步骤二
  • 实验步骤三

预备知识

本实验要求实验者具备如下的相关知识。
1.加密外壳中,破坏原程序的输入表是很关键的一步。在脱壳中,需要脱壳者对于pe格式中的输入表概念非常清楚。常见输入表的结构是为了表示从系统或外界调用DLL中的某些函数设计的。使用这些函数会涉及到函数名,函数所在的地址,这些分别用INT,IAT来表示。
INT:全称import name table输入名称表。简单来说,INT表存放了若干指针,通过这些指针,你可以找到所调用的函数的名字。
IAT:全称import address table输入地址表。简单来说,IAT表同样存放了若干指针,通过这些指针,你可以找到所调用函数的内存加载地址,也就是函数的入口地址。
windows弹窗程序编码说明:
程序用到的函数有两个:
MessageBoxA,在user32.dll中:

int WINAPI MessageBox(_In_opt_  HWND hWnd,_In_opt_  LPCTSTR lpText,_In_opt_  LPCTSTR lpCaption,_In_      UINT uType
);

ExitProcess在kernel32.dll中:

VOID WINAPI ExitProcess(_In_  UINT uExitCode
);

实验目的

通过该实验熟悉windowspe文件中导入表的结构和加载过程。通过手动完成导入表修复,理解导入表在pe文件中的作用。

实验环境


测试环境:windows xp sp3

实验步骤一

实验提供了一个不能正常运行的程序,如果在正常运行的情况下,程序的功能是弹出一个messagebox,然后正常退出。要做的,是通过排查错误,找到问题所在,修改程序,使其正常运行。
为此,本实验大致需要执行以下三个步骤:
1.大致了解程序的执行过程,通过od,配合stud_pe找到问题所在。
2.修改pe文件头中关于导入表的信息。
3.修改涉及到INT与IAT表的段中的信息。
任务描述:运行程序,用od调试,使用stud_pe查看,了解整个程序的流程,找出问题所在。
1.测试程序:

从图中可以看到可以显示控制台,但是程序没有正常运行,系统提示报错。
2.使用OD加载程序,查看程序执行流程,分析出错原因。

3.汇编代码中有push,有call,有jmp到最终地址,可以知道这里面涉及了函数调用,因为最终的函数调用都是用jmp这条指令来执行的,所以查看一下jmp到了那里。
选中jmp函数,右键[数据窗口中跟踪],[内存地址]。


从图中可以看到地址4020008处的hex数据为0x00000000,这是个不可执行的函数地址。
同理,可以查到指令0040101A处存在同样的问题。
4.问题出现了,函数调用却找不到有效地址。因此接下来使用stud_pe来查看一下该pe文件结构。

从图中可以看到,关于导入表的rva,size,raw信息都为空。
双击import_table按钮,进入导入表详情窗口。

可以看到,pe中未找到导入表。
综合上述步骤,可以发现,在pe文件中不存在导入表结构,因此程序加载的时候不能找到对应函数的入口地址,所以程序无法运行。

实验步骤二

任务描述:理解IAT、INT表,查找导入表位置。
因为导入表的位置可以随意标志,所以pe文件中必须说明导入表结构存放位置,位于PE文件提可选头DataDirectory结构中,一共有16个元素,每个元素8个字节。前4个字节表示目录表的起始位置,后4个字节表示该目录表的长度。
1.打开stud_pe,查看关于导入表的目录表。点击Basic HEADERS tree view in hexeditory。

2.进入之后在左侧的树形列表中选择Data_Directoires→Import Table。这时候右侧的显示面板中已经用红色方框选中了导入表目录描述区域,8个字节。

从图中可以看到,这里都填成了00,是没有意义的。所以还需要修改这个部分,使程序加载的时候能够找出导入表的位置。
经过上面2步,还是没有找到导入表的位置。只能慢慢在各个段中去寻找了。关闭窗口,到sections部分去查看。

可以看到,一共有3个段。在任意一个段上右键选择GoToSection Start,这样就可以跳转到该段的起始部分。

打开text的起始段,可以看到一串指令,由此可以得到程序代码执行段是在此段中。继续在.rdata段中寻找,看能否找到需要的一些线索。

在.rdata段中,我们看到了messageBoxA,user32.dll,exitProcess,kernel32.dll这些关键字。这是程序中执行时需要调用的函数名和dll名。这些函数名,dll名以及之前的函数名之前的两个字节,是导入表结构中的固定结构。这部分会在实验任务3中说明,而导入表函数说明往往和IAT,INT在一个段中。因此可以在空白部分补充需要的相关信息。

实验步骤三

任务描述:修复.rdata段,修复IAT,INT表,使之正常运行。
在实验开始之前,需要了解一些概念。
输入表以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始。简单来说,IID描述了调用哪个dll,这个dll中的函数名,以及这个函数所在的内存位置。
具体而言IID的结构:

IMAGE_IMPORT_DESCRIPTOR STRUCunionCharacteristics                DWORD ?OriginalFirstThunk             DWORD ?endsTimeDateStamp                  DWORD ?ForwarderChain                 DWORD ?Name                           DWORD ?FirstThunk                     DWORD ?IMAGE_IMPORT_DESCRIPTOR ENDS

其中:
OriginalFirstThunk(Characteristics):指向INT的RVA(相对虚拟地址)。
TimeDateStamp和ForwwaderChain我们可以忽略,以0填充。
Name:指向这个DLL的RVA。
FirstThunk:指向IAT的RVA。
IAT和INT都是IMAGE_THUNK_DATA结构,具体解释如下:

IMAGE_THUNK_DATA STRUC
union ul
ForwarderStringDWORD ?    ;指向一个转向者字符串的RVA
Function DWORD ?          ;被输入的函数的内存地址
Ordinal DWORD ?           ;被输入的API的序数值
AddressOfDataDWORD ?      ;指向IMAGE_IMPORT_BY_NAME
ends
IMAGE_THUNK_DATA_ENDS

注释中提到的image_import_by_name是指所调用的函数名结构表,结构如下:

IMAGE_IMPORT_BY_NAME STRUCT
Hint           WORD ?
Name           BYTE ?
IMAGE_IMPORT_BY_NAME ENDS

Hint是有关这个函数在dll中的索引号,name是这个函数的字符串表示。
为了方便理解,来串联一下:
系统使用外部函数,肯定得知道这个函数名名称,函数所在的dll名称,以及这个函数最终的入口地址。
INT能够表示出函数名是什么,IAT能够表示出这个函数的入口地址在哪里。
可是系统不知道那个结构是INT,IAT。这时候就用IID来描述。通过IID可以找到INT,IAT,以及调用的dll名称。
这就是IAT,INT,IID的作用。
Pe文件加载的时候,先通过IID里面指向的INT表获取出该函数名,在dll中查找出来该函数的入口地址,然后填入IID里面指向的IAT表。
回顾之前在od中看到的图。可以看到jmpdword [0x402008]。
这部分内容在我们的.text代码段中完成。Pe加载器通过IID找到函数的入口地址(准确的说就是通过INT找到地址),填写到对应的IAT表中去。所以0x402008应该是存放第一个函数入口地址的的位置,也就是messagebox的IAT的位置。
PE文件加载前:

PE文件加载后:

比较两幅图:看到,PE文件加载后,IAT表会发生变化,原来指向的是IMAGE_IMPORT_BY_NAME结构,加载后指向的是该函数的正真入口地址。这时候,我们的INT已经完成了检索作用,系统调用直接使用IAT表即可完成。
有了以上重要的基础知识,开始任务三。
确定填充结构。
填充过程一定有IAT,IID,INT。其中IAT和INT结构一致,为了区分,在两者中间加入IID表。
调用了多少个dll,就有多少个IID。IID以空值作为结束。调用了user32.dll和kernel32.dll,因此有3个IID结构(IID-exit,IID-messagebox,IID-NULL)。
每个函数对应一个IAT和INT,因此就有2个IAT和INT。
三者的位置可以随意,这与代码有关。刚才已经看到0x402008表示了messageBox的IAT。因此需要将IAT放在.rdata的段首。
所以,最终的结构应该是:
IAT(EXIT)+IAT(MESSAGEBOX)+IID(EXIT)+IID(MESSAGEBOX)+INT(EXIT)+INT(MESSAGEBOX)。
对应字节信息应该是:
4+4(4个0表示该dll中调用函数完毕)+4+4+20+20+20+4+4+4+4。
确定相关信息,完成地址转化。
先来确定函数和dll的位置信息。
先来确定messagebox的位置:

019D是messageboxA的索引号,后面是他的字符串描述。两者合起来构成IMAGE_IMPORT_BY_NAME结构。这个结构的起始VA是0x40205D,因为IID和INT,IAT中用的都是相对虚拟地址,需要做相应的转化。在stud_pe的主窗口中点击Rva↔Raw。选择Virtual Address,输入40205c,软件会自动转化为其他对应值,需要的是RVA,这时候转化为了205c。

事实上,在本程序中,涉及到.rdata段中的转化公式为:
RVA=VA-400000。
使用上面的方法,可以得到的信息包括:

首先来完成IAT表。
IAT表中每4个字节表示函数名的RVA。以4个0表示该IAT的结束。

查表可以得到,exit的rva是2076,因为只调用了kernel32.dll中的一个函数,所以,后面4个0。同理,messagebox的rva是205c,后面也补4个0。
完成IAT后,应该是上图这个样子。
接下来,完成INT的建立。INT表应该在IID之后,所以先确定IID的位置。IID一共有3个,每个IID0x14长度,3个加起来应该有0x3c长度。

这就是IID的范围。要在后方插入INT表。

一共有两个INT。同样,INT表示函数调用结束使用4个0作为标志。
因为指向的都是同一个地址,所以每个INT的值和对应的IAT的值是一样的。

这时候,已经完成了关于IAT和INT的结构了。
需要完成三个IID的构建。
3个IID=IID(exit)+IID(messagebox)+IID(00000000)。

三部分结构如上图所示。
先来完成kernel32.dll的IID。第一个4字节,是关于调用的函数的INT地址,也就是exit函数字符串的RVA。查表得到204c。后两个字节分别填0(已在本节实验中说明原因)。第3个4字节是kernel32.dll的RVA,查表得到为2084,最后一个4字节是exit的IAT,查表得到为2000。

Kernel32的IID如上图所示。
同样完成关于user32.dll的IID。查表得到信息包括:
INT:2054 dll_name_RVA:206a IAT:2008

第三部分的IID应该表示IID的结束,为全0。我们不需要修改。
这时候的.rdata段应该修改为:

点击下方的保存,文件会跟着更新。

别忘了,在最初确定IAT所在段的时候,pe头中关于导入表目录头还没有完成修改。这也就导致了系统找不到导入表。

完成导入表目录结构。
展开pe文件头,找到Data_Directories→Import_Table,左侧选中了这个区域,一共8个字节。前4个字节表示导入表中第一个IID的RVA,后面4个字节表示IID的总长度。
一共存在3个IID,每个长度为0x14,所以总长度应该为0x3c。
下面来确定第一个IID的RVA。

将VA转化为RVA结果应该是2010。所以,我们的这2个4字节应该是:2010和003C。

点击save to File。文件更新完毕。进行下一步的测试。

点击Test it。会看到程序正常运行。

pe结构分析之手工修复导入表相关推荐

  1. PE结构学习(5)_导入表与导出表

    导出表与导入表 通常来讲exe文件只有导入表而没有导出表,而dll文件既有导入表也有导出表 导出表 什么是导出表 代码重用机制提供了重用代码的动态链接库, 它会向调用者说明库里的哪些函数是可以被别人使 ...

  2. PE格式:手工实现各种脱壳后的修复

    手工修复导入表结构 实现手工修复导入表结构 1.首先需要找到加壳后程序的导入表以及导入了那些函数,使用PETools工具解析导入表结构,如下. 2.发现目录FOA地址为0x00000800的位置,长度 ...

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

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

  4. Windows PE 第八章 延迟加载导入表

    延迟加载导入表 延迟加载导入表是PE中引入的专门用来描述与动态链接库延迟加载相关的数据,因为这些数据所引起的作用和结构与导入表数据基本一致,所以称为延迟加载导入表. 延迟加载导入表和导入表是相互分离的 ...

  5. 写一个PE的壳_Part 2:ASLR+修复输入表(IAT)+重定位表支持(.reloc)

    系列汇总 写一个PE的壳_Part 1:加载PE文件到内存 写一个PE的壳_Part 2:ASLR+修复输入表(IAT)+重定位表支持(.reloc) 写一个PE的壳_Part 3:Section里实 ...

  6. PE格式:导入表与IAT内存修正

    本章教程中,使用的工具是上次制作的PE结构解析器,如果还不会使用请先看前一篇文章中对该工具的介绍,本章节内容主要复习导入表结构的基础知识点,并通过前面编写的一些小案例,实现对内存的转储与导入表的脱壳修 ...

  7. PE结构延迟加载导入表

    PE体系 PE结构&整体叙述 PE结构&导入表 PE结构&导出表 PE结构&基址重定位表 PE结构&绑定导入实现 PE结构&延迟加载导入表 简介 延迟加 ...

  8. PE学习(四)第四章:导入表

    第四章:导入表 windos加载器会一并加载导入表中的dll,并修改相应指令调用的函数地址. IMAGE_NT_HEADERS STRUCT{  Signature DWORD ?  FileHead ...

  9. PE文件结构详解(五)延迟导入表

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/12718845 PE文件结构详解(四)PE导入表讲了一般的 ...

最新文章

  1. Microsoft Visual Studio 2012 添加实体数据模型
  2. Spring Boot中使用MongoDB数据库
  3. 用Prime31实现Google Play In-App-Blling
  4. 【整体二分】区间第k小(金牌导航 整体二分-1)
  5. openshift_云上的播放框架变得简单:Openshift模块
  6. java在实际应用_Java应用程序如何部署在“现实世界”中?
  7. JMS(Java消息服务)与消息队列ActiveMQ基本使用(一)
  8. python实战演练_《Python高效开发实战》实战演练——
  9. spring(java,js,html) 截图上传
  10. 牛逼了!Python 开发植物大战僵尸游戏
  11. 贝叶斯网络结构学习方法
  12. 3dm游戏运行包_动作游戏ACT 逃离丧尸镇 Shadows of Kurgansk 电脑游戏资源
  13. 【优化预测】基于matlab天牛须算法优化BP神经网络预测【含Matlab源码 1318期】
  14. CentOS6.x安装zabbix
  15. 惠普计算机电源怎么设置充电,HP笔记本的三芯电源怎么改二芯实现充电?
  16. IntelliJ IDEA团队开始在中国招人了
  17. r语言中的或怎么表示什么不同_R语言 基本语法
  18. 抽丝拨茧——EventBus源码解析
  19. 接口可以继承接口吗?
  20. 剑指offer-二叉搜索树的第k个结点(python和c++)

热门文章

  1. 使用腾讯云开发者平台免费搭载静态云服务
  2. 大家以后不要说百毒的坏话了?
  3. Vue 监听刷新 切屏
  4. 励志照亮人生 编程改变命运
  5. linux中lost+found目录介绍
  6. 俄罗斯方块、贪吃蛇、心形表白 | 好玩的C语言源码
  7. 移动通信的主要测量指标及注意事项(转)
  8. CentOS搭建SVN服务器
  9. 玩烂vue之vue练手项目
  10. 易扩展,易复用,封装axios