大神论坛

找回密码
快速注册
查看: 504 | 回复: 0

[原创] PE学习笔记三 DOS部分

主题

帖子

6

积分

初入江湖

UID
31
积分
6
精华
威望
12 点
违规
大神币
68 枚
注册时间
2021-04-10 16:01
发表于 2021-04-18 23:27
本帖最后由 ttegame 于 2021-04-18 23:27 编辑

PE学习笔记系列

PE学习笔记一 PE介绍

PE学习笔记二 PE文件的两种状态

PE学习笔记三 DOS部分
PE学习笔记四 PE文件头之标准PE头
PE学习笔记五 PE文件头之扩展PE头
PE学习笔记六 节表和节
PE学习笔记七 RVA与FOA转换
PE学习笔记八 实战之HOOK程序添加弹窗
PE学习笔记九 实战之HOOK程序添加弹窗续
PE学习笔记十 扩大节
PE学习笔记十一 新增节
PE学习笔记十二 修正内存对齐
PE学习笔记十三 合并节
PE学习笔记十四 导出表
PE学习笔记十五 导入表
PE学习笔记十六 代码重定位
PE学习笔记十七 重定位表


前面学习了PE结构的总体结构,接下来将具体学习PE的各个结构细节

这次学习的结构为DOS 部首

DOS部首

DOS部首结构

image-20210326211401967


image-20210326235719068


DOS部首结构对应C中的结构体说明
DOS 'MZ' HEADER_IMAGE_DOS_HEADERDOS MZ头 结构体
DOS stubDOS 存根

DOS MZ头

结构体截图

在winnt.h中找到_IMAGE_DOS_HEADER,得到以下截图(具体查找对应C结构体方法在PE文件笔记一 PE介绍中已经说明了,这里不再赘述)

image-20210326212749211


结构体代码

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;

结构体成员分析

_IMAGE_DOS_HEADER结构体的成员并不少,但在现在需要学习的只有两个

因为:

回顾

DOS部首,可以说是Windows的历史遗留问题了,因为Windows程序最早是在DOS系统(16位系统)上运行的

所以该部分主要是给DOS用的(向下兼容)


更新

目前在32位或64位 WINDOWS系统上还有效的只有两个成员了:

  • 第一个成员:e_magic
  • 最后一个成员:e_lfanew

成员详情

成员数据宽度注释说明
e_magicWORD(2字节)Magic numberPE文件判断标识固定为4d 5a (ASCII='MZ')
e_lfanewLONG(4字节)File address of new exe header存储PE头首地址不定

验证其余成员无效性

前面说到在目前的系统中,只有两个成员是有效的,为验证这一点,将其余成员全部置为0试试

1.用WinHex或UltraEdit等十六进制编辑器打开一个程序

这里采用WinHex进行操作,并选中其余成员部分

image-20210326223305500


2.将选中的部分,也就是其余成员部分全部修改为0

右键→编辑→填充选块 (快捷键Ctrl+L)

image-20210326224239328


image-20210326224313607


image-20210326224403422


确定修改后:

image-20210326224440179


3.保存修改的文件

文件→保存(快捷键Ctrl+S)

image-20210326224522965


image-20210326224557452


4.执行修改后的文件

image-20210326220814513


可以看到程序仍然可以正常运行,验证了:其余成员在32位及以上的Windows系统中无效

Dos Stub

Dos Stub在32位及以上的Windows系统中其实也无效,但不妨研究一下他的作用

1.截取出Dos Stub部分的数据

image-20210326224705523

选中部分为Dos Stub,其数据范围由_IMAGE_DOS_HEADER结构体中的最后一个成员e_lfanew决定


2.复制选中部分也就是Dos Stub部分的数据

image-20210326224751035


3.将数据粘贴到记事本中

image-20210326225328310


对应数据

0E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000FD661975B9077726B9077726B90777260448E126BB077726B07FE226A2077726A755F326BE077726B07FE426A6077726B9077626E8057726B07FF4267D077726B07FF32651077726A755E326B8077726B907E026BB077726B07FE626B807772652696368B90777260000000000000000

对应数据反汇编

PUSH CS
POP DS
MOV DX,000E
MOV AH,09
INT 21
MOV AX,4C01
INT 21
DB 54
DB 68
DB 69
DB 00
DB 33
DB 70
……

通过16位的反汇编引擎即可得到对应的反汇编代码

这里我们主要关注DB段,也就是汇编中数据段部分有DB 54;DB 68;DB 69 ……

在WINHEX中找到其对应的数据部分,查看其对应的ASCII

image-20210326231526001


数据部分为This program cannot be run in DOS

结合前面的两个INT 21 中断 不难猜测出该段数据对应的16位反汇编为输出数据部分的内容:This program cannot be run in DOS

自写代码解析DOS MZ头

了解了DOS部首的结构以后就可以自己写代码来读取DOS MZ头了

代码

// PE.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <malloc.h>
#include <windows.h>

int main(int argc, char* argv[])
{
    //创建DOS对应的结构体指针
        _IMAGE_DOS_HEADER* dos;
    //读取文件,返回文件句柄
        HANDLE hFile = CreateFileA("C:\\Documents and Settings\\Administrator\\桌面\\dbghelp.dll",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,0);
    //根据文件句柄创建映射
        HANDLE hMap = CreateFileMappingA(hFile,NULL,PAGE_READONLY,0,0,0);
    //映射内容
        LPVOID pFile = MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);
    //类型转换,用结构体的方式来读取
        dos=(_IMAGE_DOS_HEADER*)pFile;
    //输出结构体的第一个成员,以十六进制输出
        printf("%X\n",dos->e_magic);
        return 0;
}

运行结果

image-20210326234752508

可以看到能够正确地得到DOS MZ头对应的第一个成员的值:5A4D(对应ASCII为MZ)


总结

  • DOS部首分为两部分:DOS 'MZ' HEADER 和 DOS stub
  • DOS 'MZ' HEADER对应的结构体_IMAGE_DOS_HEADER中仅第一个成员和最后一个成员在32位及以上的WINDOWS系统上有效
  • DOS Stub对应为一串反汇编代码,其功能和输出This program cannot be run in DOS相关
  • DOS 'MZ' HEADER中无效的成员部分可用来填充shellcode来达到其它目的

返回顶部