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位后的值
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 储存有符号字节
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 */
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 */
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; */
当特定条件满足时,借助条件指令, 通过跳转(分支)或执行某些特定指令来控制程序的流动方向。相关条件被描述为CPSR寄存器中的特定位的状态,这些位根据指令计算后的结果实时改变。比如,如果我们比较两个数并且他们相等,就将零标志位置位(Z=1) ,因为在系统底层发生了a-b=0。在这个例子里两个数是相等的,但如果第一个数字比第二个大,会得出大于结论。而相反的情况下得出小于结论。当然还有很多其他的条件,比如小于等于(LE),大于等于(GE)等等。
下表列出了可能的条件指令,他们的含义以及被检测的状态标志位
b分支指令 简单地跳向一个函数
bl分支连接指令 将(PC+4)保存到LR中并跳转到函数
BX(分支切换指令)和BLX(分支连接切换指令)
a) 和B/BL+交换指令集相同( ARM <-> Thumb )
b) 需要用寄存器作为第一操作数:BX/BLX+具体的寄存器
BX/BLX用来从ARM指令集切换到Thumb指令集
PE头分为5个部分,如下表
在DOS-根之后是一个32位的签名以及魔数0x00004550 (IMAGE_NT_SIGNATURE)(意为“NT签名”,也就是PE签名;十六进制数45和50分别代表ASCII码字母E和P----译者注)。
之后是文件头(按COFF格式),用来说明该二进制文件将运行在何种机器之上、分几个区段、链接的时间、是可执行文件还是DLL、等等。(本文中可执行文件和DLL文件的区别在于:DLL文件不能被启动,但能被别的二进制文件使用,而一个二进制文件则不能链接到另一个可执行文件。)
在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结构体的大小为字节,而e_lfanew位于该结构的最后四字节,所以e_lfanew对应第61~四个字节的数据(下标索引为60~63)。
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;
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结构各个成员字段的含义如下:
Magic,表明是ROM映像还是普通的可执行映像,普通的可执行文件的值为0x010B,PE32+可执行文件的值为0x020B(PE32+即位的PE文件,这种文件只能运行在位的Windows操作系统下);
MajorLinkerVersion,链接器的主版本号;
MinorLinkerVersion,链接器的次版本号;
SizeOfCode,代码区块的大小,通常而言可执行文件仅有一个代码区块.text,所以通常就是.text区块的大小;
SizeOfInitializedData,已初始化数据块的大小,不作研究;
SizeOfUninitializedData,未初始化数据块的大小,不作研究;
SectionAlignment,当PE文件装载到内存时区块的对齐大小,假设.text区块的大小为0x7748,而SectionAlignment的大小为0x1000,那么对齐后的大小为0x8000字节;
FileAlignment,磁盘上PE文件中区块的对齐大小,对齐方式类似SectionAlignment;
MajorOperatingSystemVersion,要求的操作系统最低的主版本号;
MinorOperatingSystemVersion,要求的操作系统最低的次版本号;
MajorImageVersion,PE文件本身的主版本号;
MinorImageVersion,PE文件本身的次版本号;
MajorSubsystemVersion,要求的子系统最低的主版本号;
MinorSubsystemVersion,要求的子系统最低的次版本号;
Win32VersionValue,保留字段,通常为0;
SizeOfImage,PE文件被装载到内存空间后总的大小,指从ImageBase到最后一个区块的大小;
SizeOfHeaders,Dos头、DosStub、PE头以及区块头的总大小,并进行FileAlignment对齐后的大小;
CheckSum,校验和,一般的EXE文件通常为0,对重要的系统驱动程序而言比较有意义;
Subsystem,子系统,EXE最常用的子系统类型有console以及windows,分别对应控制台应用程序以及GUI应用程序;
DllCharacteristics,包含有DEP以及ASLR相关的属性位;
SizeOfStackReserve,不作研究;
SizeOfStackCommit,不作研究;
SizeOfHeapReserve,不作研究;
SizeOfHeapCommit,不作研究;
LoaderFlags,与调试相关,不作研究;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
紧跟在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;
其中各个字段的介绍如下:
Name,为对应节区的名字,其中IMAGE_SIZEOF_SHORT_NAME的值固定为8。如果名字的长度小于8,则以NULL字符结束;如果名字的长度等于8,则没有NULL字符,因为数组长度为8。节区的名字通常以点号开头,如.text、.data等,通常而言,这个名字可以随便修改。
VirtualSize,在未对齐的情况下,区块所有数据的大小。
VirtualAddress,区块被装载到内存时的RVA,这个值总是SectionAlignment的整数倍。
SizeOfRawData,区块数据在磁盘文件中按照FileAlignment对齐后的大小。
PointerToRelocations、PointerToLinenumbers、NumberOfRelocations、NumberOfLinenumbers:不是很重要的字段,这里不作研究。
Characteristics,区块的属性值,表明区块的可读、可写、可执行等相关属性。
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
关于IID结构体中各个成员的含义介绍如下:
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;
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;
Characteristics,表示输出属性,这个值当前没有意义,总是设置为0;
TimeDateStamp,输出表创建的时间(格林威治标准时间);
MajorVersion,输出表的主版本号,这个值当前没有意义,总是设置为0;
MinorVersion,输出表的次版本号,这个值当前没有意义,总是设置为0;
Name,RVA,模块的真实名字,指向一个ASCII字符串,这个字符串是与这些输出函数关联的DLL的名字(如User32.dll);
Base,输出表的起始序数值;
NumberOfFunctions,输出函数的个数;
NumberOfNames,以名字输出的函数的个数;
AddressOfNames,RVA,指向包含以名字方式输出的函数的ASCII字符串名字的RVA数组;
AddressOfNameOrdinals,RVA,指向每个以名字方式输出的函数对应在AddressOfFunctions指向的数组中的索引;
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结构体中各个字段的含义如下:
Characteristics,资源的属性标志,当前没有意义,它的值总是设置为0;
TimeDateStamp,资源数据被资源编译器创建的时间;
Major Version,资源的主版本号,通常为0;
Minor Version,资源的次版本号,通常为0;
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个字节,结构体的成员的介绍如下:
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结构体成员的介绍如下:
OffsetToData,这是一个RVA,指向真正的资源数据;
Size,表示资源的大小;
CodePage,代码页,一般为0;
Reserved,保留字段,暂时没有意义
重定位操作由操作系统提供的装载器来完成,那么装载器是如何知道哪些数据需要进行重定位操作呢?这就涉及到我们即将介绍的重定位表了。
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
该结构各个成员的介绍如下:
常见的重定位类型有:
重定位表的结构如下图所示:
在线沙箱
网上有许多公开的在线沙箱,使用这些沙箱提供的服务,我们可以方便的观察一个程序的详细行为报告,进而判断一个程序大致的内部逻辑。
在线沙箱通常用于大致判定一个程序的行为是否安全,在逆向分析中,我们可以通过提交一个文件给沙箱程序来判断程序内部的大致逻辑,通过对沙箱报告的分析,有时候可以有效加快我们的逆向分析进程。
常见的在线沙箱包括但不限于:
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解压!
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 99spj.com 版权所有 湘ICP备2022005869号-5
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务