前言

通过之前的学习,相信大家已经对磁盘的引导区有了充分的认识。但是我们之前的学习都是利用现成的工具来对引导区进行解析的,而对于一名反病毒工程师而言,不单单需要有扎实的逆向分析功底,同时也需要有很强的编程能力来解决实际问题。对于我们本次的课程来说,就需要大家亲自动手,利用程序来实现引导区的解析。这样做的目的,一方面是为了提高大家的编程能力,而另一方面则有助于我们更好地理解引导区的内容。

通过程序解析MBR

对于学习过PE文件格式解析的朋友来说,解析MBR可能不会有太大的问题,毕竟二者的原理还是非常相似的。但是虽然解析过PE文件,还是有一些微小的差别。首先造成解析困难的一点是MBR没有给出具体的结构体。如果大家分析过PE文件结构,那就知道,各个结构体在WinNt.h头文件中都有给出现成的定义,而MBR的定义是没有给出的。因此我们上次课在解析MBR时,并没有对照着结构体给大家介绍。再一个问题是,解析PE文件时,我们会打开具体的可执行文件去按照PE文件结构的定义进行解析,而硬盘的引导区并不属于某一个文件。用WinHex打开的是物理硬盘,那么我们如何通过程序来打开物理硬盘呢?这就是比较困惑的地方。不过,这些都不是太大的问题。本次的课程只要解决了这两个问题,那么,我们的进行编程解析时就容易多了。

自定义MBR的各种结构体

下面介绍如何将MBR的信息定义成一个个的结构体。通过前面用WinHex对MBR的手动分析,我们了解到MBR分为五部分,并且知道每部分占用的字节数。因此,可以将MBR定义为如下:

typedef struct _MBR
{
unsigned char BootRecord[440]; // 引导程序
unsigned char ulSigned[4]; // Windows磁盘签名
unsigned char sReserve[2]; // 保留位
unsigned char Dpt[64]; // 分区表
unsigned char EndSign[2]; // 结束标志
}MBR, *PMBR;

这就是定义的MBR了,引导程序共440个字节,Windows签名共4个字节,保留字节共2个字节,分区表共64个字节,再加上2个结束标志,一共512个字节。不过这样定义并不好,因为里面的常量比较多,下面修改一下,定义如下:

#define BOOTRECORDSIZE 440

#define DPTSIZE 64

typedef struct _MBR
{
unsigned char BootRecord[BOOTRECORDSIZE]; // 引导程序
unsigned char ulSigned[4]; // Windows磁盘签名
unsigned char sReserve[2]; // 保留位
unsigned char Dpt[DPTSIZE]; // 分区表
unsigned char EndSign[2]; // 结束标志
} MBR, *PMBR;

这样定义后,可以很方便地获得引导程序的大小和分区表的大小。虽然这样定义直观一些,但是还不能算太直观,因为定义的都是unsigned char类型,无法真正反映出每个成员变量的具体含义。下面再次进行修改,定义如下:

#define BOOTRECORDSIZE 440

typedef struct _BOOTRECORD
{
unsigned char BootRecord[BOOTRECORDSIZE];
}BOOTRECORD, *PBOOTRECORD; #define DPTSIZE 64 typedef struct _DPT
{
unsigned char Dpt[DPTSIZE];
}DPT, *PDPT; typedef struct _MBR
{
BOOTRECORD BootRecord;
unsigned char ulSigned[4];
unsigned char sReserve[2];
DPT Dpt;
unsigned char EndSign[2];
}MBR, *PMBR;

这次修改后,可以很容易地从MBR这个结构体中看出主要两个成员变量的含义了。虽然直观了,但还是有问题。Dpt其实是一个有4条记录的表,也就是说它其实是一个数组,这样的定义当解析它的时候并不方便。这样的定义方便我们一次性将DPT读出,只要再定义一个DP的结构体来对DPT进行转换,就可以方便地对DPT进行解析了。下面再次定义一个结构体,定义如下:

#define DPTNUMBER 4

typedef struct _DP
{
unsigned char BootSign; // 引导标志
unsigned char StartHsc[3]; // 分区的起始磁头号、扇区号、柱面号
unsigned char PartitionType; // 分区类型
unsigned char EndHsc[3]; // 分区的结束磁头号、扇区号、柱面号
ULONG SectorsPreceding; // 本分区之前使用的扇区数
ULONG SectorsInPartition; // 分区的总扇区数
}DP, *PDP;

有了这个结构体,就可以方便地对DPT进行解析了。最后两个定义就是对MBR各结构体的完整定义。之所以如此反复介绍如何进行MBR结构体的定义,是想告诉大家一个在没有相关数据结构定义的情况下如何通过自己的分析来定义数据结构的思路和方法。

大家可以思考一下,如果不定义这些结构体是不是就无法对MBR进行解析,定义了这些结构体后对于解析MBR有哪些影响。对于MBR的解析,可以完全不定义这些结构体,定义这些结构体的目的是方便对程序的后期维护,并使程序在整体上有一个良好的格式。定义数据结构可以清晰地表达各个数据结构之间的关系,让我们在写程序的过程中有一个清晰的思路,让看程序的人也可以一目了然。

硬盘设备的符号链接

有了上面的结构体,解析MBR已经不是太大的问题了。不过还有一个问题,那就是如何打开硬盘读取MBR。其实很简单,只要打开硬盘设备提供的设备符号链接就可以了。如何找到硬盘的设备符号链接呢?有一款工具WinObj可以帮助查找到。打开WinObj,再依次打开左边的树形控件,如下图所示:


通过上图可以找到硬盘设备的设备名称,例如可以通过\Device\Harddisk0\DR0这个设备名称再去查找相应的设备符号链接。我们再依次打开WinObj左边的树形控件,如下图所示:

由上图可知,硬盘的设备符号链接为PhysicalDrive0,那么在使用时就应该书写为\\.\PhysicalDrive0。

这里给大家简单地介绍一下设备名和设备符号链接。每个设备在Windows的内核中都有对应的驱动模块,在驱动模块中会为设备提供一个名字来对设备进行操作,驱动模块中提供的名字即为“设备名”。设备名只能在内核模块中使用。如果想要在应用程序下对设备进行操作,不能直接使用设备名称,应该使用设备符号链接。设备符号链接就是驱动模块为应用程序提供的操作设备的一个符号,通过这个符号可与设备进行对应。

解析MBR的程序实现

到了这里大家可能会觉得通过程序解析MBR已经不是问题了,下面直接提供程序的代码吧。如果代码中有不理解的地方,可以参考一下我们是如何通过WinHex对MBR进行解析的。代码如下:

#include "windows.h"
#include "stdio.h" // 显示MBR数据
VOID ShowMbr (HANDLE hDevice, PMBR pMbr)
{
DWORD dwRead = 0;
ReadFile(hDevice, (LPVOID)pMbr, sizeof(MBR), &dwRead, NULL); int i;
for(i= 0; i < 512; i++)
{
printf("%02X", ((BYTE*)pMbr)[i]);
if ((i+1)%16 == 0)
{
printf("\r\n");
}
}
} // 解析MBR
VOID ParseMbr(MBR Mbr)
{
printf("引导记录: \r\n"); for( int i = 0; i < BOOTRECORDSIZE; i++ )
{
printf("%02X ", Mbr.BootRecord.BootRecord[i]);
if((i+1)%16 == 0)
{
printf("\r\n");
}
} printf("\r\n"); printf("磁盘签名: \r\n");
for(i = 0; i < 4; i ++)
{
printf("%02X ", Mbr.ulSigned[i]);
} printf("\r\n"); printf("解析分区表: \r\n");
for(i = 0; i < DPTSIZE; i++)
{
printf("%02X ", Mbr.Dpt.Dpt[i]);
if((i+1)%16 == 0)
{
printf("\r\n");
}
} printf("\r\n"); // 获取分区表的地址,并将指向分区表的指针转换为PDP的类型,最后赋给pDp
PDP pDp = (PDP)&(Mbr.Dpt.Dpt);
for(i = 0; i < DPTNUMBER; i++)
{
printf("引导标志:%02X ",pDp[i].BootSign);
printf("分区类型:%02X ",pDp[i].PartitionType);
printf("\r\n");
printf("本分区之前扇区数:%d ",pDp[i].SectorsPreceding);
printf("本分区的总扇区数:%d ",pDp[i].SectorsInPartition);
printf("\r\n");
printf("该分区的大小:%f \r\n", (double)pDp[i].SectorsInPartition/1024*512/1024/1024); printf("\r\n \r\n");
} printf("结束标志:\r\n");
for (i = 0; i < 2; i ++)
{
printf("%02X ", Mbr.EndSign[i]);
} printf("\r\n");
} int main(int argc, char* argv[])
{
// 打开物理硬盘设备
HANDLE hDevice = CreateFile("\\\\.\\PhysicalDrive0",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("CreateFile Error %d \r\n", GetLastError());
return -1;
} MBR Mbr = { 0 };
ShowMbr(hDevice, &Mbr);
ParseMbr(Mbr); CloseHandle(hDevice); getchar(); return 0;
}

代码非常短,也不复杂,看起来跟读写文件没什么太大的差别,其实就是在读写文件。前面介绍过,Windows将各种设备都当作文件来看待,因此打开硬盘设备的时候直接使用CreateFile()函数就可以了。

小结

我们这次的实验,为了实现编程解析MBR的目的,首先是通过之前的知识创建了相应的结构体,并给大家演示了我们的结构体一步一步不断完善的过程。接下来给大家讲解了如何利用工具查找硬盘设备的符号链接,通过这个符号链接,我们就可以通过程序来打开MBR了。最后是整个程序的编写,主要实现了显示MBR的数据以及MBR的解析这两大功能,希望大家能够举一反三,有所收获。

病毒木马查杀实战第024篇:MBR病毒之编程解析引导区的更多相关文章

  1. 病毒木马查杀实战第025篇:JS下载者脚本木马的分析与防御

    前言 这次我与大家分享的是我所总结的关于JS下载者脚本木马的分析与防御技术.之所以要选择这样的一个题目,是因为在日常的病毒分析工作中,每天都会遇到这类病毒样本,少则几个,多则几十个(当然了,更多的样本 ...

  2. 病毒木马查杀实战第011篇:QQ盗号木马之专杀工具的编写

    前言 由于我已经在<病毒木马查杀第004篇:熊猫烧香之专杀工具的编写>中编写了一个比较通用的专杀工具的框架,而这个框架对于本病毒来说,经过简单修改也是基本适用的,所以本文就不讨论那些重叠的 ...

  3. 病毒木马查杀实战第010篇:QQ盗号木马之十六进制代码分析

    前言 按照我的个人习惯,在运用诸如IDA Pro与OllyDBG对病毒进行逆向分析之前,我都会利用一些自动化的工具,通过静态或动态的分析方法(参见<病毒木马查杀第008篇:熊猫烧香之病毒查杀总结 ...

  4. 病毒木马查杀实战第009篇:QQ盗号木马之手动查杀

    前言 之前在<病毒木马查杀第002篇:熊猫烧香之手动查杀>中,我在不借助任何工具的情况下,基本实现了对于"熊猫烧香"病毒的查杀.但是毕竟"熊猫烧香" ...

  5. 病毒木马查杀实战第023篇:MBR病毒之引导区的解析

    前言 引导型病毒指寄生在磁盘引导区或主引导区的计算机病毒.这种病毒利用系统引导时,不对主引导区的内容正确与否进行判别的缺点,在引导系统的过程中入侵系统,驻留内存,监视系统运行,伺机传染和破坏.按照引导 ...

  6. 病毒木马查杀实战第020篇:Ring3层主动防御之基本原理

    前言 假设说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后.杀软一般都会弹出一个对话框提示我们,下载的程序非常可能是恶意程序,建议删除之类的.或者杀软就不提示.直接删除了:或 ...

  7. 病毒木马查杀实战第015篇:U盘病毒之脱壳研究

    前言 因为我们的终于目标是编写出针对于这次的U盘病毒的专杀工具.而通过上次的分析我们知道,病毒有可能在不同的计算机中会以不同的名称进行显示.假设真是如此,那么就有必要在此分析出病毒的命名规律等特征,然 ...

  8. 病毒木马查杀实战第017篇:U盘病毒之专杀工具的编写

    前言 经过前几次的讨论,我们对于这次的U盘病毒已经有了一定的了解,那么这次我们就依据病毒的行为特征,来编写针对于这次U盘病毒的专杀工具. 专杀工具功能说明 因为这次是一个U盘病毒,所以我打算把这次的专 ...

  9. 病毒木马查杀实战第022篇:txt病毒研究

    前言 反病毒爱好者们非常喜欢讨论的一个问题就是,现在什么样的病毒才算得上是主流,或者说什么样的病毒才是厉害的病毒呢?我们之前的课程所解说的都是Ring3层的病毒.所以有些朋友可能会觉得.那么Ring0 ...

随机推荐

  1. HDOJ-2896(AC自动机+文本串中出现了哪几个模板串)

    病毒侵袭 HDOJ-2896 主要使用AC自动机解决,其次在query函数中改变一下,用来记录每个模板串出现的次数,还有insert函数中记录模板串的编号 需要注意最好使用结构体,而且不能一次性使用m ...

  2. CCF(通信网络):简单DFS+floyd算法

    通信网络 201709-4 一看到题目分析了题意之后,我就想到用floyd算法来求解每一对顶点的最短路.如果一个点和任意一个点都有最短路(不为INF),那么这就是符合的一个答案.可是因为题目超时,只能 ...

  3. 太上老君的炼丹炉之分布式 Quorum NWR

    分布式系列文章: 1.用三国杀讲分布式算法,舒适了吧? 2.用太极拳讲分布式理论,真舒服! 3.诸葛亮 VS 庞统,拿下 Paxos 共识算法 4.用动图讲解分布式 Raft 5.韩信大招:一致性哈希 ...

  4. WooYun-2016-199433 -phpmyadmin-反序列化-getshell

    文章参考 http://www.mottoin.com/detail/521.html https://www.cnblogs.com/xhds/p/12579425.html 虽然是很老的漏洞,但在 ...

  5. Netty源码 新连接处理

    上文我们阐述了Netty的Reactor模型.在Reactor模型的第二阶段,Netty会处理各种io事件.对于客户端的各种请求就是在这个阶段去处理的.本文便来分析一个新的连接是如何被处理的. 代码的 ...

  6. 推荐!!! Markdown图标索引网站

    作者:三十三重天 博客: http://www.zhouhuibo.club 我们在观察别人的文章时候时,总能看到很多有趣的图标,像是这样

  7. 以“有匪”为实战案例,用python爬取视频弹幕

    最近腾讯独播热剧"有匪"特别火,我也一直在追剧,每次看剧的时候都是把弹幕开启的,这样子看剧才有灵魂呀.借助手中的技术,想爬取弹幕分析下这部电视剧的具体情况和网友们的评论!对于弹幕的 ...

  8. 【odoo14】第十八章、自动化测试

    当我们开发大型应用的时候,通过自动化测试可以大幅提高应用的健壮性.每年,odoo都会发布新版本,自动化测试对于应用的回归测试非常有帮助.幸运的是,odoo框架有不同自动化测试用例.odoo主要包括三种 ...

  9. 优化 ASP.NET Core Docker 镜像的大小

    在这容器化的世界里,我们已经很少直接通过文件发布来运行asp.net core程序了.现在大多数情况下,我们都会使用docker来运行程序.在使用docker之前,我们往往需要打包我们的应用程序.as ...

  10. 【10.5NOIP普及模拟】sum

    [10.5NOIP普及模拟]sum 文章目录 [10.5NOIP普及模拟]sum 题目描述 输入 输出 输入输出样例 样例输入 样例输出 解析 code 题目描述 小x有很多糖果,分成了 N 堆,排成 ...