lyl610abc 发表于 2021-3-26 23:58

PE文件笔记三 DOS部分

本帖最后由 lyl610abc 于 2021-4-3 11:51 编辑

继续PE系列笔记的更新

PE其它笔记索引可前往:
(https://www.52pojie.cn/thread-1391994-1-1.html)

------

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

这次学习的结构为DOS 部首

# DOS部首

## DOS部首结构

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326211401967.png)

------

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326235719068.png)

------

| DOS部首结构   | 对应C中的结构体   | 说明            |
| --------------- | ----------------- | --------------- |
| DOS 'MZ' HEADER | _IMAGE_DOS_HEADER | DOS MZ头 结构体 |
| DOS stub      | 无                | DOS 存根      |

------

## DOS MZ头

### 结构体截图

在winnt.h中找到_IMAGE_DOS_HEADER,得到以下截图(具体查找对应C结构体方法在(https://www.52pojie.cn/forum.php?mod=viewthread&tid=1391994&page=1&extra=#37411342_pe%E5%9C%A8c%E4%B8%AD%E7%9A%84%E5%AE%9A%E4%B9%89)中已经说明了,这里不再赘述)

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326212749211.png)

------

### 结构体代码

```c
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;                  // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2;                  // 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_magic| WORD(2字节) | Magic number                   | **PE文件判断标识** | 固定为4d 5a (ASCII='MZ') |
| e_lfanew | LONG(4字节) | File address of new exe header | **存储PE头首地址** | 不定                     |

------

#### 验证其余成员无效性

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

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

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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326223305500.png)

------

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

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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224239328.png)

------

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224313607.png)

------

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224403422.png)

------

确定修改后:

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224440179.png)

------

**3.保存修改的文件**

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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224522965.png)

------

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224557452.png)

------

**4.执行修改后的文件**

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326220814513.png)

------

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

## Dos Stub

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

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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224705523.png)

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

------

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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326224751035.png)

------

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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326225328310.png)

------

### 对应数据

```assembly
0E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000FD661975B9077726B9077726B90777260448E126BB077726B07FE226A2077726A755F326BE077726B07FE426A6077726B9077626E8057726B07FF4267D077726B07FF32651077726A755E326B8077726B907E026BB077726B07FE626B807772652696368B90777260000000000000000
```

------

### 对应数据反汇编

```assembly
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

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326231526001.png)

------

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

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

## 自写代码解析DOS MZ头

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

### 代码

```c
// 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;
}
```

------

### 运行结果

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210326234752508.png)

可以看到能够正确地得到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来达到其它目的

# 附件

附上本笔记中分析的EverEdit文件:[点我下载](https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/EverEdit.zip?versionId=CAEQHhiBgID6g6vXxBciIDMxMjY5Y2Q0NGE5NTRkNmNiNTUwOGM0YjdmZTQxMTI3)

lyl610abc 发表于 2021-3-28 14:25

本帖最后由 lyl610abc 于 2021-3-30 16:40 编辑

布衣木人 发表于 2021-3-28 14:15
最好在64位机器上写,同时最好用下vs
代码是通用的,编译环境不同而已
同样的代码,在VS 2019win10 x64下也是可以运行的

lyl610abc 发表于 2021-4-6 12:44

aswcy815174418 发表于 2021-4-6 12:39
输出完5a4d后不得CloseHandle嘛

只是读取,读取完程序都停止运行了,这个也只是个demo,我也就懒得加了
出于程序结构的完整性应该是要CloseHandle的
除了CloseHandle还要UnmapViewOfFile

布衣木人 发表于 2021-3-28 15:16

lyl610abc 发表于 2021-3-28 14:25
代码是通用的,编译环境不同而已
同样的代码,在VS 2019win10 x64下也是可以运行的

pStrNtHeaders->OptionalHeader.Magic;这个值0x10b、0x20b等,就要区分了,IMAGE_NT_HEADERS、IMAGE_NT_HEADERS64里面的结构就不一样了。所以不如直接用你现在的vs2019

dukeimp 发表于 2021-3-27 00:20

模板大佬

a970826 发表于 2021-3-27 00:30

膜拜大佬。感谢分享

qianshang666 发表于 2021-3-27 06:33

点名表扬,更新的非常nice

fly19930620 发表于 2021-3-27 08:09

学习下看看

Redfiremaple 发表于 2021-3-27 08:44

先收藏,感谢分享。

helanzhu1 发表于 2021-3-27 09:26

感谢分享

平头哥头不平 发表于 2021-3-27 09:43

膜拜大佬。感谢分享

宜城小站 发表于 2021-3-27 10:18

感谢分享{:1_893:}
学习{:1_921:}

xjh123456 发表于 2021-3-27 16:09


感谢分享
学习
页: [1] 2 3 4
查看完整版本: PE文件笔记三 DOS部分