惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
G
Google Developers Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
Cloudbric
Cloudbric
L
LINUX DO - 最新话题
MyScale Blog
MyScale Blog
H
Heimdal Security Blog
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
S
Security @ Cisco Blogs
Latest news
Latest news
I
Intezer
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
T
Threatpost
博客园 - 【当耐特】
S
Schneier on Security
P
Privacy International News Feed
G
GRAHAM CLULEY
T
Tenable Blog
AWS News Blog
AWS News Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
博客园 - Franky
Engineering at Meta
Engineering at Meta
美团技术团队
S
Secure Thoughts
T
Troy Hunt's Blog
Microsoft Security Blog
Microsoft Security Blog
SecWiki News
SecWiki News
V
Visual Studio Blog
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Martin Fowler
Martin Fowler
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - 老王博客

HttpModule的认识 破解软件感悟-PE文件格式之Import Table(引入表)(四) 破解软件系列-PE文件深入浅出之Section Table节表 反射调用与Lambda表达式调用 每日购入汇总 External Table 推荐绝对好评 C#中使用Oracle存储过程返回结果集 绝对好评的淘宝购物导航:baiso.uz.taobao.com over Oracle 正则表达式的学习与小结 Oracle 日期查询 oracle中如何创建dblink Oracle中的Union、Union All、Intersect、Minus 使用用法区别 ORACLE PL/SQL编程之八: 把触发器说透 验证数字的正则表达式集 --必有用正则表达式 大数据量传输时配置WCF的注意事项 Oracle中dual表的用途介绍 Oracle存储过程+游标
破解软件感悟-PE文件格式之实例总结(五)
老王博客 · 2014-09-03 · via 博客园 - 老王博客

有很多介绍PE文件的文章,但是我打算写一篇关于输入表的文章,因为它对于破解很有用。 
    我想解释它的最好的方法是举一个例子,你可以跟着我逐步深入,一步一步的思考,最后你将完全明白,我选择了一个我刚下载下来的小程序,它是用TASM编译的,有一个比较小的输入表,所以我想它应该是个不错的范例。 
    好了,让我们开始吧。首先我们得找到输入表,它的地址放在PE文件头偏移80处,所以我们用16进制编辑器打开我们的EXE文件,我们先得找到PE文件头的起始点,这很简单,因为它总是以PE,0,0开始,我们可以在偏移100处找到它。在一般的WIN32程序中文件头偏移被放在文件0X3C处,在那我们通常可看到00 01 00 00,由于数据存储时是低位在前,高位在后的,所以翻转过来实际就是00000100,就象前面我们说的。接下来我们就可以在PE文件中找到我们的输入表,100+80=180在偏移180处我们看到0030 0000,翻转一下,它其实应该是00003000,这说明输入表在内存3000处,我们必须把它转换成文件偏移。 
    一般来说,输入表总是在某个段的起始处,我们可以用PE编辑器来查看虚拟偏移,寻找3000并由此发现原始偏移。很简单的。打开我们看到: 
-CODE  00001000 00001000 00000200 00000600 
-DATA  00001000 00002000 00000200 00000800 
.idata 00001000 00003000 00000200 00000A00 
.reloc 00001000 00004000 00000200 00000C00 
    找一下,我们就发现.idata段的虚拟偏移是3000,原始偏移是A00,3000-A00=2600,我们要记住2600,以便以后转换其它的偏移。如果你没找到输入表的虚拟偏移,那么就找一下最接近的段。 
    来到偏移A00处,我们就看到被称为IMAGE_IMPORT_DESCRIPTORs(IID)的东东,它用5个字段表示每一个被调用DLL的信息,最后以Null结束。 
************************************************************************** 
(IID) IMAGE_IMPORT_DESCRIPTOR的结构包含如下5个字段: 
OriginalFirstThunk, TimeDateStamp, ForwarderChain, Name, FirstThunk 

OriginalFirstThunk 
该字段是指向一32位以00结束的RVA偏移地址串,此地址串中每个地址描述一个输入函数,它在输入表中的顺序是不变的。 

TimeDateStamp 
一个32位的时间标志,有特殊的用处。 

ForwarderChain 
输入函数列表的32位索引。 

Name 
DLL文件名(一个以00结束的ASCII字符串)的32位RVA地址。 

FirstThunk 
该字段是指向一32位以00结束的RVA偏移地址串,此地址串中每个地址描述一个输入函数,它在输入表中的顺序是可变的。 
************************************************************************** 

好了,你有没有理解?让我们看看我们有多少IID,它们从偏移A00处开始 
3C30 0000 / 0000 0000 / 0000 0000 / 8C30 0000 / 6430 0000 
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk} 
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000 
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk} 
0000 0000 / 0000 0000 / 0000 0000 / 0000 0000 / 0000 0000 

    每三分之一是个分界,我们知道每个IID包含了一个DLL的调用信息,现在我们有2个IID,所以我们估计这个程序调用了2个DLL。甚至我可以打赌,你能推测出我们将能找到什么。 
    每个IID的第四个字段表示的是名字,通过它我们可以知道被调用的函数名。第一个IID的名字字段是8C30 0000,翻转过来也就是地址0000308C,将它减去2600可以得到原始偏移,308C-2600=A8C,来到文件偏移A8C处,我们看到了什么?啊哈!原来调用的是KERNEL32.dll。 
    好了,接下来我们就要去找出KERNEL32.dll中被调用的函数。回到第一个IID。 
    FirstThunk字段包含了被调用的函数名的标志,OriginalFirstThunk仅仅是FirstThunk的备份,甚至有的程序根本没有,所以我们通常看FirstThunk,它在程序运行时被初始化。 
    KERNEL32.dll的FirstThunk字段值是6430 0000,翻转过来也就是地址00003064,减去2600得A64,在偏移A64处就是我们的IMAGE_THUNK_DATA,它存储的是一串地址,以一串00结束。如下:    
A430 0000/B230 0000/C030 0000/CE30 0000/DE30 0000/EA30 0000/F630 0000/0000 0000 
    通常在一个完整的程序里都将有这些。我们现在有了7个函数调用,让我们来看其中的两个: 
DE30 0000翻转后是30DE,减去2600后等于ADE,看在偏移ADE处的字符串是ReadFile, 
EA30 0000翻转后是30EA,减去2600后等于AEA,看在偏移AEA处的字符串是WriteFile, 
    你可能注意到了,在函数名前还有2个这字节的00,它是被作为一个提示。 
    很简单吧,你可以自己来试一下。回到A00,看第二个DLL的调用 
5C30 0000 / 0000 0000 / 0000 0000 / 9930 0000 / 8430 0000 
{OrignalFirstThunk} {TimeDateStamp} {ForwardChain} {Name} {First Thunk} 
    先找它的DLL文件名。9930翻转为3099-2600 =A99,在偏移A99处找到USER32.dll。再看FirstThunk字段值:8430翻转为3084-2600=A84,偏移A84处保存的地址为08310000,翻转后3108-2600=B08,偏移B08处字符串为MessageBoxA。明白了吧,接下来你就可以把这些用在你自己的EXE文件上了。 
    摘要: 
    在PE文件头+80偏移处存放着输入表的地址,输入表包含了DLL被调用的每个函数的函数名和FirstThunk,通常还有Forward Chain和TimeStamp。 
    当运行程序时系统调用GetProcAddress,将函数名作为参数,换取真正的函数入口地址,并在内存中写入输入表。当你对一个程序脱壳时你可能注意到你有了一个已经初始化的FirstThunk。例如,在我的WIN98上,函数GetProcAddress的入口地址是AE6DF7BF,在98上,所有的KERNEL32.dll函数调用地址看上去地址都象:xxxxF7BF,如果你在输入表中看到这些,你可以利用orignal thunk重建它,或者重建这个PE程序。 

第二个实例:

如果我们需要保护自己的程序,可以修改PE头的大小和起始位。

实例下载

解说:

  前提:需要用16进制编辑工具打开,比如:winhex,ultraedit,cs32asm等

1.       如果IMAGE_NT_HEADERS的signature域值等于"PE\0\0",那么就是有效的PE文件。我们看此处偏移0100处就是50 45,代表偏移头。

2.       我们前面讲过,在DOSMZ中有一项是用来指定PE头的起始位置的,那就在002c和002d处的值就是00 01,反过来写就是0100了。

PE头的起始位置

100  60 00

01 00

00 01

PE头的大小:

224个字节    00 E0,这个值我们前面讲过,其PE头的大小我们可以从中看到在0114 0115处。

修改的PE头:

24*16=384字节  180 反过来80 01偏移移到了0060处

然后根据需要修改偏移及大小即可完成pe头的修改。