逆向工程(一):汇编、逆向工程基础篇

原文链接:逆向工程(一):汇编、逆向工程基础篇

*本文原创作者:VillanCh,已经原作者转载同意,未经过原作者本人同意,切勿私自转载


PREFACE

逆向标题图

非原作者内容:本篇转载过来的文章和原作者基本相同,在部分细节上会看起来舒服一点,图片大部分是原链接中的图片。自己看了感受是,还是蛮不错的。但是汇编深入的东西需要自己去买《汇编语言(第三版)》去看。

本系列文章将讲解逆向工程的各种知识,难度由浅入深。

汇编是逆向工程的基础,这篇文章讲解并不深入但是覆盖了你刚开始学习汇编需要了解的所有基础知识!汇编语言是一切程序的起点和终点,毕竟所有的高级语言都是建立在汇编基础之上的。在许多高级语言中我们都需要相对明确的语法,但是在汇编中,我们会使用一些单词缩写和数字来表达程序。


I. 单元、位和字节

BIT(位)电脑数据量的最小单元,可以是0或者1。例:00000001 = 1;00000010 = 2;00000011 = 3
BYTE(字节)一个字节包含8个位,所以一个字节最大值是255(0-255)。为了方便阅读,我们通常使用16进制来表示。
WORD(字)一个字由两个字节组成,共有16位。一个字的最大值是0FFFFh (或者是 65535d) (h代表16进制,d代表10进制)。
DOUBLE WORD(双字DWORD)一个双字包含两个字,共有32位。最大值为0FFFFFFFF (或者是 4294967295d)。
KILOBYTE(千字)千字节并不是1000个字节,而是1024 (32*32) 个字节。
MEGABYTE兆字节同样也不是一兆个字节,而是1024*1024=1,048,576 个字节

II. 寄存器

寄存器是计算机储存数据的“特别地方”。你可以把寄存器看作一个小盒子,我们可以在里面放很多东西:比如名字、数字、一段话……

如今Win+Intel CPU组成的计算机通常有9个32位寄存器 (w/o 标志寄存器)。它们是:

  • EAX: 累加器
  • EBX: 基址寄存器
  • ECX: 计数器
  • EDX: 数据寄存器
  • ESI: 源变址寄存器
  • EDI: 目的变址寄存器
  • EBP: 扩展基址指针寄存器
  • ESP: 栈指针寄存器
  • EIP: 指令指针寄存器

通常来说寄存器大小都是32位 (四个字节) 。它们可以储存值为从0-FFFFFFFF (无符号)的数据。起初大部分寄存器的名字都暗示了它们的功能,比如ECX=计数,但是现在你可以使用任意寄存器进行计数 (只有在一些自定义的部分,计数才必须用到ECX)。当我用到EAX、EBX、ECX、EDX、ESI和EDI这些寄存器时我才会详细解释其功能,所以我们先讲EBP、ESP、EIP。

  • EBP: EBP在栈中运用最广,刚开始没有什么需要特别注意的 ;)
  • ESP: ESP指向栈区域的栈顶位置。栈是一个存放即将会被用到的数据的地方, 你可以去搜索一下push/pop 指令了解更多栈知识。
  • EIP: EIP指向下一个将会被执行的指令。

* 还有一件值得注意的事:有一些寄存器是16位甚至8位的,它们是不能直接寻址的。*

包括:

32位寄存器16位寄存器8位寄存器
EAXAXAH/AL
EBXBXBH/BL
ESISI-
EDID-
EBPBP-
ESPSP-
EIPIP-

通常一个寄存器可以这样看:
寄存器可以这么看

由图可知,EAX是这个32位寄存器的名字,EAX的低16位部分被称作AX,AX又分为高8位的AH和低8位的AL两个独立寄存器。


1. 这些寄存器可以帮助我们区分大小:

AL and AH
BL and BH
CL and CH
DL and DH


2. 单字(16位)寄存器: 这些寄存器大小为一个字 (=2 字节 = 16 位)。一个单字寄存器包含两个单字节寄存器。我们通常根据它们的功能来区分它们。

1. 通用寄存器:

  • AX (单字=16位) = AH + AL -> 其中‘+’号并不代表把它们代数相加。AH和AL寄存器是相互独立的,只不过都是AX寄存器的一部分,所以你改变AH或AL (或者都改变) ,AX寄存器也会被改变。 -> ‘accumulator’(累加器):用于进行数学运算
  • BX -> ‘base’(基址寄存器):用来连接栈(之后会说明)
  • CX -> ‘counter’(计数器):
  • DX -> ‘data’(数据寄存器):大多数情况下用来存放数据
  • DI -> ‘destination index’(目的变址寄存器): 例如将一个字符串拷贝到DI
  • SI -> ‘source index’(源变址寄存器): 例如将一个字符串从SI拷贝

2. 索引寄存器(指针寄存器):

  • BP -> ‘base pointer’(基址指针寄存器):表示栈区域的基地址
  • SP -> ‘stack pointer’(栈指针寄存器):表示栈区域的栈顶地址

3. 段寄存器:

  • CS -> ‘code segment’(代码段寄存器):用于存放应用程序代码所在段的段基址(之后会说明)
  • DS -> ‘data segment’(数据段寄存器):用于存放数据段的段基址(以后会说明)
  • ES -> ‘extra segment’(附加段寄存器):用于存放程序使用的附加数据段的基地址
  • SS -> ‘stack segment’(栈段寄存器):用于存放栈段的段基址(以后会说明)

4. 指令指针寄存器:

IP -> ‘instruction pointer’(指令指针寄存器):指向下一个指令 ;)


3. 双字(32位)寄存器:

2 字= 4 字节= 32 位, EAX、EBX、ECX、EDX、EDI……

如果16位寄存器前面加了‘E’,就代表它们是32位寄存器。例如,AX=16位,EAX=32位。


III. 标志寄存器

标志寄存器代表某种状态。在32位CPU中有32个不同的标志寄存器,不过不用担心,我们只关心其中的3个:ZFOFCF。在逆向工程中,你了解了标志寄存器就能知道程序在这一步是否会跳转,标志寄存器就是一个标志,只能是0或者1,它们决定了是否要执行某个指令。

Z-Flag(零标志):

ZF是破解中用得最多的寄存器(通常情况下占了90%),它可以设成0或者1。若上一个运算结果为0,则其值为1,否则其值为0。(你可能会问为什么‘CMP’可以操作ZF寄存器,这是因为该指令在做比较操作(等于、不等于),那什么时候结果是0什么时候是1呢?待会再说)

The O-Flag(溢出标志):

OF寄存器在逆向工程中大概占了4%,当上一步操作改变了某寄存器的最高有效位时,OF寄存器会被设置成1。例如:EAX的值为7FFFFFFFF,如果你此时再给EAX加1,OF寄存器就会被设置成1,因为此时EAX寄存器的最高有效位改变了(你可以使用电脑自带计算器将这个16进制转化成2进制看看)。还有当上一步操作产生溢出时(即算术运算超出了有符号数的表示范围),OF寄存器也会被设置成1。

The C-Flag(进位标志):

进位寄存器的使用大概占了1%,如果产生了溢出,就会被设置成1。例,假如某寄存器值为FFFFFFFF,再加上1就会产生溢出,你可以用电脑自带的计算器尝试。


IV. 段偏移

内存中的一个段储存了指令(CS)数据(DS)堆栈(SS)或者其他段(ES)。每个段都有一个偏移量,在32位应用程序下,这些偏移量由 00000000 到 FFFFFFFF。段和偏移量的标准形式如下:

段:偏移量 = 把它们放在一起就是内存中一个具体的地址。

可以这样看:

一个段是一本书的某一页:偏移量是一页的某一行


V. 栈

栈是内存里可以存放稍后会用到的东西的地方。可以把它看作一个箱子里的一摞书,最后一本放进去的永远是最先出来的。或者把栈看作一个放纸的盒子,盒子是栈,而每一张纸就代表了一个内存地址。总之记住这个规则:最后放的纸最先被拿出来。push命令就是向栈中压入数据,pop命令就是从栈中取出最后放入的数据并且把它存进具体的寄存器中。


VI. 指令 (字母表排序)

请注意,所有的值通常是以16进制形式储存的。

大部分指令有两个操作符,例如:

add EAX, EBX

有些是一个操作符,例如:

not EAX

还有一些是三个操作符,例如:

IMUL EAX、EDX、64

如果你使用 DWORD PTR [XXX]就表示使用了内存中偏移量为[XXX]的数据。注意:字节在内存中储存方式是倒过来的(Win+Intel的电脑上大部分采用”小端法”WORD PTR [XXX](双字节)和 BYTE PTR [XXX](单字节)也都遵循这一规定)。

大部分有两个操作符的指令都是以下这些形式(以add指令举例):

add eax,ebx                          ;; 寄存器, 寄存器
add eax,123                          ;; 寄存器, 数值
add eax,dword ptr [404000]           ;; 寄存器, Dword  指针 [数值]
add eax,dword ptr [eax]              ;; 寄存器, Dword  指针 [寄存器值]
add eax,dword ptr [eax+00404000]     ;; 寄存器, Dword  指针 [寄存器值+数值]
add dword ptr [404000],eax           ;; Dword 指针[数值], 寄存器
add dword ptr [404000],123           ;; Dword 指针[数值], 数值
add dword ptr [eax],eax              ;; Dword 指针[寄存器值], 寄存器
add dword ptr [eax],123              ;; Dword 指针[寄存器值], 数值
add dword ptr [eax+404000],eax       ;; Dword 指针[寄存器值+数值], 寄存器
add dword ptr [eax+404000],123       ;; Dword 指针[寄存器值+数值], 数值

ADD(加)

语法:

ADD 被加数, 加数

加法指令将一个数值加在一个寄存器上或者一个内存地址上。

ADD eax,123   ;; eax=eax+123;

加法指令对ZF、OF、CF都会有影响。

AND (逻辑与)

语法:

and 目标数, 原数

AND运算对两个数进行逻辑与运算。

AND指令会清空OF,CF标记,设置ZF标记。

为了更好地理解AND,这里有两个二进制数:

1001010110
0101001101

如果对它们进行AND运算,结果是

0001000100

即同真为真(1),否则为假(0),自己可以用计算器进行验证。

CALL (调用)

语法:

call something

CALL指令将当前的相对地址(IP)压入栈中,并且调用CALL 后的子程序

CALL 可以这样使用:

CALL 404000                ;; 最常见: CALL 地址
CALL EAX                   ;; CALL 寄存器 - 如果寄存器存的值为404000,那就等同于第一种情况
CALL DWORD PTR [EAX]       ;; CALL [EAX]偏移量所指向的地址
CALL DWORD PTR [EAX+5]     ;; CALL [EAX+5]偏移量所指向的地址

CDQ

语法:

CDQ

CDQ指令第一次出现时通常不好理解。它通常出现在除法前面,作用是将EDX的所有位变成EAX最高位的值,

比如当EAX>=80000000H时,其二进制最高位为1,则EDX被32位全赋值为1,即FFFFFFFF

EAX<80000000H,则其二进制最高位为0,EDX为00000000

然后将EDX:EAX组成新数(64位):FFFFFFFF 80000000

CMP (比较)

语法:

CMP 目标数, 原数

CMP指令比较两个值并且标记CF、OF、ZF:

CMP     EAX, EBX              ;; 比较eaxebx是否相等,如果相等就设置ZF为1
CMP     EAX,[404000]          ;; 比较eax和偏移量为[404000]的值是否相等
CMP     [404000],EAX          ;; 比较[404000]是否与eax相等

DEC (自减)

语法:

DEC something

dec用来自减1,相当于c中的–

dec可以有以下使用方式:

dec eax                             ;; eax自减1
dec [eax]                           ;; 偏移量为eax的值自减1
dec [401000]                        ;; 偏移量为401000的值自减1
dec [eax+401000]                    ;; 偏移量为eax+401000的值自减1

dec指令可以标记ZF、OF

DIV (除)

语法:

DIV 除数

DIV指令用来将EAX除以除数(无符号除法),被除数通常是EAX,结果也储存在EAX中,而被除数对除数取的模存在除数中。

例如:

mov eax,64                      ;; EAX = 64h = 100
mov ecx,9                       ;; ECX = 9
div ecx                         ;; EAX除以ECX

在除法之后 EAX = 100/9 = 0B(十进制:11) 并且 ECX = 100 MOD 9 = 1

div指令可以标记CF、OF、ZF

IDIV (整除)

语法:

IDIV 除数

IDIV执行方式同div一样,不过IDIV是有符号的除法

idiv指令可以标记CF、OC、ZF

IMUL (整乘)

语法:

IMUL 数值
IMUL 目标寄存器,数值,数值
IMUL 目标寄存器,数值

IMUL指令可以把让EAX乘上一个数(INUL 数值)或者让两个数值相乘并把乘积放在目标寄存器中(IMUL 目标寄存器, 数值,数值)或者将目标寄存器乘上某数值(IMUL 目标寄存器, 数值)

如果乘积太大目标寄存器装不下,那OF、CF都会被标记,ZF也会被标记

INC (自加)

语法:

INC something

INC同DEC相反,它是将值加1

INC指令可以标记ZF、OF

INT

语法:

INT 目标数

INT 的目标数必须是产生一个整数(例如:int 21h),类似于call调用函数,INT指令是调用程序对硬件控制,不同的值对应着不同的功能。

具体参照硬件说明书。

JUMPS

这些都是最重要的跳转指令和触发条件(重要用*标记,最重要用**标记):

指令条件条件
JA*如果大于就跳转(无符号)CF=0 and ZF=0
JAE如果大于或等于就跳转(无符号)CF=0
JB*如果小于就跳转(无符号)CF=1
JBE如果小于或等于就跳转(无符号)CF=1 or ZF=1
JC如果CF被标记就了跳转CF=1
JCXZ如果CX等于0就跳转CX=0
JE**如果相等就跳转ZF=1
JECXZ如果ECX等于0就跳转ECX=0
JG*如果大于就跳转(有符号)ZF=0 and SF=OF (SF = Sign Flag)
JGE*如果大于或等于就跳转(有符号)SF=OF
JL*如果小于就跳转(有符号)SF != OF (!= is not)
JLE*如果小于或等于就跳转(有符号ZF=1 and OF != OF
JMP**跳转强制跳转
JNA如果不大于就跳转(无符号)CF=1 or ZF=1
JNAE如果不大于等于就跳转(无符号)CF=1
JNB如果不小于就跳转(无符号)CF=0
JNBE如果不小于等于就跳转(无符号)CF=0 and ZF=0
JNC如果CF未被标记就跳转CF=0
JNE**如果不等于就跳转ZF=0
JNG如果不大于就跳转(有符号)ZF=1 or SF!=OF
JNGE如果不大于等于就跳转(有符号)SF!=OF
JNL如果不小于就跳转(有符号)SF=OF
JNLE如果不小于等于就跳转(有符号)ZF=0 and SF=OF
JNO如果OF未被标记就跳转OF=0
JNP如果PF未被标记就跳转PF=0
JNS如果SF未被标记就跳转SF=0
JNZ如果不等于0就跳转ZF=0
JO如果OF被标记就跳转OF=1
JP如果PF被标记就跳转PF=1
JPE如果是偶数就跳转PF=1
JPO如果是奇数就跳转PF=0
JS如果SF被标记就跳转SF=1
JZ如果等于0就跳转ZF=1

LEA (有效地址传送)

语法:

LEA 目的数,源数

LEA可以看成和MOV差不多的指令LEA ,它本身的功能并没有被太广泛的使用,反而广泛运用在快速乘法中:

lea eax,dword ptr [4*ecx+ebx]       ;; 将eax赋值为 4*ecx+ebx

MOV (传送)

语法:

MOV 目的数,源数

这是一个很简单的指令,MOV指令将源数赋值给目的数,并且源数值保持不变

这里有一些MOV的变形:

movs edi,esi
movsb edi,esi
movsw edi,esi
movsd edi,esi       ;; 这些变形能将ESI指向的内容传送到EDI指向的内容中去

MOVZX:MOVZX扩展单字节或单字为双字节或双字并且用0填充剩余部分(通俗来说就是将源数取出置于目的数,其他位用0填充)

MUL(乘法)

语法:

MUL 数值

这个指令同IMUL一样,不过MUL可以乘无符号数。

NOP (无操作)

语法:

NOP

这个指令说明不做任何事!!

所以它在逆向中运用范围最广

OR (逻辑或)

语法:

OR 目的数,源数

OR指令对两个值进行逻辑或运算

这个指令会清空OF、CF标记,设置ZF标记

为了更好的理解OR,思考下面二进制串:

1001010110
0101001101

如果对它们进行逻辑与运算,结果将是

1101011111

只有当两边同为0时其结果为0,否则就为1。你可以用计算器尝试计算。希望你能理解为什么,最好自己动手算一算

POP

语法:

POP 目的地址

POP指令将栈顶第一个字传送到目的地址。 每次POP后,ESP(栈指针寄存器)都会增加以指向新栈顶

PUSH

语法:

PUSH 值

PUSH是POP的相反操作,它将一个值压入栈并且减小栈顶指针值以指向新栈顶。

REP/REPE/REPZ/REPNE/REPNZ

语法:

REP/REPE/REPZ/REPNE/REPNZ ins

重复上面的指令:直到CX=0。ins必须是一个操作符,比如CMPS、INS、LODS、MOVS、OUTS、SCAS 或 STOS

RET (返回)

语法:

RET

RET digit ;; 在返回前会清理栈

RET指令的功能是从一个代码区域中退出到调用CALL的指令处。

SUB (减)

语法:

SUB 目的数,源数

SUB与ADD相反,它将源数减去目的数,并将结果储存在目的数中

SUB可以标记ZF、OF、CF

TEST

语法:

TEST 操作符、操作符

这个指令99%都是用于”TEST EAX, EAX”,它执行与AND相同的功能,但是并不储存数据。如果EAX=0就会标记ZF,如果EAX不是0,就会清空ZF

XOR

语法:

XOR 目的数,源数

XOR指令对两个数进行异或操作

这个指令清空OF、CF,但会标记ZF

为了更好的理解,思考下面的二进制串:

1001010110
0101001101

如果异或它们,结果将是:

1100011011

如果两个值相等,则结果为0,否则为1,你可以使用计算器验算。

很多情况下我们会使用”XOR EAX, EAX”,这个操作是将EAX赋值为0,因为当一个值异或其自身,就过都是0。你最好自己动手尝试下,这样可以帮助你理解得更好。


VII. 逻辑操作符

下面都是通常的逻辑操作符:
逻辑操作符

好了,到这里逆向工程所需要的汇编基础已经补充完毕,下一篇再见!

*原创作者:VillanCh,本文属FreeBuf原创奖励计划文章,未经作者本
人及FreeBuf许可,切勿私自转载

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
稀缺资源 iOS应用逆向工程: 分析与实战是iOS应用逆向工程方面的权威著作,三位作者都是iOS领域内的专家,拥有扎实的理论知识和丰富的实践经验。本书内容以工具+代码的形式全面、系统地展开知识点,由浅入深,图文并茂地带着读者一步步探索常规iOS App之外的世界。 《iOS应用逆向工程:分析与实战》分为四大部分,分别是概念、工具、理论和实战。前三部分介绍iOS逆向分析领域的背景、知识体系,以及相应的工具集、理论知识;第四部分则通过4个实际案例来将前面的知识以实战的方式展开。第一部分为概念,简单介绍iOS逆向分析的概念以及iOS平台系统架构。第二部分为工具,介绍一系列基于Mac和iOS平台的配套工具,并且重点讲解其中的class-dump、Theos、Reveal、IDA、GDB等5个工具的使用方法,前3个侧重于使用,后2个侧重于分析。第三部分为理论,主要讲述iOS逆向/越狱方向的进阶必备理论知识。第四部分为实战,通过对3个App Store App及1个系统App进行逆向分析的实战操作,让读者能够了解并同步实践已掌握的知识。 iOS应用逆向工程:分析与实战 目录: 推荐序一 推荐序二 自序 前言 第一部分 概念 第1 章  iOS 逆向工程简介 2 1.1  iOS 软件逆向工程的要求 2 1.2  iOS 软件逆向工程的作用 2 1.2.1  与安全相关的iOS 逆向工程 4 1.2.2  与开发相关的iOS 逆向工程 5 1.3  iOS 软件逆向工程的一般过程 6 1.3.1  系统分析 7 1.3.2  代码分析 7 1.4  iOS 软件逆向工程用到的工具 8 1.4.1  监测工具 8 1.4.2  开发工具 9 1.4.3  反编译器 9 1.4.4  调试器 10 1.5  小结 11 第2 章 越狱iOS 平台简介 12 2.1  iOS 系统架构 12 2.1.1  iOS 目录结构 13 2.1.2  iOS 文件权限 15 2.2  iOS 程序类型 16 2.2.1  Application 16 2.2.2  Dynamic Library 19 2.2.3  Daemon 19 2.3  小结 20 第二部分 工具 第3 章 Mac 工具集 22 3.1  class-dump 22 3.1.1  class-dump 介绍及下载 22 3.1.2  class-dump 使用演示 23 3.1.3  关于class-dump 的补充说明 25 3.2  Theos 25 3.2.1  Theos 简介 25 3.2.2  Theos 安装及编译 26 3.2.3  Theos 用法简介 28 3.2.4  Theos 开发tweak 示例 47 3.3  Reveal 49 3.3.1  Reveal 简介 49 3.3.2  Reveal 安装及功能扩展 50 3.4  IDA 55 3.4.1  IDA 简介 55 3.4.2  IDA 使用说明 56 3.4.3  IDA 分析示例 65 3.5  其他工具 68 3.5.1  iTools 68 3.5.2  dyld_decache 69 3.5.3  MesaSQLite 69 3.6  小结 70 第4 章 iOS 工具集 71 4.1  SBSettings 71 4.2  MobileSubstrate 72 4.3  OpenSSH 73 4.4  GDB 74 4.4.1  GDB 简介 74 4.4.2  GDB 的使用说明 74 4.5  Cycript 85 4.6  其他常用工具 88 4.6.1  BigBoss RecommendedTools 88 4.6.2  AppCrackr 88 4.6.3  iFile 89 4.6.4  MobileTerminal 89 4.6.5  Vi IMproved 90 4.6.6  SQLite 90 4.6.7  top 91 4.6.8  syslogd 92 4.7  小结 92 第三部分 理论 第5 章  Objective-C 相关的iOS逆向理论基础 94 5.1  tweak 的作用原理 94 5.1.1  Objective-C 语言的特性 94 5.1.2  MobileSubstrate 96 5.2  tweak 的编写套路 97 5.2.1  灵感的来源 98 5.2.2  分析文件,寻找切入点 99 5.2.3  定位目标函数 102 5.2.4  测试函数功

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值