zoukankan      html  css  js  c++  java
  • DOS头分析

    DOS头分析

    PE文件结构综览:

    首先上图片:

    看到上面的图片可以清晰的看到PE结构复杂结构式什么样子的。有DOS首部,PE头部,PE节表,很多的表块,最后就是一些调试信息。

    DOS头由DOS 'MZ' HEADER 和DOS stub组成,DOS "MZ"头中的MZ是PE文件的一个标志之一。后期我们在写PE小工具的时候这个会被我们用于去识别PE文件。

    首先我们来理解DOS头。我们知道Windows系统主体是由C去完成的。所有我们可以在windows中去找到用C描述的DOS头结构。

    DOS头分析:

    第一个就是WinNT.h在我计算机中位置。打开后我们就能看到我们想要的DOS头数据结构了。

    复制代码
     1 typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
     2 +0   WORD   e_magic;                     // Magic number
     3 +2   WORD   e_cblp;                      // Bytes on last page of file
     4 +4   WORD   e_cp;                        // Pages in file
     5 +6   WORD   e_crlc;                      // Relocations
     6 +8   WORD   e_cparhdr;                   // Size of header in paragraphs
     7 +A   WORD   e_minalloc;                  // Minimum extra paragraphs needed
     8 +C   WORD   e_maxalloc;                  // Maximum extra paragraphs needed
     9 +E   WORD   e_ss;                        // Initial (relative) SS value
    10 +10   WORD   e_sp;                        // Initial SP value
    11 +12   WORD   e_csum;                      // Checksum
    12 +14    WORD   e_ip;                        // Initial IP value
    13 +16    WORD   e_cs;                        // Initial (relative) CS value
    14 +18    WORD   e_lfarlc;                    // File address of relocation table
    15 +1A    WORD   e_ovno;                      // Overlay number
    16 +1C    WORD   e_res[4];                    // Reserved words
    17 +24    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    18 +26    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    19 +28    WORD   e_res2[10];                  // Reserved words
    20 +3C    LONG   e_lfanew;                    // File address of new exe header
    21   } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
    复制代码

    我们可以看到这样一个结构体这个结构体就是DOS 头结构体,Windows很多数据都是通过这样的结构体去组织的。前面的"+数字"是我添加上去的,WinNT.H头文件中没有的,这个表示偏移地址。在这里我默认情况是你知道什么是偏移地址,Windows的内存基址。否则一些东西你是无法理解的。当然这个数字是16进制表示的。

    我们用一个UE打开一个PE文件,也就是exe程序。(当然DLL也是PE文件,遵循PE结构。但是我们这里默认就是EXE程序),我打开我收集的一个内存查看工具:

    入下图:

    可以看+0H这个位置开始的2个字节(我在这里假设了解windows下的WORD类型是两个字节)。这两个字节也就是e_magic中的内容是MZ。DOS头中我们关注的另一个内容就是最后一个成员也就是+3Ch这个位置,也就是占四个字节的e_lfanew。(大家要知道32位机器中long型和DWORD型是一样的4个字节),DOS头中我们关注这两个内容。e_lfanew这个是为了存储PE头的偏移地址。那为什么要这个呢??DOS头后面不就是PE头了么?也就是+3Ch位置后面不就是PE头了么?其实不是我么这里的DOS头不是广义上的。广义上的DOS头还有一个。DOS stub 这个DOS stub的大小是不固定的,既然这个不固定那么广义上的DOS头的大小也就是不固定。广义DOS头不固定,那我们怎么定位PE头,那就离不开这个四字节的e_lfanew了。e_lfanew中存储了PE头的偏移地址。e_lfanew的位置在+3Ch位置。因为小端存储方式。所以就是上面图中画红线框中的倒过来,那么PE头的开始位置是 "0x00 00 0100",也就是16进制的100处。这样我们很容易的就定位到了PE头。

    这里就提前让大家看一下PE头结构吧:

    1 typedef struct _IMAGE_NT_HEADERS {
    2 +00h    DWORD Signature;
    3 +04h    IMAGE_FILE_HEADER FileHeader;
    4  ???    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    5 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;


    这个是广义上的PE头结构。这个结构的+00h处其实就是e_lfanew中偏移指向的位置总PE文件开始处的+3Ch处就是这个Signature相对文件开始的偏移(我们称FOA,也就是文件相对偏移地址,这个要和虚拟地址相对偏移RVA区分开,后续我会介绍FOA和RVA的装换方式,这样我们就可以在内存中定位数据了)。Signature中的四个字节内容其实就是"PE00",也就是+3Ch中的内容"0x00 00 0100"所指向的位置中的内容,100h位置中的四个字节中ASIIC就是“PE00”,这个是PE文件的标志符,e_magic,+0地址开始的2个字节中的“MZ”,和Signature 的+3C开始位置的四个字节内容“PE00”共同标志这就是PE文件。我们后期写的PE工具,判断PE就是有这两个位置的数据去区分是不是Windows PE文件。

    这一节到此位置。后面将和大家探讨PE head结构。

    版权:归博客园和Egojit所有,转载请标明出处。
     
     

    sql 分组不一定要group by

     

    今天在公司写代码的时候,遇到一个sql语句构建问题。

    情形是这样的:

    我需要获取不同小组下前N条记录。

    select top 10 * from dbo.Topic where GroupID in (60034,60037) and State=0  order by CrtTime desc

    很明显,这是错的,不仅没group by,获取出来的还是按两个小组的创建时间的前10条数据。

    可是,用group by的话,它有个很不通人性的特性。即是——凡是在group by后面出现的字段,必须同时在select后面出现;凡是在select后面出现的、同时未在聚合函数中出现的字段,必须同时出现在group by后面。

    这样就让我不得不放弃使用group by来分组查询。

    后来在网上找到,分组,不一定要用group by来实现。用row_number() over()同样可以实现。

    语法:ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN)

    简单的说row_number()从1开始,为每一条分组记录返回一个数字,这里的ROW_NUMBER() OVER (ORDER BY xlh DESC) 是先把xlh列降序,再为降序以后的没条xlh记录返回一个序号。

    row_number() OVER (PARTITION BY COL1 ORDER BY COL2) 表示根据COL1分组,在分组内部根据 COL2排序,而此函数计算的值就表示每组内部排序后的顺序编号(组内连续的唯一的)

    有了这么一个函数存在,最终我构建了我的sql语句:

    select *
    from
    (
    select *,row_number() over (partition by GroupID order by CrtTime desc) rank from Topic where State=0
    ) T
    where T.rank<=10 and T.GroupID in(60034,60040) 

    实现了这么一个功能:从不同的组别(60034,60040)里获取到了按创建时间降序排序的各个小组里的前10条记录。

    参考文章:sql查询-分组

         SQL ROW_NUMBER() OVER函数的基本用法用法

  • 相关阅读:
    如何让背景图片全屏显示
    浅谈图片如何在页面中居中显示
    clientHeight、offsetHeight 区别 笔记
    使用Flexible实现手淘H5页面的终端适配
    max(min)-device-width和max(min)-width的区别
    HTML5 meta最全使用手册
    常见浏览器兼容性问题与解决方案
    响应式设计的性能优化(转)
    移动H5前端性能优化指南
    SQL Server Profiler使用方法
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3335894.html
Copyright © 2011-2022 走看看