REVERSE总结

1.ARM汇编指令

1)ARM指令通常跟一到两个操作数,我们使用如下模板描述:

MNEMONIC{S}{condition} {Rd}, Operand1, Operand2

其中各个字段的作用如下:

MNEMONIC - 指令的助记符如ADD

{S} - 可选的扩展位,如果指令后加了S,将依据计算结果更新CPSR寄存器中相应的FLAG

{condition} - 执行条件,如果没有指定,默认为AL(无条件执行)

{Rd} - 目的寄存器,存储指令计算结果

Operand1 - 第一个操作数,可以是一个寄存器或一个立即数

Operand2 - 第二个(可变)操作数,可以是一个立即数或寄存器甚至带移位操作的寄存器

助记符、S扩展位、目的寄存器和第一个操作数的作用很好理解,不多做解释,这里补充解释一下执行条件和第二个操作数。设置了执行条件的指令在执行指令前先校验CPSR寄存器中的标志位,只有标志位的组合匹配所设置的执行条件指令才会被执行。第二个操作数被称为可变操作数,因为它可以被设置为多种形式,包括立即数、寄存器、带移位操作的寄存器,如下所示:
#123 - 立即数

Rx - 寄存器比如R1

Rx, ASR n - 对寄存器中的值进行算术右移n位后的值

Rx, LSL n - 对寄存器中的值进行逻辑左移n位后的值

Rx, LSR n - 对寄存器中的值进行逻辑右移n位后的值

Rx, ROR n - 对寄存器中的值进行循环右移n位后的值

Rx, RRX - 对寄存器中的值进行带扩展的循环右移1位后的值

在这里插入图片描述

1.1.载入和存储

ldr = Load Word 载入字

ldrh = Load unsigned Half Word 载入无符号半字

ldrsh = Load signed Half Word 载入有符号半字

ldrb = Load unsigned Byte 载入无符号字节

ldrsb = Load signed Bytes 载入有符号字节

str = Store Word 储存字

strh = Store unsigned Half Word 储存无符号半字

strsh = Store signed Half Word 储存有符号半字

strb = Store unsigned Byte 储存无符号字节

strsb = Store signed Byte 储存有符号字节
在这里插入图片描述
在这里插入图片描述
ARM专用的函数调用规则规定了函数的前四个参数应该分别存贮与R0到R3中。

ldm r0, {r4,r5} /* words[3] -> r4 = 0x03; words[4] -> r5 = 0x04 */

使用LDM指令,将R0指向的内存中,取两个字的数据出来。由于之前我们让R0指向了word[3],因此word[3]的值会被存入R4, word[4]的会被存入R5

stm r1, {r4,r5} /* r4 -> array_buff[0] = 0x03; r5 -> array_buff[1] = 0x04 */
使用STM指令将多个值写入内存。STM指令将R4,R5寄存器的值0x3和0x4存入R1指向的内存空间中。

LDM和STM有多种不同的使用形式。具体是哪一种使用形式由指令的后缀所决定。这个示例列出了后缀的几种形式:-IA (之后增加), -IB (之前增加), -DA (之后减少), -DB (之前减少)。

ldmia r0, {r4-r6} /* words[3] -> r4 = 0x03, words[4] -> r5 = 0x04; words[5] -> r6 = 0x05; */

stmia r1, {r4-r6} /* r4 -> array_buff[0] = 0x03; r5 -> array_buff[1] = 0x04; r6 -> array_buff[2] = 0x05 */

执行完上面的两条指令后,寄存器R4-R6分别包含了 0x3,0x4,0x5 ,内存地址0x000100D0, 0x000100D4,和 0x000100D8中也包含了0x3,0x4,0x5

ldmib r0,{r4-r6} /*words[4] -> r4 = 0x04; words[5] -> r5 = 0x05; words[6] -> r6 = 0x06 */

stmib r1,{r4-r6} /* r4-> array_buff[1] = 0x04; r5 -> array_buff[2] = 0x05; r6 ->array_buff[3] = 0x06 */

LDMIB指令先将源地址增加4字节(一个字),然后开始载入数据。该方法仍能让我们载入一串前向数据,但第一个数据的起始地址相较源地址有4个字节的偏移。这就是为什么我们用LDMIB指令将内存中的第一个元素载入R4后,R4是0x04(word[4]),而不是R0指向的0x3(word[3])

ldmda r0, {r4-r6} /* words[3] -> r6 = 0x03; words[2] -> r5 = 0x02; words[1] -> r4 = 0x01 */

使用LDMDA向相反方向执行运算。

stmda r2, {r4-r6} /* r6 -> array_buff[2] = 0x02; r5 -> array_buff[1] = 0x01; r4 -> array_buff[0] = 0x00 */

同样的道理

stmdb r2, {r4-r5} /* r5 -> array_buff[1] = 0x01; r4 -> array_buff[0] = 0x00; */

1.2.转移指令

当特定条件满足时,借助条件指令, 通过跳转(分支)或执行某些特定指令来控制程序的流动方向。相关条件被描述为CPSR寄存器中的特定位的状态,这些位根据指令计算后的结果实时改变。比如,如果我们比较两个数并且他们相等,就将零标志位置位(Z=1) ,因为在系统底层发生了a-b=0。在这个例子里两个数是相等的,但如果第一个数字比第二个大,会得出大于结论。而相反的情况下得出小于结论。当然还有很多其他的条件,比如小于等于(LE),大于等于(GE)等等。

下表列出了可能的条件指令,他们的含义以及被检测的状态标志位
在这里插入图片描述

1.3.跳转指令

b分支指令 简单地跳向一个函数

bl分支连接指令 将(PC+4)保存到LR中并跳转到函数

BX(分支切换指令)和BLX(分支连接切换指令)

a) 和B/BL+交换指令集相同( ARM <-> Thumb )

b) 需要用寄存器作为第一操作数:BX/BLX+具体的寄存器

BX/BLX用来从ARM指令集切换到Thumb指令集

2.PE文件知识

在这里插入图片描述

2.1.PE头

PE头分为5个部分,如下表
在这里插入图片描述
在一个PE文件的开始处,我们会看到一个MS-DOS可执行体(英语叫“stub”,意为“根,存根”);它使任何PE文件都是一个有效的MS-DOS可执行文件。

在DOS-根之后是一个32位的签名以及魔数0x00004550 (IMAGE_NT_SIGNATURE)(意为“NT签名”,也就是PE签名;十六进制数45和50分别代表ASCII码字母E和P----译者注)。

之后是文件头(按COFF格式),用来说明该二进制文件将运行在何种机器之上、分几个区段、链接的时间、是可执行文件还是DLL、等等。(本文中可执行文件和DLL文件的区别在于:DLL文件不能被启动,但能被别的二进制文件使用,而一个二进制文件则不能链接到另一个可执行文件。)

那些之后,是可选头(尽管它一直都存在,却仍被称作“可选”----因为COFF文件格式仅为库文件使用一个“可选头”,却不为目标文件使用一个“可选头”,这就是为什么它被称为“可选”的原因)。它会告诉我们该二进制文件怎样被载入的更多信息:开始的地址呀、保留的堆栈数呀、数据段的大小呀、等等。

可选头的一个有趣的部分是尾部的“数据目录”数组;这些目录包含许多指向各“节”数据的指针。例如:如果一个二进制文件拥有一个输出目录,那么你就会在数组成员“IMAGE_DIRECTORY_ENTRY_EXPORT”(输出目录项)中找到一个指向那个目录的指针,而该指针指向文件中的某节。

跟在各种头后面我们就发现各个“节”了,它们都由“节头”引导。本质上讲,各节中的内容才是你执行一个程序真正需要的东西,所有头和目录这些东西只是为了帮助你找到它们。

每节都含有和对齐、包含什么样的数据(如“已初始化数据”等等)、是否能共享等有关的一些标记,还有就是数据本身。大多数(并非所有)节都含有一个或多个可通过可选头的“数据目录”数组中的项来参见的目录,如输出函数目录和基址重定位目录等。无目录形式的内容有:例如“可执行代码”或“已初始化数据”等。

2.2.PE头之IMAGE_FILE_HEADER解析

在IMAGE_DOS_HEADER结构中,只关心e_magic以及e_lfanew,分别是第一个字段以及最后一个字段。

1.e_magic字段

e_magic字段类型为WORD,占用两个字节,表示PE文件的一个Magic标志,它的值固定为4D 5A,即PE文件的e_magic值一定就是4D 5A,如果不是4D 5A则表示不是PE文件,因此e_magic通常作为识别一个文件是否是PE文件的标准之一。在小端模式下,4D 5A表示为WORD类型的数据为0x5A4D,在WinNT.h头文件中定义了一个名为IMAGE_DOS_SIGNATURE的宏,它的值就是0x5A4D,如下所示:

#define IMAGE_DOS_SIGNATURE    0x5A4D  // MZ

2.e_lfanew字段

e_lfanew用于指定PE文件头的偏移,因为在IMAGE_DOS_HEADER头和PE头之间隔了一段DosStub数据,而DosStub的大小是可变的,所以需要通过e_lfanew才能找到PE文件头的位置。IMAGE_DOS_HEADER结构体的大小为64字节,而e_lfanew位于该结构的最后四字节,所以e_lfanew对应第61~64四个字节的数据(下标索引为60~63)。

2.3.PE头之IMAGE_FILE_HEADER

IMAGE_NT_HEADERS结构体在WinNT.h头文件中的定义如下所示:

typedef struct _IMAGE_NT_HEADERS {

    DWORD Signature;

    IMAGE_FILE_HEADER FileHeader;

    IMAGE_OPTIONAL_HEADER32 OptionalHeader;

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

可以看出,IMAGE_NT_HEADERS由一个DWORD类型的Signature成员、一个IMAGE_FILE_HEADER类型的FileHeader、以及一个IMAGE_OPTIONAL_HEADER32类型的OptionalHeader组成。

Signature字段的类型为DWORD,占用四个字节,也表示PE文件的一个Magic标志(在之前的实验中,讲到了IMAGE_DOS_HEADER的e_magic也是PE文件的Magic标志),同样,Signature的值固定为50 45 00 00,在小端模式下表示为0x00004550,字符串表示为“PE\0\0”。Signature在WinNt.h头文件中的定义如下所示:

#define IMAGE_NT_SIGNATURE    0x00004550  // PE00

1.该结构的第二个成员为FileHeader,对应的类型为IMAGE_FILE_HEADER,该结构体在WinNt.h头文件中的定义如下:

typedef struct _IMAGE_FILE_HEADER {

    WORD    Machine;

    WORD    NumberOfSections;

    DWORD   TimeDateStamp;

    DWORD   PointerToSymbolTable;

    DWORD   NumberOfSymbols;

    WORD    SizeOfOptionalHeader;

    WORD    Characteristics;

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
  1. Machine字段,占用2个字节,指明PE文件支持的CPU类型;PE文件可以支持多种CPU类型,不同的CPU对应的机器码是不一样的,几种典型的机器类型标志如下所示:
    在这里插入图片描述

  2. NumberOfSections字段,占用2个字节,指明PE文件包含的节区数量,对notepad.exe而言,这个值为3,因为其包含.text、.data以及.rsrc三个区块;

  3. TimeDateStamp字段,占用4个字节,指明文件的创建时间,注意这个“创建”指的是编译器生成这个PE文件的时间;TimeDateStamp的值表示为1970年1月1日以来用格林威治时间计算的秒数,通过一定的换算即可将其转换为当前的时间信息,LordPE工具提供了这样的功能。
    打开桌面的LordPE工具,在LordPE的主界面中单击右侧的“PE编辑器”按钮,载入C:\PE\notepad.exe文件,可以看到“日期时间标志”为0x48025287,如下图所示:
    在这里插入图片描述
    复制下48025287这个值,然后单击界面右侧的“时间标志”按钮来打开转换工具,在编码区域内填入48025287,随后单击解码左侧的复选框,即可完成时间转换,可以看到转换后的时间信息为2008年4月13日18时35分51秒,这就是编译器生成notepad.exe的时间,如下图所示:
    在这里插入图片描述

  4. PointerToSymbolTable字段,COFF符号表的文件偏移位置,这个字段跟调试符号相关,这里不作研究;

  5. NumberOfSymbols字段,如果存在COFF符号表,这个字段表示其中的符号数目,该字段同样跟调试符号相关,这里不作研究;

  6. SizeOfOptionalHeader字段,指明在IMAGE_NT_HEADERS中紧跟在FileHeader后的OptionalHeader的大小,对于32位的PE文件而言,这个值的大小通常为0x00E0;

  7. Characteristics字段,表明文件的属性,如果Characteristics & 0x2000 = 0x2000,那么表明这是一个DLL文件(在WinNT.h头文件中定义了#define IMAGE_FILE_DLL 0x2000这样一个宏);

2.4.PE头之IMAGE_OPTIONAL_HEADER解析

typedef struct _IMAGE_OPTIONAL_HEADER {

   WORD    Magic;

   BYTE    MajorLinkerVersion;

   BYTE    MinorLinkerVersion;

   DWORD   SizeOfCode;

   DWORD   SizeOfInitializedData;

   DWORD   SizeOfUninitializedData;

   DWORD   AddressOfEntryPoint;

   DWORD   BaseOfCode;

   DWORD   BaseOfData;

   DWORD   ImageBase;

   DWORD   SectionAlignment;

   DWORD   FileAlignment;

   WORD    MajorOperatingSystemVersion;

   WORD    MinorOperatingSystemVersion;

   WORD    MajorImageVersion;

   WORD    MinorImageVersion;

   WORD    MajorSubsystemVersion;

   WORD    MinorSubsystemVersion;

   DWORD   Win32VersionValue;

   DWORD   SizeOfImage;

   DWORD   SizeOfHeaders;

   DWORD   CheckSum;

   WORD    Subsystem;

   WORD    DllCharacteristics;

   DWORD   SizeOfStackReserve;

   DWORD   SizeOfStackCommit;

   DWORD   SizeOfHeapReserve;

   DWORD   SizeOfHeapCommit;

   DWORD   LoaderFlags;

   DWORD   NumberOfRvaAndSizes;

   IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

IMAGE_OPTIONAL_HEADER结构各个成员字段的含义如下:

  1. Magic,表明是ROM映像还是普通的可执行映像,普通的可执行文件的值为0x010B,PE32+可执行文件的值为0x020B(PE32+即64位的PE文件,这种文件只能运行在64位的Windows操作系统下);

  2. MajorLinkerVersion,链接器的主版本号;

  3. MinorLinkerVersion,链接器的次版本号;

  4. SizeOfCode,代码区块的大小,通常而言可执行文件仅有一个代码区块.text,所以通常就是.text区块的大小;

  5. SizeOfInitializedData,已初始化数据块的大小,不作研究;

  6. SizeOfUninitializedData,未初始化数据块的大小,不作研究;

  7. AddressOfEntryPoint,重要字段,程序执行的入口点地址,是一个RVA值;

  8. BaseOfCode,代码段的起始地址,是一个RVA值,通常而言就是.text区块的起始RVA;

  9. BaseOfData,数据段的起始地址,是一个RVA值,通常而言就是.data区块的起始RVA;

  10. ImageBase,重要字段,PE文件在内存中首选的装载基地址;

  11. SectionAlignment,当PE文件装载到内存时区块的对齐大小,假设.text区块的大小为0x7748,而SectionAlignment的大小为0x1000,那么对齐后的大小为0x8000字节;

  12. FileAlignment,磁盘上PE文件中区块的对齐大小,对齐方式类似SectionAlignment;

  13. MajorOperatingSystemVersion,要求的操作系统最低的主版本号;

  14. MinorOperatingSystemVersion,要求的操作系统最低的次版本号;

  15. MajorImageVersion,PE文件本身的主版本号;

  16. MinorImageVersion,PE文件本身的次版本号;

  17. MajorSubsystemVersion,要求的子系统最低的主版本号;

  18. MinorSubsystemVersion,要求的子系统最低的次版本号;

  19. Win32VersionValue,保留字段,通常为0;

  20. SizeOfImage,PE文件被装载到内存空间后总的大小,指从ImageBase到最后一个区块的大小;

  21. SizeOfHeaders,Dos头、DosStub、PE头以及区块头的总大小,并进行FileAlignment对齐后的大小;

  22. CheckSum,校验和,一般的EXE文件通常为0,对重要的系统驱动程序而言比较有意义;

  23. Subsystem,子系统,EXE最常用的子系统类型有console以及windows,分别对应控制台应用程序以及GUI应用程序;

  24. DllCharacteristics,包含有DEP以及ASLR相关的属性位;

  25. SizeOfStackReserve,不作研究;

  26. SizeOfStackCommit,不作研究;

  27. SizeOfHeapReserve,不作研究;

  28. SizeOfHeapCommit,不作研究;

  29. LoaderFlags,与调试相关,不作研究;

  30. NumberOfRvaAndSizes,数据目录项的个数,固定为16,即后面一个成员DataDirectory的数组元素个数;

  31. DataDirectory,数据目录表,包含有输入表、输出表等表项的具体信息;

OptionalHeader的末尾是一个数据目录表数组,即类型为IMAGE_DATA_DIRECTORY的数组DataDirectory,数组的元素个数为16,各个元素所对应的表项如下图所示:
在这里插入图片描述

typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD   VirtualAddress;
      DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

2.5.PE头之节表头解析

紧跟在IMAGE_NT_HEADERS之后的是节表头,即IMAGE_SECTION_HEADER。
每个节区都会有一个对应的节表头,节表头以及节区的数量都是由IMAGE_NT_HEADERS.FileHeader.NumberOfSections字段的值所决定的。节表头对应的结构体为IMAGE_SECTION_HEADER,该结构体在WinNT.h头文件中的定义如下所示:

typedef struct _IMAGE_SECTION_HEADER {

    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];

    union {

            DWORD   PhysicalAddress;

            DWORD   VirtualSize;

    } Misc;

    DWORD   VirtualAddress;

    DWORD   SizeOfRawData;

    DWORD   PointerToRawData;

    DWORD   PointerToRelocations;

    DWORD   PointerToLinenumbers;

    WORD    NumberOfRelocations;

    WORD    NumberOfLinenumbers;

    DWORD   Characteristics;

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

其中各个字段的介绍如下:

  1. Name,为对应节区的名字,其中IMAGE_SIZEOF_SHORT_NAME的值固定为8。如果名字的长度小于8,则以NULL字符结束;如果名字的长度等于8,则没有NULL字符,因为数组长度为8。节区的名字通常以点号开头,如.text、.data等,通常而言,这个名字可以随便修改。

  2. VirtualSize,在未对齐的情况下,区块所有数据的大小。

  3. VirtualAddress,区块被装载到内存时的RVA,这个值总是SectionAlignment的整数倍。

  4. SizeOfRawData,区块数据在磁盘文件中按照FileAlignment对齐后的大小。

  5. PointerToRawData,区块数据在磁盘文件中的偏移地址。

  6. PointerToRelocations、PointerToLinenumbers、NumberOfRelocations、NumberOfLinenumbers:不是很重要的字段,这里不作研究。

  7. Characteristics,区块的属性值,表明区块的可读、可写、可执行等相关属性。

2.6.输入表解析

INT能够表示出函数名是什么,IAT能够表示出这个函数的入口地址在哪里。
可是系统不知道那个结构是INT,IAT。这时候就用IID来描述。通过IID可以找到INT,IAT,以及调用的dll名称。
这就是IAT,INT,IID的作用。

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

         DWORD   Characteristics;

         DWORD   OriginalFirstThunk;

     } DUMMYUNIONNAME;

          DWORD   TimeDateStamp;

          DWORD   ForwarderChain;

          DWORD   Name;

          DWORD   FirstThunk;

} IMAGE_IMPORT_DESCRIPTOR;

关于IID结构体中各个成员的含义介绍如下:

  1. OriginalFirstThunk,RVA,指向输入名称表(简称INT),INT是一个类型为IMAGE_THUNK_DATA结构的数组,同样的,通过在数组末尾附加一个空的IMAGE_THUNK_DATA结构来表示数组的结束,每一个输入的函数都有一个对应的IMAGE_THUNK_DATA结构;
  2. TimeDateStamp,DLL的时间日期标志,通常忽略;
  3. ForwarderChain,通常忽略,不作讲解;
  4. Name,RVA,指向DLL的名字,如“User32.dll”;
  5. FirstThunk,RVA,指向输入地址表(简称IAT),IAT也是一个类型为IMAGE_THUNK_DATA结构的数组,同样的,通过在数组末尾附加一个空的IMAGE_THUNK_DATA结构来表示数组的结束,每一个输入的函数都有一个对应的IMAGE_THUNK_DATA结构;
    需要注意的是,OriginalFirstThunk与FirstThunk极其相似,前者指向INT,后者指向IAT,其中INT和IAT都是类型为IMAGE_THUNK_DATA的数组
    在这里插入图片描述

IMAGE_THUNK_DATA结构:

typedef struct _IMAGE_THUNK_DATA32 {

     union {

         DWORD ForwarderString;

         DWORD Function;

         DWORD Ordinal;

         DWORD AddressOfData;

     } u1;

} IMAGE_THUNK_DATA32;

可以看出,该类型仅有的一个成员u1是一个联合体(union),而联合体内的类型都是DWORD,所以IMAGE_THUNK_DATA的大小是4字节。当该类型的最高位为1时,表示函数以序号的方式进行输入,这时候低31位的值就表示函数的序号;当该类型的最高位为0时,表示函数以名字的方式进行输入,这时候值就表示一个指向IMAGE_IMPORT_BY_NAME结构的RVA。

typedef struct _IMAGE_IMPORT_BY_NAME {

     WORD    Hint;

     BYTE    Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

OriginalFirstThunk指向的INT数组和FirstThunk指向的IAT数组到底有什么区别呢?答案是当PE文件被加载时,PE加载器会遍历INT结构中的数组项,通过其指向的IMAGE_IMPORT_BY_NAME结构来找到函数的名字,PE加载器可以通过函数的名字找到函数的地址,随后把得到的函数地址填充到IAT结构中,此后,通过IAT结构中的函数地址就可以进行函数调用了。
在这里插入图片描述

2.7.输出表解析

typedef struct _IMAGE_EXPORT_DIRECTORY {

    DWORD   Characteristics;

    DWORD   TimeDateStamp;

    WORD    MajorVersion;

    WORD    MinorVersion;

    DWORD   Name;

    DWORD   Base;

    DWORD   NumberOfFunctions;

    DWORD   NumberOfNames;

    DWORD   AddressOfFunctions;

    DWORD   AddressOfNames;

     DWORD   AddressOfNameOrdinals;

} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
  1. Characteristics,表示输出属性,这个值当前没有意义,总是设置为0;

  2. TimeDateStamp,输出表创建的时间(格林威治标准时间);

  3. MajorVersion,输出表的主版本号,这个值当前没有意义,总是设置为0;

  4. MinorVersion,输出表的次版本号,这个值当前没有意义,总是设置为0;

  5. Name,RVA,模块的真实名字,指向一个ASCII字符串,这个字符串是与这些输出函数关联的DLL的名字(如User32.dll);

  6. Base,输出表的起始序数值;

  7. NumberOfFunctions,输出函数的个数;

  8. NumberOfNames,以名字输出的函数的个数;

  9. AddressOfFunctions,RVA,指向包含输出函数地址的RVA数组;

  10. AddressOfNames,RVA,指向包含以名字方式输出的函数的ASCII字符串名字的RVA数组;

  11. AddressOfNameOrdinals,RVA,指向每个以名字方式输出的函数对应在AddressOfFunctions指向的数组中的索引;
    在这里插入图片描述

2.8.资源结构解析

一个PE文件或多或少都会包含有资源数据,如图标(Icon)、对话框(Dialog)、字符串(String)等,相对于输入表、输出表、重定位而言,资源结构相对更加复杂。 资源结构采用类似文件系统的分层目录结构,通常包含三层目录。数据目录表的第三个成员指向资源表,其类型为IMAGE_RESOURCE_DIRECTORY,该结构占用16字节,该结构体在WinNT.h头文件中的定义如下:

typedef struct _IMAGE_RESOURCE_DIRECTORY {

    DWORD   Characteristics;

    DWORD   TimeDateStamp;

    WORD    MajorVersion;

    WORD    MinorVersion;

    WORD    NumberOfNamedEntries;

    WORD    NumberOfIdEntries;

} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

IMAGE_RESOURCE_DIRECTORY结构体中各个字段的含义如下:

  1. Characteristics,资源的属性标志,当前没有意义,它的值总是设置为0;

  2. TimeDateStamp,资源数据被资源编译器创建的时间;

  3. Major Version,资源的主版本号,通常为0;

  4. Minor Version,资源的次版本号,通常为0;

  5. NumberOfNamedEntries,重要字段,以字符串命名的资源条目的数量;

  6. NumberOfIdEntries,重要字段,以整数命名的资源条目的数量;

主要关注最后两个字段:NumberOfNamedEntries与NumberOfIdEntries,两者加起来就是资源表根目录下总的资源条目的数量。

在IMAGE_RESOURCE_DIRECTORY之后,紧跟着NumberOfNamedEntries + NumberOfIdEntries个IMAGE_RESOURCE_DIRECTORY_ENTRY(简称IRDE)结构类型的数据。IMAGE_RESOURCE_DIRECTORY_ENTRY结构体在WinNT.h头文件中的定义如下所示:

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {

    DWORD   Name;

    DWORD   OffsetToData;

} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

该结构体占用8个字节,结构体的成员的介绍如下:

  1. Name,这个字段拥有多个不同的含义:当IRDE位于第一层目录时,Name表示资源的类型;当IRDE位于第二层目录时,Name表示资源的名称;当IRDE位于第三层目录时,Name表示代码页的编号。此外,还需要先判断Name的最高位是0还是1,如果是0则表示当做一个值来使用,如果是1,则表示低31位当做指针来使用;
  2. OffsetToData,指针;当最高位为1时,表示低位数据指向下一层目录;当最高位为0时,表示没有下一层目录,低位数据指向一个IMAGE_RESOURCE_DATA_ENTRY结构。

注意,当Name和OffsetToData当做指针使用时,其值并不是RVA,而是表示相对于资源区块起始位置的偏移值。
前面提到,当IMAGE_RESOURCE_DIRECTORY_ENTRY位于第一层目录时,它的Name字段表示资源类型,系统预定义的资源类型如下图所示:
在这里插入图片描述
资源名称字符串使用Unicode编码,其结构类型为IMAGE_RESOURCE_DIR_STRING_U,定义如下:

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {

    WORD    Length;

    WCHAR   NameString[ 1 ];

} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

其中Length表示字符串的长度,NameString数组存储实际的Unicode字符。

IMAGE_RESOURCE_DATA_ENTRY结构体在WinNT.h头文件中的定义如下:

typedef struct _IMAGE_RESOURCE_DATA_ENTRY {

    DWORD   OffsetToData;

    DWORD   Size;

    DWORD   CodePage;

    DWORD   Reserved;

} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;      

IMAGE_RESOURCE_DATA_ENTRY结构体成员的介绍如下:

  1. OffsetToData,这是一个RVA,指向真正的资源数据;

  2. Size,表示资源的大小;

  3. CodePage,代码页,一般为0;

  4. Reserved,保留字段,暂时没有意义

2.9.重定位表解析

在上述的例子中,重定位起到的作用就是将第二条指令从push 00402000修改为push 00872000,经过重定位修正之后,程序就能正常运行了。经过重定位操作之后,PE文件被加载到任意一个基地址都可以正常的运行。

重定位操作由操作系统提供的装载器来完成,那么装载器是如何知道哪些数据需要进行重定位操作呢?这就涉及到我们即将介绍的重定位表了。

需要进行重定位的数据通常都会放在PE文件名为.reloc的节区中,数据目录表的第六个成员指向重定位表。重定位表存放着许多类型为IMAGE_BASE_RELOCATION的结构,该结构体在WinNT.h头文件中的定义如下:

typedef struct _IMAGE_BASE_RELOCATION {

     DWORD   VirtualAddress;

     DWORD   SizeOfBlock;

     WORD    TypeOffset[1];

} IMAGE_BASE_RELOCATION;

该结构各个成员的介绍如下:

  1. VirtualAddress,RVA,指向当前这一组重定位数据开始的地址,各个重定项的值加上VirtualAddress计算出实际需要重定位操作的RVA值;
  2. SizeOfBlock,整个IMAGE_BASE_RELOCATION结构的大小,注意因为后面的TypeOffset数组的大小不固定,所以需要通过SizeOfBlock来指定整个结构的大小;
  3. TypeOffset,需要重定位的项,数组的项数可以通过(SizeOfBlock - 8)/2计算出来,数组每一项的大小占用2个字节,一共16位,其中高4位表示重定位的类型,低12位表示重定位的地址,低12位加上VirtualAddress得到实际上需要进行重定位操作的数据的RVA值;

常见的重定位类型有:

  1. IMAGE_REL_BASED_ABSOLUTE,值为0,没有具体意义,用于对齐使用;
  2. IMAGE_REL_BASED_HIGHLOW,值为3,表示指向的地址需要进行重定位修正;
  3. IMAGE_REL_BASED_DIR64,值为10,出现在64位的PE文件中,对指向的整个地址修正;

重定位表的结构如下图所示:
在这里插入图片描述

3.病毒分析

在线沙箱

网上有许多公开的在线沙箱,使用这些沙箱提供的服务,我们可以方便的观察一个程序的详细行为报告,进而判断一个程序大致的内部逻辑。

在线沙箱通常用于大致判定一个程序的行为是否安全,在逆向分析中,我们可以通过提交一个文件给沙箱程序来判断程序内部的大致逻辑,通过对沙箱报告的分析,有时候可以有效加快我们的逆向分析进程。

常见的在线沙箱包括但不限于:

VirusTotal:https://www.virustotal.com/gui/

VirSCAN:https://virscan.org/

微步云沙箱:https://s.threatbook.cn/

Joe Sandbox Cloud Basic:https://www.joesandbox.com/#windows

布谷鸟沙盒:https://sandbox.pikker.ee/

OPSWAT MetaDefender:https://metadefender.opswat.com/?lang=en

exe还可以用7z解压!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值