题记:一篇绝对详细的手工构造PE文件教程

题记:一篇绝对详细的手工构造PE文件教程。



转载请注明版权:A1Pass

       一直以来都在学习PE文件结构,从不敢轻视,但是即使如此还是发现自己在这方面有所不足,于是便想到了用纯手工方式打造一个完整的可执行的PE文件。在这期间我也查了大量资料,但是这些资料都有一个通病就是不完整,看雪得那个只翻译了一部分,加解密技术内幕介绍的更是笼统,而且是打造一个只有180字节的PE文件,是高手们茶余饭后的怡情小游戏。

       鉴于此,心想为什么不自己摸索着手工打造一个完整些的呢?一是加强一下自己对于PE文件的了解,二是写出一篇参考性比较强的文章,给有志于在此发展的朋友们铺一铺路,也算是干了一件利国利民的好事。

       对于手工打造PE文件,我个人认为至少要分为三篇文章来阐述,每篇相对独立,合起来形成一个相对的体系。第一篇文章(也就是本文)用来介绍怎样用手工打造一个最典型、最简单的PE文件,而后两篇文章的问世还要引用潘爱民先生的一句话“还需要时日与机缘”。

       本文介绍的PE文件手工编辑方式,是本着以下三个原则所写的,望读者注意:
1、完整性:对于手工打造PE文件所不需注意的字段也进行了必要的介绍,因此整文可能显得非常臃肿。
2、典型性:完全按照典型的PE文件结构构造,因此对于某些不常见的PE文件结构有一定差距。
3、易学性:对于字段之间的逻辑关系进行了比较细致的介绍,因此对于一部分底子比较好的读者来说可能显得有些啰嗦。

       为了方便各位阅读与查阅,我将文章分成了三各部分,以便各位读者各取所需,不用把宝贵的精力浪费在查找上。
1、PE文件整体信息,提供了一个剖析PE文件的图表,以便于读者对于PE文件有一个整体的了解,并监督自己的工作进度。
2、对于重点字段的介绍,以及字段之间的逻辑关系,建议首先从这里开始看。
3、手工构造PE文件字段清单,此清单包含构造一个完整PE文件的每一个字节,跟着这个清单走就可以构造一个PE文件。

        对于第一次手工打造PE文件的朋友们来说,你们可以以“一、整体性息”为大纲,并参考第三部分一块一块的慢慢打造,如果有不懂的地方就去看第二部分。


选读:为什么要手工打造PE文件?

        我们知道,往往从一个系统可执行文件结构上,就可以看整个操作系统的一些特性。也就是说PE里有Windows操作系统结构与运行机理的影子。由此可见,PE文件必然是一个非常庞杂且逻辑复杂的结构,那么为什么我们还要“自取其辱”来手工制造一个PE文件呢?这就要从PE文件的重要性说起了。
     
        我们现今组成Windows大家庭的主要成员就是PE文件了,里面包括EXE、DLL、OCX、SYS等一切最有价值的文件都是PE文件格式,出于对版权的考虑或对某种技术的渴求,任何一种与Windows系统相关的行为最终都要归集到这里--PE文件。

        特别是对于想学习加壳、破解、搞虚拟机的朋友们来说,熟知PE文件结构更是必不可少的基本功!

        但也正是由于PE文件的复杂性,我们才要采取一些特别的办法来攻克它,其中手工打造PE文件就是一条捷径。

        你可以想像一下,如果你都可以手工打造PE文件的话,那么对于PE文件的了解更是可见一斑了。但是我还想提醒一下各位读者,即便是如此,我们所了解的也仅仅是一部分,不过一般情况下已经足够了。


一、整体性息

    这部分以图表的形式表示PE文件的整体结构。

-------------*-------------------------------------------------*
                 | DOS Header(IMAGE_DOS_HEADER) | -->64 Byte
DOS头部  --------------------------------------------------
                 | DOS Stub                                            | -->112 Byte
-------------*-------------------------------------------------*
                 | "PE"00 (Signature)                              | -->4 Byte
                  -------------------------------------------------
                 | IMAGE_FILE_HEADER                        | -->20 Byte
PE文件头 --------------------------------------------------
                 | IMAGE_OPTIONAL_HEADER32          | -->96 Byte
                 ---------------------------------------------------
                 | 数据目录表                                           | -->128 Byte
-------------*--------------------------------------------------*
                 | IMAGE_SECTION_HEADER                 | -->40 Byte
                 ---------------------------------------------------
   块表       | IMAGE_SECTION_HEADER                 | -->40 Byte
                  --------------------------------------------------
                 | IMAGE_SECTION_HEADER                 | -->40 Byte  
-------------*--------------------------------------------------*
                 |.text                                                       | -->512 Byte
                 ---------------------------------------------------
    块          |.rdata                                                   | -->512 Byte
                 ---------------------------------------------------
                 |.data                                                     | -->512 Byte
-------------*-------------------------------------------------*
                 | COFF行号                                            | -->NULL
                 ---------------------------------------------------
调试信息 | COFF符号表                                         | -->NULL
                 ---------------------------------------------------
                 | Code View 调试信息                             | -->NULL
-------------*--------------------------------------------------*

这部分内容的意义有二:
1、对于PE文件有一个整体的认识。
2、方便审查自己的构造进度。

       这里我们重点介绍怎样用其审查自己的构造进度,首先希望各位读者明白我们将要手工构造的一个体积为2560字节的这个小家伙,对于初次上手的读者们来说并不是一件小的工程,因此有必要知道自己现在正做什么,以及做到哪里了。

       记得我少年学画时老师教我们构图就要从整体到局部,后来自学编程仍然是先实现大的框架再去解决每一个细节问题。OK,现在到了这里,很显然我们仍然需要本着从整体到局部的思想来构造我们的PE文件。

       那好,我们先搞明白第一个问题“我们的文件体积是怎么计算出来的呢”。

       首先我们要知道,PE文件自始至终都是以一种节的思想来构造的,那么我们就要从节开始。

       对于本文所讲述的PE文件来讲总共有三个区块(节),他们分别用来存放可执行代码、输入表信息以及全局变量,接触过PE文件的朋友对于区块的概念应该不陌生,我们知道Windows下的很多应用程序的文件对齐粒度,也就是大名鼎鼎的FileAlignment字段的值多为200h Byte,也就是十进制的512 Byte。我们同样应该知道,对于不足512字节的区段,余下部分要用00h填充到512字节大小,对与超过部分(例如513字节的区段)我们就要在多分配给他512字节个空间。

       当然,这些基础知识我想有一部分读者应该比较熟悉,那么对于PE文件头部分呢?也是如此吗?例如本例中的PE头就占用了544个字节,但是很显然这要使其填充到1024(400h)字节处才能开始第一个区段.text段。

       正是如此,我们整个文件的体积就是PE头+3个区段的体积之和,也就是PE头(512*2)+3个区段(512*3)=2560,这也就是我们所构造的PE文件的最终大小了。

       其次我们提前搞清楚一些字段与区段的偏移量也是比较重要的,这里对于计算方式不再多说,请大家直接看下面的表:
1、PE头开始处 000000B0h
2、IMAGE_OPTIONAL_HEADER32开始处 000000C8h
3、数据目录表开始处 00000128h
4、块表开始处 000001A8h
5、.text块开始处 00000400h
6、.rdata块开始处 00000600h
7、.data块开始处 00000800h

       当然,上面的那些Offset只是针对本文件而言,并不绝对,具体情况还要具体分析。

       到此,本段就告一段落了,剩下的两段为了提高效率我并没有加以润色,全都是干货,希望各位读者能吃下这两块营养丰富的压缩饼干……


二、重点字段介绍

       这里只对需手工构造的字段进行着重介绍,详细的PE文件结构字段清单请见第三部分。

1、DOS头部
  1-1  DOS Header
    1-1-1   e_magic  [WORD]  -->4D 5A (* DOS可执行文件头标记)
    注释:此处值总是为MZ的16进制码。

    1-1-19  e_lfanew  [DWORD]  -->B0 00 00 00 (* 指向PE文件头的偏移量。0xB0=64+112)
    注释:此处的值正好为为DOS头部的大小,因为DOS头部后面就是PE文件头部分了。

2  PE文件头
  2-1  "PE"00
    2-1-1  Signature  [DWORD] -->50450000h (* PE文件头标记)
    注释:此处的值总是为PE的16进制值加0000h。

  2-2  IMAGE_FILE_HEADER
    2-2-1  Machine  [WORD]  -->4C 01 (* 可执行文件的目标CPU类型)
    注释:此PE文件可以运行于哪个CPU下,其标志就为相应的值。
    *------------------------------*
    |     机器            |  标志   |
    -------------------------------
    | Intel i386         | 14Ch   |
    -------------------------------
    | MIPS R3000    | 162h   |
    -------------------------------
    | MIPS R4000    | 166h   |
    -------------------------------
    | Alpha AXP       | 184h   |
    -------------------------------
    | Power PC        | 1F0h   |
    *-----------------------------*

    2-2-2  NumberOfSections  [WORD]  -->03 00 (* 区块数目)
    注释:此值决定此PE文件的区块数目,本文件为3个区块。

    2-2-6  SizeOfOptionalHeadr  [WORD]  -->E0 00 (* PE头(IMAGE_OPTIONAL_HEADER32)大小)
    注释:此值表示PE文件头的大小。

    2-2-7  Characteristics  [WORD]  -->0F 01 (* 文件属性)
    注释:此值为文件的执行属性。EXE文件此值一般为010Fh,DLL文件此值一般为0210h。

  2-3  IMAGE_OPTIONAL_HEADER32
    2-3-1   Magic  [WORD]  -->0B 01 (* 标记字)
    注释:此处是一个标记字,用于描述次PE文件的映像类型。ROM映像为0107h;普通可执行映像010Bh;PE32+则是020Bh。

    2-3-7   AddressOfEntryPoint  [DWORD] -->00 10 00 00 (* 程序执行入口RAV)
    注释:通俗的说就是指向可执行代码区块(例如.text)的首地址。

    2-3-10  ImageBase  [DWORD] -->00 00 40 00 (* 程序默认装入基地址)
    注释:是指文件在内存中首选的装入地址。

    2-3-11  SectionAlignment  [DWORD] -->00 10 00 00 (* 内存中区块对齐值)
    注释:PE文件被装入内存中时的块对齐大小,也叫做块粒度。其默认的对其尺寸是CPU的页尺寸。

    2-3-12  FileAlignment  [DWORD] -->00 02 00 00 (* 文件中区块对齐值)
    注释:磁盘上PE文件的区块对齐大小。这个值必须是2的幂,并且最小为200h。

    2-3-17  MajorSubsystemVersion  [WORD]  -->04 00 (* 运行所需最低子系统主版本号)
    注释:要求最低的子系统主版本号,一般情况下都为4。

    2-3-18  MinorSubsystemVersion  [WORD]  -->00 00 (* 运行所需最低子系统次版本号)
    注释:要求最低的子系统次版本号,一般情况下都为0。

    2-3-20  SizeOfImage  [DWORD] -->00 40 00 00 (* 映像装入内存后的总尺寸)
    注释:指的是装入文件从Image Base到最后一个区块的总大小。

    2-3-21  SizeOfHeaders  [DWORD] -->00 04 00 00 (* DOS头、PE头、区块表的总大小)
    注释:指的是DOS头、PE头与区块表的总大小,并且所有这些项目都出现在PE文件中任何代码或数据块之前。此值遵守文件对齐粒度。

    2-3-23  Subsystem [WORD]  -->03 00 (* 文件子系统)
    注释:标明可执行文件所期望的子系统(用户界面类型)。
          *----*-------------------------------------*
          | 值 |          子系统                          |
          *----*-------------------------------------*
          | 0  | 未知                                        |
          --------------------------------------------
          | 1  | 不需要子系统(如驱动程序) |
          ---------------------------------------------
          | 2  | 图形接口子系统(GUI)          |
          ---------------------------------------------
          | 3  | 字符子系统(CUI)                 |
          ---------------------------------------------
          | 5  | OS/2字符子系统                      |
          ---------------------------------------------
          | 7  | POSIX字符子系统                    |
          ---------------------------------------------
          | 8  | 保留                                         |
          ---------------------------------------------
          | 9  | Windows CE图形界面              |
          *----*--------------------------------------*

    2-3-30  NumberOfRvaAndSizes  [DWORD] -->10 00 00 00 (* 数据目录标的项数,默认总为16)
    注释:数据目录的项数。这个字段字NT系统发布以来就一直是16。

  2-4  数据目录表
    2-4-2   Import Table
    注释:输入表

      2-4-2-1  VirtualAddress [DWORD] -->10 20 00 00 (* 数据块的起始RAV)
      注释:输入表的起始地址,要去除IAT所占空间,直接从第一个IID开始。

      2-4-2-2  Size  [DWORD] -->3C 00 00 00 (* 数据块的长度)
      注释:从第一个IID到最后一个IMAGE_IMPORT_BY_NAME的总长度。

3  块表
  3-1  IMAGE_SECTION_HEADER (1 .text)
    3-1-1   Name  [BYTE]  -->2E 74 65 78 74 00 00 00 (* 8个字节的块名)
    注释:此区块的名称,限制在8字节以内。

    3-1-2   VirtualSize  [DWORD] -->26 00 00 00 (* 被实际使用的区块大小)
    注释:此区块包含数据的大小。

    3-1-10  Characteristics  [DWORD] -->20 00 00 60 (* 该区块的读写、执行属性)
    注释:

         *----------------------------------------------------------------*
          |  字段值   |              用 途                                         |
          -----------------------------------------------------------------
          | 00000020h | 包含代码,常与10000000h一起设置 |
          -----------------------------------------------------------------
          | 00000040h | 包含已初始化数据                              |
          -----------------------------------------------------------------
          | 00000080h | 包含未初始化数据                              |
          -----------------------------------------------------------------
          | 02000000h | 可以被丢弃                                         |
          -----------------------------------------------------------------
          | 10000000h | 共享块                                                |
          -----------------------------------------------------------------
          | 20000000h | 可执行                                                |
          -----------------------------------------------------------------
          | 40000000h | 可读                                                   |
          -----------------------------------------------------------------
          | 80000000h | 可写                                                   |
          -----------------------------------------------------------------

4  块
  4-1  .text (* 此区段是一段汇编代码的16进制形式,功能是弹出一个MessageBox提示框)
    -->HEX
    6A 00 68 00 30 40 00 68  07 30 40 00 6A 00 E8 07
    00 00 00 6A 00 E8 06 00  00 00 FF 25 08 20 40 00
    FF 25 00 20 40 00
    注释:这是下面汇编指令的16进制模式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 男女之间的爱情,源自朋友之间的善良,升华为恋人相互的珍重,最终演变成夫妻之间真挚的爱情。无论是从朋友到恋人,还是从恋人到夫妻,它们都有着共同的特点:坚定的信任、坚实的依赖、深沉的爱恋。 朋友之间,可以是一种无言的了解,也可以是熟悉的笑容,虽然不像恋人之间那样深情,但也有着温暖的爱意。只要朋友之间的感情坚固,就可以把这份友谊深化,发展成恋爱关系。恋人之间,开始可能只是一种牵动心弦的吸引,但随着相处越来越长,一种熟悉和依赖也会逐渐形成,双方也会越来越了解彼此,对对方的爱更加贴心。夫妻之间,是一种共同承担责任的爱,也是一种坚定不移的承诺。夫妻相互扶持,在困难时帮助彼此,在幸福时分享快乐,相互扶持,造就了一个完美的家庭。男女之间的爱情,从朋友到恋人,再到夫妻,走过的路都是那么的美好,愿每一份爱情都能够承受时间的考验,抵达最后的彼岸。 ### 回答2: 朋友,是一种伴我笑,伴我闹的亲密关系。我们分享喜悦,诉说心情,无拘无束地相互扶持。然而,在这个纷繁复杂的世界里,友情是那样的单纯而珍贵,它并不常常转变成恋情。然而,有些幸运的人却从朋友角色逐渐渗透进了爱的领域。 我们的关系像水般悄无声息地发生着改变,从朋友到恋人。那是一份互相吸引的力量,仿佛有一股无形的纽带将我们紧密联系在一起。我们不再只是谈论琐事,而开始分享更深层次的感情。每一次相遇都像是缠绵的一吻,每一句问候都蓄满了柔情蜜意。 我们的爱情在细水长流承载着我们的梦想和希望,以成为一对有情有义的夫妻。我们相信彼此,彼此信任、关心和支持。我们渴望通过婚姻的誓约,将这份爱 perpetuated 到永远。 但婚姻更像是一个新的起点,一个完全陌生的领域。我们将面临更多的责任和挑战,我们需要学会更多的忍耐、理解和包容。然而,我相信我们的坚定信念将会支撑我们的爱。在每个挫折面前,我们会紧握对方的手,共同面对人生的风雨。我们的夫妻关系将成为两颗心紧密相连的羁绊,永远无法割舍。 朋友,恋人,夫妻,这是一个演替的剧本。我们从一个角色转变为另一个,经历着不同的人生阶段。然而,我们的情感始终如一,不断升华。我们的爱不仅仅是悦目的表象,而是一种深入灵魂的契约。无论走到何处,我们将始终携手共进,共同创造美好的回忆。 ### 回答3: 朋友,是一个人生最美好的礼物。当我们与心灵相投的人成为朋友,情感的种子悄然埋下,温暖的友谊之花在我们心绽放。然而,有时候,友谊的边界并不止于此。当两个灵魂开始互相吸引,友情之上的情感便默默滋生,演变为一段美丽的恋情。 从朋友到恋人,这个过程接踵而来。我们开始对对方产生更多的非分之想,有了愈发深入的了解和真心的关心。我们乐于分享心事,乐于倾听对方的痛苦和喜悦。友谊之间的默契和信任,成为我们之间恋爱的基石。 在爱情的土壤里,我们的关系逐渐变得亲密而温暖。我们体验到彼此之间的亲密接触,感受到心弦上那动人的共鸣。我们能够在对方身上找到安慰和支持,一同度过人生的喜怒哀乐。 终于,我们迈入了夫妻的殿堂。这是一个承诺,表明我们决心一生一世守护对方、彼此相伴。婚姻不仅仅意味着爱情的牢牢束缚,更是两颗心彼此紧紧相连的象征。我们相信,这份爱将带给我们无尽的欢乐和充实,使我们的人生更加完整。 友谊可以演变成深厚的爱情,一对恋人可以成为携手走过一生的夫妻。这种发展,是源于我们共同的成长和理解,以及对彼此不断的包容和宽容。我们的情感经历了这样的转变,茁壮成长,让我们的心灵从相识到相知,从相知到相守。 友情、爱情和婚姻,是人生旅途上的三层境界。无论何时何地,这些情感都会陪伴我们度过平凡和特殊的日子。愿我们的友谊转变为永恒的爱情,成为相伴一生的伴侣,共同书写一段幸福的篇章。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值