手写可执行程序


【软件名称】: Hello World!
【软件大小】: 2.5K
【编写语言】: 机器码
【使用工具】: VC++ 6.0
【操作平台】: Winxp

--------------------------------------------------------------------------------
【详细过程】
      
        最近,学习PE结构的知识。之后深有感触,随即便萌发了不依赖任何开发环境和编译器,纯手工写一个小程序的念
  头。为了简单而又令所有学习程序开发的人感到亲切,就写一个Hello World! 程序吧...
    
        在这里,我们首先复习一下Win32可执行程序的大体结构,就是通常所说的PE结构。
    PE 的意思就是Portable Executable(可移植的执行体)。
    PE结构如下图:
    
    ︱ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄︱
    ︱     MS-DOS       ︱
    ︱    MZ  头部      ︱--------------> 64 byte
    ︱                  ︱
    ︱  ̄  ̄  ̄  ̄  ̄  ̄︱
    ︱     MS-DOS       ︱
    ︱ 实模式残余程序   ︱--------------> 112 byte 
    ︱                  ︱
    ︱ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄︱
    ︱    PE文件标志    ︱--------------> 4 byte
    ︱                  ︱
    ︱ ̄  ̄  ̄  ̄  ̄  ̄ ︱
    ︱     PE文件头     ︱--------------> 20 byte
    ︱                  ︱
    ︱ ̄  ̄  ̄  ̄  ̄  ̄ ︱
    ︱   PE文件可选头   ︱--------------> 224 byte
    ︱                  ︱
    ︱ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄︱
    ︱     各段头部     ︱--------------> n * 40 byte
    ︱                  ︱
      ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
    
    DOS MZ header:所有PE文件(甚至32位的DLLs) 必须以一个简单的DOS MZ header 开始。有了它,一旦程序在DOS下执
  行,DOS就能识别出这是有效的执行体,然后运行紧随MZ header 之后的DOS程序。以此达到对Dos系统的兼容。
    (DOS MZ header总共占用64byte)
    
    MS-DOS 实模式残余程序:实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它将简单显示一个错误提示,
    大多数情况下它是由汇编器/编译器自动生成。通常,它简单调用中断21h服务9来显示字符串
    "This program cannot run in DOS mode"。(在我们写的程序中,他不是必须的,可以不于以实现,但是要保留其大
  小,大小为112byte,为了简洁,我们就用00来填充。)
    
    PE文件标志:是PE文件结构的起始标志。(长度4byte, Windows程序此值必须为0x50450000)
    
    PE文件头:是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。执行体在支持PE文件
  结构的操作系统中执行时,PE装载器将从DOS MZ header中找到PE header的起始偏移量,跳过了MS-DOS 实模式残余程序 ,
  直接定位到真正的文件头PE header, 长度20byte)。
    
    PE文件可选头:虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。
    (长度 224byte )
     
    各段头部:一个Windows NT的应用程序典型地拥有9个预定义段,它们是.text、.bss、.rdata、.data、.rsrc、
    .edata、.idata、.pdata和.debug。一些应用程序不需要所有的这些段,同样还有一些应用程序为了自己特殊
    的需要而定义了更多的段。(每段占40byte,我们这里也不需要所有的段,仅需3个段。)
    
        以上仅仅是对PE结构各部分的大体讲解,先热热身而已。接下来在手写这个Hello World!程序中,我会详细介绍
  每个字节的含义。
        
        首先准备一下工具(肯定有人要问了,不是纯手写吗?怎么还要准备工具啊?),毕竟一个十六进制编辑器是少
  不了的,否则在哪写啊?我在这里就使用VC++ 6.0所携带的十六进制编辑器。好了,工具暂时就这个了,开工了。
    
        打开VC,选择文件,新建菜单项,然后选择一个二进制文件,确定。一切就绪了,下面就开始我们的手写可执行
  程序旅程吧......

        首先我们来完成“DOS MZ header”,“DOS MZ header”的功能前面已经讲过,在这里就不多说了,我们直接来
  看一下如何实现他。“DOS MZ header”总共64byte,他对应的结构是IMAGE_DOS_HEADER ,在WINNT.H文件中有定义。通过
  这个结构我们可以看到,这64字节被分成19个成员,每个成员都有他的功能,与其说我们在一个字节一个字节的手写可执行
  程序,倒不如说我们是在一个成员一个成员的写。因为单独的一个字节并不一定具有什么意义。我们在学习过程中,就是要
  把整个部分拆分成几个成员,然后一个成员一个成员的去学习。(实际上并不是我们去拆分,人家已经帮我们拆分好了。我
  们只需按照标准的结构去学习就可以了。所有的结构都定义在WINNT.H文件中。)

  typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
      WORD   e_magic;                     // Magic number
      WORD   e_cblp;                      // Bytes on last page of file
      WORD   e_cp;                        // Pages in file
      WORD   e_crlc;                      // Relocations
      WORD   e_cparhdr;                   // Size of header in paragraphs
      WORD   e_minalloc;                  // Minimum extra paragraphs needed
      WORD   e_maxalloc;                  // Maximum extra paragraphs needed
      WORD   e_ss;                        // Initial (relative) SS value
      WORD   e_sp;                        // Initial SP value
      WORD   e_csum;                      // Checksum
      WORD   e_ip;                        // Initial IP value
      WORD   e_cs;                        // Initial (relative) CS value
      WORD   e_lfarlc;                    // File address of relocation table
      WORD   e_ovno;                      // Overlay number
      WORD   e_res[4];                    // Reserved words
      WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
      WORD   e_oeminfo;                   // OEM information; e_oemid specific
      WORD   e_res2[10];                  // Reserved words
      LONG   e_lfanew;                    // File address of new exe header
    } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
    
        第一个成员占2个字节,它被用于表示一个MS-DOS兼容的文件类型,他的值是固定的----“4D5A”
    (注意:因为我们是在十六进制编辑器下写数据,所以所有的数据格式都是十六进制式的。但是我们在开发环境中默认
    都是十进制的,所以必须在数据前加 0x ,即:0x4D5A。而我为了方便,就直接写成“4D5A”,也就是直接输入到编辑器
    中的值,是十六进制,后面的都照此规定书写。)
    
        第2个成员到第18个成员总共58个字节,是对DOS程序环境的初始化等操作,对于我们这个程序来说,没什么影响,
    我们通通用“00”来填充。(如果读者想对其进行详细了解,请查阅相关书籍。)注意:因为我们不可能把PE结构所
    有的东西都面面俱到,他十分的庞大。当然也没有必要都去记他,只需掌握关键的地方就可以了。以后我们都将把不
    影响程序执行的成员填充为零,这样做,一方面使程序看起来简洁,另一方面可以使您快速定位PE结构中要重点掌握
    的地方。
    
        第19个成员非常重要,他占4个字节,用来表示“PE文件标志”在文件中的偏移,单位是byte。而从上图中可以看到
  “PE文件标志”紧随“MS-DOS 实模式残余程序”其后。知道这一点,我们就可以计算一下了,我们的“DOS MZ header”总
   共64 byte,后面的“MS-DOS 实模式残余程序”占112 byte, 64 + 112 = 176 byte,但是要注意,我们这里的176可是十进制
   的,转化成十六进制是B0,对了,就是这个值,因为是4个字节,所以我们应该填“B0000000”。

      接下来我们来完成“MS-DOS 实模式残余程序”,我们已经知道,他是用在DOS下执
  行的,我们这里可以直接用“00”来填充,注意总共112 byte。 这两部分完成之后代码如下:
    
    00000  4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00  MZ..............
    00010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00030  00 00 00 00 00 00 00 00 00 00 00 00 B0 00 00 00  ............?...
    00040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    000a0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    
     接下来我们便进入主题,开始写真正的PE结构部分:微软将“PE文件标志”,“PE文件头 ”,“PE文件可选头 ”
  这三个部分用一个结构来定义,即:IMAGE_NT_HEADERS32(WINNT.H中有定义,后面象这样的结构均在WINNT.H中有定义),

  typedef struct _IMAGE_NT_HEADERS {
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
  } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

  这个结构含有3个成员:<

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值