汇编指令学习总结_第1页
汇编指令学习总结_第2页
汇编指令学习总结_第3页
汇编指令学习总结_第4页
汇编指令学习总结_第5页
已阅读5页,还剩16页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

看过破解教程,都知道test,cmp是比较关键,可是我一直不清楚它们究竟是怎么比较的,最后下决心找了很多资料,和大家一起把它们弄清楚.首先看看:状态寄存器(即标志寄存器)PSW(ProgramFlag)程序状态字(即标志)寄存器,是一个16位寄存器,由条件码标志(flag)和控制标志构成,

如下所示:1514131211109

8

7

6

5

4

3

2

1

0

OFDFIFTFSFZFAFPFCF条件码:

①OF(OverflowFlag)溢出标志,溢出时为1,否则置0.标明一个溢出了的计算,如:结构和目标不匹配.

②SF(SignFlag)符号标志,结果为负时置1,否则置0.

③ZF(ZeroFlag)零标志,运算结果为0时置1,否则置0.

④CF(CarryFlag)进位标志,进位时置1,否则置0.注意:Carry标志中存放计算后最右的位.

⑤AF(AuxiliarycarryFlag)辅助进位标志,记录运算时第3位(半个字节)产生的进位置。

有进位时1,否则置0.

⑥PF(ParityFlag)奇偶标志.结果操作数中1的个数为偶数时置1,否则置0.控制标志位:

⑦DF(DirectionFlag)方向标志,在串处理指令中控制信息的方向。

⑧IF(InterruptFlag)中断标志。

⑨TF(TrapFlag)陷井标志。

为举例方便说一下jnz和jz

测试条件

JZ

ZF=1

JNZ

ZF=0

即Jz=jumpifzero(结果为0则设置ZF零标志为1,跳转)

Jnz=jumpifnotzero好,接着来看test和cmp*******************************************************************************

test属于逻辑运算指令功能:执行BIT与BIT之间的逻辑运算

测试(两操作数作与运算,仅修改标志位,不回送结果).

Test对两个参数(目标,源)执行AND逻辑操作,并根据结果设置标志寄存器,结果本身不会保存。ESTAX,BX与ANDAX,BX命令有相同效果语法:TESTr/m,r/m/data

影响标志:C,O,P,Z,S(其中C与O两个标志会被设为0)运用举例:

1.Test用来测试一个位,例如寄存器:testeax,100b;

b后缀意为二进制

jnz

******;

如果eax右数第三个位为1,jnz将会跳转我是这样想的,jnz跳转的条件是ZF=0,ZF=0意味着ZF(零标志)没被置位,即逻辑与结果为1.2.Test的一个非常普遍的用法是用来测试一方寄存器是否为空:testecx,ecx

jzsomewhere如果ecx为零,设置ZF零标志为1,Jz跳转*******************************************************************************

CMP属于算术运算指令功能:比较两个值(寄存器,内存,直接数值)

语法:CMPr/m,r/m/data

标志位:C,P,A,Z,OCMP比较.(两操作数作减法,仅修改标志位,不回送结果).

cmp实际上是只设置标志不保存结构的减法,并设置Z-flag(零标志).

零标志很像carry,也是内部标志寄存器的一位.例如:

Cmpeax,2;

如果eax-2=0即eax=2就设置零标志为1

Jz****;

如果设置了零标志就跳转

*******************************************************************************

我得出的结论

test逻辑与运算结果为零,就把ZF(零标志)置1;

cmp算术减法运算结果为零,就把ZF(零标志)置1.TEST

-

Test

For

Bit

Pattern

//位测试指令

Usage:

TEST

dest,src

//用法:TEST

dest,src

Modifies

flags:

CF

OF

PF

SF

ZF

(AF

undefined)

//受影响的标志位有:

//你要注意的是这一部位才对。你上面的指令test

eax,eax后面跟着的是jnz指令,jnz指令作用于零标志位的,若零标志位不为零则跳,所以test

eax,eax是测试eax是否为零的指令,若eax有数据,不管是什么数据(111也好,101也好),jnz都会跳。

//更新一下,若eax有数据,不管是111也好,222也好,只要他们两个操作数作逻辑与运算之后,结果不为零,则jnz都会跳。而jz则不跳。

CF

//进位标志,主要作用于JC/JNC指令。

OF

//溢出标志,主要作用于JO/JNO指令

PF

//奇偶标志,主要作用于JP/JPE/JNP/JPO指令

SF

//符号标志,主要作用于JS/JNS指令

ZF

//零标志,主要作用于JZ/JE/JNZ/JNE指令

AF

//辅助进位标志,不过未定义。

Performs

a

logical

AND

of

the

two

operands

updating

the

flags

//执行两个操作数的逻辑与运算,并更新标志位。

register

without

saving

the

result.

//该操作只修改标志位,不保留运算结果值。已经有经验的知道TEST指令是对寄存器或者说对两个操作数进行逻辑与,然后影响标志寄存器。

最常见的指令是TEST

EAX,EAX,

但是如何逆向出TEST指令呢?

对我们新手来说正向经验不足是个难度。

开始的时候

我也不知道从何下手得到这

TEST指令,

我就随便看Object

Pascal官方中文版的书,

然后反出来看,

于是渐渐的TEST指令就出来了。

而实际要逆出的指令是这一组,

不妨我们都逆逆看。

0040899C

|.

8B1D

4CD24000

mov

ebx,

dword

ptr

[40D24C]

;

NoPacker.0040D164

004089A2

|.

8B5B

18

mov

ebx,

dword

ptr

[ebx+18]

004089A5

|.

85DB

test

ebx,

ebx

004089A7

|.

74

12

je

short

004089BB

004089A9

|.

A1

4CD24000

mov

eax,

dword

ptr

[40D24C]

004089AE

|.

E8

DDB9FFFF

call

<jmp.&kernel32.GetACP>

;

[GetACP

004089B3

|.

3BD8

cmp

ebx,

eax

004089B5

|.

0F85

D2030000

jnz

00408D8D

004089BB

|>

BA

9CE94000

mov

edx,

0040E99C

一、

(1)函数调用

00407CDC

|.

B8

FC7C4000

mov

eax,

00407CFC

;

ASCII

"0

:"

00407CE1

|.

E8

8AFFFFFF

call

00407C70

00407CE6

|.

B8

087D4000

mov

eax,

00407D08

;

ASCII

"Hello

World"

00407CEB

|.

E8

90FFFFFF

call

00407C80

(2)另外一种形式的

MessageBox(0,

PChar(varb),

PChar(varb2),

0);

00407CBF

|.

6A

00

push

0

00407CC1

|.

8B45

F8

mov

eax,

dword

ptr

[ebp-8]

00407CC4

|.

E8

77BCFFFF

call

00403940

00407CC9

|.

50

push

eax

00407CCA

|.

8B45

FC

mov

eax,

dword

ptr

[ebp-4]

00407CCD

|.

E8

6EBCFFFF

call

00403940

00407CD2

|.

50

push

eax

;

|Text

00407CD3

|.

6A

00

push

0

;

|hOwner

=

NULL

00407CD5

|.

E8

8EC8FFFF

call

<jmp.&user32.MessageBoxA>

;

\MessageBoxA

(3)两个整型参数

func(90,

90);

00407D3A

|.

BA

5A000000

mov

edx,

5A

00407D3F

|.

B8

5A000000

mov

eax,

5A

00407D44

|.

E8

47FFFFFF

call

00407C90

(4)将两个值相加再转为string

tmpStr:=IntToStr(a

+

b);

00407CB8

|.

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

00407CBB

|.

8D041E

lea

eax,

dword

ptr

[esi+ebx]

00407CBE

|.

E8

01D6FFFF

call

004052C4

(5)值相加tBuf

:=

Integer('A');

tmpStr:=IntToStr(a

+

b

+

tBuf);

00407CB9

|.

BB

41000000

mov

ebx,

41

00407CBE

|.

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

00407CC1

|.

8D0437

lea

eax,

dword

ptr

[edi+esi]

00407CC4

|.

03C3

add

eax,

ebx

00407CC6

|.

E8

F9D5FFFF

call

004052C4

(6)获取一个函数指针

00407CB9

|.

A1

A0974000

mov

eax,

dword

ptr

[4097A0]

00407CBE

|.

A3

9C974000

mov

dword

ptr

[40979C],

eax

(7)来回传递

F

:=

Func(P);

Func(P)

:=

F;

00407CB9

|.

A1

A0974000

mov

eax,

dword

ptr

[4097A0]

00407CBE

|.

A3

9C974000

mov

dword

ptr

[40979C],

eax

00407CC3

|.

A1

9C974000

mov

eax,

dword

ptr

[40979C]

00407CC8

|.

A3

A0974000

mov

dword

ptr

[4097A0],

eax

(8)

相同的反汇编结果

@F

:=

P;

F

:=

Func(P);

00407CB9

|.

A1

A0974000

mov

eax,

dword

ptr

[4097A0]

00407CBE

|.

A3

9C974000

mov

dword

ptr

[40979C],

eax

00407CCD

|.

A1

A0974000

mov

eax,

dword

ptr

[4097A0]

00407CD2

|.

A3

9C974000

mov

dword

ptr

[40979C],

eax

P

:=

@F;

Func(P)

:=

F;也是相同的

(9)调用

N

:=

F(N);

00407CDC

|.

A1

A4974000

mov

eax,

dword

ptr

[4097A4]

00407CE1

|.

FF15

9C974000

call

dword

ptr

[40979C]

00407CE7

|.

A3

A4974000

mov

dword

ptr

[4097A4],

eax

(10)

N

:=

Func(P)(N);

00407CEC

|.

A1

A4974000

mov

eax,

dword

ptr

[4097A4]

00407CF1

|.

FF15

A0974000

call

dword

ptr

[4097A0]

00407CF7

|.

A3

A4974000

mov

dword

ptr

[4097A4],

eax

(11)

00407CB9

|.

BB

03000000

mov

ebx,

3

;

I

:=

3

00407CBE

|.

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

;得到tmpStr地址

00407CC1

|.

8D0437

lea

eax,

dword

ptr

[edi+esi]

;edi+esi就是90+90

00407CC4

|.

03C3

add

eax,

ebx

;

I

+

edi

+

esi值

00407CC6

|.

E8

F9D5FFFF

call

004052C4

;

存储在tmpStr内

(12)

00407CB9

|.

BB

03000000

mov

ebx,

3

;I

:=

3

00407CBE

|.

43

inc

ebx

;I

:=

I+1;

(13)当不自动为变量初始化时Delphi会自己给变量初始化,

形态是这样的。

//一般不是自己手动的临时变量要么是作为输入参数要么是作为中转存储。

00407C9B

|.

894D

F8

mov

dword

ptr

[ebp-8],

ecx

00407C9E

|.

894D

F4

mov

dword

ptr

[ebp-C],

ecx

00407CA1

|.

8955

FC

mov

dword

ptr

[ebp-4],

edx

(14)Delphi的临时栈空间是在每个函数的后面。

00407D1F

00

db

00

00407D20

.

FFFFFFFF

dd

FFFFFFFF

00407D24

.

14000000

dd

00000014

00407D28

.

4D

69

63

72

6F

73

>ascii

"Microsoft

vs

Bor"

00407D38

.

6C

61

6E

64

00

ascii

"land",0

00407D3D

00

db

00

00407D3E

00

db

00

00407D3F

00

db

00

如这段,

它在00407D1F之前存在一个函数将引用此段内某临时变量。

(15)

一个逻辑表达式

Done

:=

(I>=1)

and

(I<100);

00407CB6

|.

83FB

01

cmp

ebx,

1

(1)

00407CB9

|.

7C

05

jl

short

00407CC0

00407CBB

|.

83FB

64

cmp

ebx,

64

(100)

00407CBE

|.

7C

04

jl

short

00407CC4

00407CC0

|>

33C0

xor

eax,

eax

//清零

00407CC2

|.

EB

02

jmp

short

00407CC6

00407CC4

|>

B0

01

mov

al,

1

//清1

00407CC6

|>

8BD8

mov

ebx,

eax

(16)

sqr求平方,

sqrt开平方

I:=

Sqr(J);

00407CB3

|.

B9

64000000

mov

ecx,

64

00407CB8

|.

8BC1

mov

eax,

ecx

00407CBA

|.

F7E8

imul

eax

00407CBC

|.

8BD8

mov

ebx,

eax

(17)关键字

test终于被我再次搞出来了,

且是存在于循环之中。

00407CB3

|.

BB

64000000

mov

ebx,

64

00407CB8

|>

4B

/dec

ebx

00407CB9

|.

85DB

|test

ebx,

ebx

//这里的test

ebx

ebx正好合乎我的需求。

00407CBB

|.^

7F

FB

\jg

short

00407CB8

当然肯定还有各种test的情形,

不一定非要是循环。不过我们到是可以检测下各种

条件表达式下的循环。

[源]

I

:=

100;

while

I

>

0

do

begin

I:=

I-1;

end;

关于条件表达式中进行总结。

根据上面的循环由于

I

0

作比较,

所以一般用test指令作为检测指令。然后根据

标志寄存器状态作跳转。我根据此推测又写了一个循环,同样还是让另外一个新变量J和

0作比较。

[反]

00407CB3

|.

BB

64000000

mov

ebx,

64

;i

=

100

00407CB8

|.

B8

04000000

mov

eax,

4

;j

=

4;

00407CBD

|>

48

/dec

eax

;循环四次,没次减1

00407CBE

|.

85C0

|test

eax,

eax

00407CC0

|.^

75

FB

\jnz

short

00407CBD

00407CC2

|.

85DB

test

ebx,

ebx

00407CC4

|.

7E

05

jle

short

00407CCB

;(test后若ZF!=OF或ZF=1)转

;否则循环100次,每次减1

00407CC6

|>

4B

/dec

ebx

00407CC7

|.

85DB

|test

ebx,

ebx

00407CC9

|.^

7F

FB

\jg

short

00407CC6

00407CCB

|>

\8D55

FC

lea

edx,

dword

ptr

[ebp-4]

这是根据循环,我目的是想产生类似这样的代码。

0040899C

|.

8B1D

4CD24000

mov

ebx,

dword

ptr

[40D24C]

;

NoPacker.0040D164

004089A2

|.

8B5B

18

mov

ebx,

dword

ptr

[ebx+18]

004089A5

|.

85DB

test

ebx,

ebx

004089A7

|.

74

12

je

short

004089BB

004089A9

|.

A1

4CD24000

mov

eax,

dword

ptr

[40D24C]

004089AE

|.

E8

DDB9FFFF

call

<jmp.&kernel32.GetACP>

;

[GetACP

004089B3

|.

3BD8

cmp

ebx,

eax

004089B5

|.

0F85

D2030000

jnz

00408D8D

004089BB

|>

BA

9CE94000

mov

edx,

0040E99C

现在知道了如何产生

test指令,

而cmp指令则很容易产生,一般

逻辑或条件表达式

内又大于0的数就会使用cmp作减法,

然后比较,

test

cmp都是

将两个操作数进行

逻辑和算术运算,但是不修改寄存器值。

test负责逻辑判断影响标志寄存器。

cmp进行算术寄存器,也影响标志寄存器。

现在再次推理,

如果被比较的操作数是小于0,

或者

是个存储器操作数,或是个函数,

或是其他形式

该如何产生test指令呢?

如果

被比较的数是1或

-1会不会也产生

test指令呢。

下面这段是前面两个小循环

I

:=

100;

J

:=

4;

while

j

<>

0

do

begin;

J

:=

J

-

1;

end;

while

I

>

0

do

begin

I:=

I-1;

end;

//先面开始测试是否

1

-1等有关,

也可产生test指令。

第一次试验失败,看来跟1比较是不行的

00407CB3

|.

B8

64000000

mov

eax,

64

00407CB8

|>

48

/dec

eax

00407CB9

|.

83F8

01

|cmp

eax,

1

00407CBC

|.^

75

FA

\jnz

short

00407CB8

[源]

J

:=

100;

while

j

<>

1

do

begin;

J

:=

J

-

1;

end;

还有-1

同样也失败

00407CB3

|.

B8

64000000

mov

eax,

64

00407CB8

|>

48

/dec

eax

00407CB9

|.

83F8

FF

|cmp

eax,

-1

00407CBC

|.^

75

FA

\jnz

short

00407CB8

[源]

J

:=

100;

while

j

<>

1

do

begin;

J

:=

J

-

1;

end;

看来都不行,

不过现在知道和0比较可以了就可以了。那么再次推测,和常量0

或者

存储器0,

或者其他形式的,

如返回值0比较是否也要产生test指令呢?

先不管循环的形式,

如果将条件转移为if

then

else

...类的条件内和0比较是否也可以呢

00407CB3

|.

B8

64000000

mov

eax,

64

00407CB8

|.

85C0

test

eax,

eax

00407CBA

|.

7E

13

jle

short

00407CCF

00407CBC

|.

6A

00

push

0

;

/Style

=

MB_OK|MB_APPLMODAL

00407CBE

|.

68

3C7D4000

push

00407D3C

;

|Title

=

"Hello

World"

00407CC3

|.

68

3C7D4000

push

00407D3C

;

|Text

=

"Hello

World"

00407CC8

|.

6A

00

push

0

;

|hOwner

=

NULL

00407CCA

|.

E8

99C8FFFF

call

<jmp.&user32.MessageBoxA>

;

\MessageBoxA

00407CCF

|>

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

看来是可以的,

[源]

J

:=

100;

if

J

>

0

then

begin

MessageBox(0,

'Hello

World',

'Hello

World',

0);

end;

小结:

test是两两个寄存器作与运算,然后影响标志寄存器,然后后面的转移指令根据标志寄存器

来转。

我稍微改变了个符号后

00407CB3

|.

B8

64000000

mov

eax,

64

00407CB8

|.

85C0

test

eax,

eax

00407CBA

|.

7D

13

jge

short

00407CCF

00407CBC

|.

6A

00

push

0

;

/Style

=

MB_OK|MB_APPLMODAL

00407CBE

|.

68

3C7D4000

push

00407D3C

;

|Title

=

"Hello

World"

00407CC3

|.

68

3C7D4000

push

00407D3C

;

|Text

=

"Hello

World"

00407CC8

|.

6A

00

push

0

;

|hOwner

=

NULL

00407CCA

|.

E8

99C8FFFF

call

<jmp.&user32.MessageBoxA>

;

\MessageBoxA

[源]

J

:=

100;

if

J

<

0

then

begin

MessageBox(0,

'Hello

World',

'Hello

World',

0);

end;

则jle

被改为了jge,

jge要求

SF=OF,

由于test指令对eax进行了与运算,将全部标志寄存器清零了。

所以SF=OF,所以要转。好下面完成后面的测试,

当被比较的不是0,

是常量,是变量,是函数时

会产生什么代码。

(1).1常量

[源]

const

ZeroN

=

0;

...

J

:=

100;

if

J

<

ZeroN

then

begin

MessageBox(0,

'Hello

World',

'Hello

World',

0);

end;

[反]

00407CB3

|.

B8

64000000

mov

eax,

64

00407CB8

|.

85C0

test

eax,

eax

00407CBA

|.

7D

13

jge

short

00407CCF

00407CBC

|.

6A

00

push

0

;

/Style

=

MB_OK|MB_APPLMODAL

00407CBE

|.

68

3C7D4000

push

00407D3C

;

|Title

=

"Hello

World"

00407CC3

|.

68

3C7D4000

push

00407D3C

;

|Text

=

"Hello

World"

00407CC8

|.

6A

00

push

0

;

|hOwner

=

NULL

00407CCA

|.

E8

99C8FFFF

call

<jmp.&user32.MessageBoxA>

;

\MessageBoxA

00407CCF

|>

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

看来常量是可以的。那么存储器的呢。

存储器的就不行了

00407CB3

|.

B8

64000000

mov

eax,

64

;J

:=

100;

00407CB8

|.

33DB

xor

ebx,

ebx

;I

:=

0;

00407CBA

|.

3BD8

cmp

ebx,

eax

00407CBC

|.

7E

13

jle

short

00407CD1

00407CBE

|.

6A

00

push

0

;

/Style

=

MB_OK|MB_APPLMODAL

00407CC0

|.

68

407D4000

push

00407D40

;

|Title

=

"Hello

World"

00407CC5

|.

68

407D4000

push

00407D40

;

|Text

=

"Hello

World"

00407CCA

|.

6A

00

push

0

;

|hOwner

=

NULL

00407CCC

|.

E8

97C8FFFF

call

<jmp.&user32.MessageBoxA>

;

\MessageBoxA

00407CD1

|>

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

存储通过xor

进行了清零,然后用的cmp

做减法

然后判断跳。

还有一种就是函数的返回值了。当函数返回Boolean值或

指针

0

值时。

函数的返回值得也是不行的

00407CE0

|.

BB

64000000

mov

ebx,

64

00407CE5

|.

B8

5A000000

mov

eax,

5A

00407CEA

|.

E8

A1FFFFFF

call

00407C90

00407CEF

|.

3BD8

cmp

ebx,

eax

00407CF1

|.

7D

13

jge

short

00407D06

00407CF3

|.

6A

00

push

0

;

/Style

=

MB_OK|MB_APPLMODAL

00407CF5

|.

68

747D4000

push

00407D74

;

|Title

=

"Hello

World"

00407CFA

|.

68

747D4000

push

00407D74

;

|Text

=

"Hello

World"

00407CFF

|.

6A

00

push

0

;

|hOwner

=

NULL

00407D01

|.

E8

62C8FFFF

call

<jmp.&user32.MessageBoxA>

;

\MessageBoxA

00407D06

|>

8D55

FC

lea

edx,

dword

ptr

[ebp-4]

但是我们随便Google一下test指令,

找到的破解的test的形式有如下几种等:

1)TEST

EAX,EAX

2)TEST

EBX,EBX

3)TEST

ESI,ESI

4)TEST

EDI,EDI

这种直接逻辑与寄存器的比较多。

含存储器的呢?

5)test

al,1

含一个立即数

6)test

ebp,esi

总之还有其他形式的test

,

但是以test

eax,eax最多,

因为Windows

API多对eax做了优化,

当使用

if

检测是否返回0

时,

就要使用test指令,

更多有待总结。浮点数的几种表示方法浮点二进制表示浮点数是由三部分组成的:符号、尾数、指数。以数字-1.23456X10^5为例,其中“-”是符号,表示该浮点数是负数,1.23456是尾数,5是指数。IEEE二进制浮点数的表示Intel微处理器使用IEEE发布的Standard754-1985forBinaryFloating-PointArithmetic中规定的三种浮点二进制存储格式。见下表。格式说明单精度32位:符号占1位、指数占8位、尾数中的小数部分占23位。可表示的大致范围2^-126~2^127,也称为短实数双精度64位:符号占1位、指数占11位、尾数中的小数部分占52位。可表示的大致范围2^-1022~2^1023,也称为长实数扩展精度80位:符号占1位、指数占16位、尾数中的小数部分占63位。可表示的大致范围是2^-16382~2^16383,也称为扩展实数三种格式非常相似,因此这里重点讲述单精度格式。见下图。图中最高有效位(MSB)在左边:1823符号指数尾数中的小数部分符号二进制浮点数的符号由一个符号位便是,如果该位为1表示负数,为0表示正数。浮点数的0是正数。尾数在形如mXb*e的表达式表示的浮点数中,m称为尾数,b是基数,e是指数。尾数部分m是一个十进制小数。大家知道的二进制,十进制和十六进制的加权进位表示法。现在可以把同样的概念扩展到数字的小数部分。例如十进制数123.154可以表示为表达式的和:123.154=(1X10^2)+(2X10^1)+(3X10^-0)+(1X10^-1)+(5X10^-2)+(4X10^-3)二进制浮点数的表示方法类似,只要用2为基数计算位权值即可。例如浮点数二进制值11.1011可表示为:11.1011=(1X2^1)+(1X2^0)+(1X2^-1)+(0X2^-2)+(1X2^-3)+(1X2^-4)另一种表示小数点后面的值的方法是用以2的幕为分母的分数之和表示,例如.1011=1/2+0/4+1/8+1/16=11/16得到分数表示的方法是相当直观的。二进制数1011实际上是十进制数11,可用1011(十进制数11)做分数的分子。假设e是二进制小数点后的有效数字的位数,那么分母就是2^e。在该例子中e=4,因此分母2^e=16。下表中给出了另外一些二进制浮点表示转换成十进制分数的例子。表1中的最后一项是23位规格化尾数所以能存储的最小分数。表2中列出了另一些例子,例子中分别给出了二进制浮点数以及与其等价的分数和十进制数。表1二进制浮点数转换成分数二进制浮点数十进制分数值11.1133/4101.001153/161101.1001011337/640.001015/321.01113/80.000000000000000000000011/8388608表2二进制和十进制分数二进制浮点数十进制分数值十进制值.11/2.5.011/4.25.0011/8.125.00011/16.0625.000011/32.03125尾数的精度连续实数的整体不可能以有限个数据位的浮点格式表示。例如,假设使用一种简化的格式表示浮点数,其中尾数占5个数据位,那么就没有办法表示二进制数1.1111和二进制数10.0000之间的实数了。因此表示二进制数1.11111就需要更高精度的尾数。类似的结论可以推广到IEEE双精度格式,53位的尾数不能表示需要54或更多个数据位的二进制数字。指数单精度浮点数的指数是以8位无符号正数的格式存储的,实际存储的是指数值与127相加的和。以数字1.101X2^5为例,指数(5)与127相加的和(十进制132,二进制10100010)存储在指数部分。下表给出了一些指数的例子。其中第一列是有符号十进制整数,第二列是调整后的值,最后一列是调整后的整数对应的二进制值。调整后的指数部分总是正数(在1~254之间),实际指数范围是-126~+127。之所以选择这个范围,是为了避免最小可能的指数的倒数发生溢出(这里由于如果最小的指数选择了-127,那么-127+127=0)指数(E)调整后(E+127)二进制值+513210000100012701111111-1011701110101+12725411111110-126100000001-112601111110二进制浮点数的正规化大多数浮点二进制数都是以正规化的格式存储,以便能够使得尾数的精度最大化。对于任何给定的浮点二进制数,可通过移动小数点,使小数点前仅有一个数字“1”从而使其正规化,指数表示小数点向左(正数)或向右移动(负数)的位数。看下面例子未正规化格式正规化格式1110.11.1101X2^3.0001011.01X2^-4101001.1.010001X2^6非正规化值:二进制浮点正规化操作的逆过程称为逆正规化,可通过移动二进制小数点直到指数为零。如果指数部分使正数n,那么向右移动小数点n位;如果指数部分是负数n,那么向左移动小数点n位。如果需要,开头的空位应以0填充。IEEE表示法实数编码一旦符号位、指数和尾数域进行了正规化和编码,得到一个完整的二进制IEEE短实数就非常容易了。例如,二进制值1.101X2^0可表示如下:符号位:0(因为是正数)指数:01111111(因为指数是00+127=127=01111111)小数:10100000000000000000000(因为单精度短实数尾数中小数部分占23位)调整后的指数01111111是十进制数127,所有正规化后的二进制尾数在小数点前面都有一个1,因此对该位就无需显式编码了。下表中给出了几个例子:单精度浮点数位编码的例子二进制调整后的指数值符号、指数和尾数-1.1112710111111111000000000000000000000+1101.10113001000001010110100000000000000000-.0010112410111110001000000000000000000000+100111.013201000010000111000000000000000000+000000110101112000111100010101100000000000000000IEEE规范中还规定了几类实数和非实数的编码:正数0和负数0反向正规化的有限数正规化的有限数正无穷和负无穷非数字值(NaN)不确定数Intel浮点单元使用不确定数响应一些无效的浮点操作。正规化和反向正规化:正规化的有限数是0到无穷大之间的所有可以编码为正规化实数的非0有限值。尽管乍看起来所有非零浮点数都可以正规化,不过事实上在值非常接近于0时这是不可能的。由于指数范围的限制,FPU有可能不能把二进制的小数点移动到正规化的位置。假设FPU的计算结果是1.0101111X2^-129,其指数太小无法在单精度实数中存储,这时将产生一个下溢出异常,可通过向左移动小数点(每次一位)使其正规化,直到指数落在有效的范围内为止:例1.01011110000000000001111X2^-1290.10101111000000000000111X2^-1280.01010111100000000000011X2^-1270.00101011110000000000001X2^-126在这个例子中,由于移动了小数点,精度会有一些损失。正无穷和负无穷:正无穷(+∞)表示最大的正实数,负无穷(-∞)表示最小的负实数。可以比较无穷值和其他值:-∞小于+∞,-∞小于任何有限数,+∞大于任何有限数。正无穷和负无穷都能用来表示溢出。非数字(NaN):NaN是不表示任何有效实数的位序列。IA-32体系结构包含两种类型的NaN:quietNaN可通过大多数算术运算而不会导致任何异常;signallingNaN可用于产生一个无效操作异常。编译器可以用SignallingNaN值填充未初始化的数组,对该数组进行任何计算的企图都会引发一个异常。quietNaN可用于存放调试会话产生的诊断信息。浮点单元不会试图对NaN执行操作。IA-32手册详细描述了一套规则,用于确定以这两种类型的NaN组合作为操作数时指令执行的结果。特殊值的编码:下表列出了浮点操作中经常遇到的几个特殊值的编码。标记位的位置既可以是0,也可以试1。QNaN表示quietNaN,SNaN表示signallingNaN。特殊的单精度浮点值的编码值符号、指数、尾数正数000000000000000000000000000000000负数010000000000000000000000000000000正无穷01111111100000000000000000000000负无穷11111111100000000000000000000000QNaNx111111111xxxxxxxxxxxxxxxxxxxxxxxxxSNaNx111111111xxxxxxxxxxxxxxxxxxxxxxxxx*SNaN的尾数域以0开始,但是其余x表示的域中至少要有一个为1。把十进制分数转换为二进制实数如果十进制分数可以很容易地表示为1/2+1/4+1/8+......的格式,那么就很容易得到其对应的二进制实数。下表中的左边一列中的大部分分数一眼上去都不容易转换成二进制,不过如果写成第二列的格式就容易多了。十进制分数和二进制实数的例子十进制分数分解为二进制实数1/21/2.11/41/4.013/41/2+1/4.111/81/8.0017/81/2+1/4+1/8.1113/81/4+1/8.0111/161/16.00013/161/8+1/16.00115/161/4+1/16.0101很多实数,如1/10(0.1)或1/100(.01),不能用有限个二进制数字位表示,这样的分数只能近似表示为若干以2的幕为分母的分数之和。想想货币值如$39.95会受什么影响。另一种方法:使用二进制长除法。把十进制分数转换成二进制数时如果涉及到的十进制值比较小,可以使用一种很方便的方法:首先把分子和分母都转换成二进制值,然后再进行除法操作。例如,十进制值0.5可表示成分数5/10,十进制5是二进制值0101,十进制值10是二进制值1010,执行下面的除法,我们发现商是二进制值0.1:_____.1__________1010|0101.0-1010___________0在被除数减去1010后余数为0时,除法终止。因此十进制分数5/10等于二进制值0.1,我们称上面这种方法为二进制长除法。PS来自于DePaul大学的HarveyNice。例子:以二进制数表示0.2。下面把十进制的0.2(2/10)转换成二进制值,使用长除法。首先,把二进制值10除以1010(十进制值10):___.00110011(等等)_________________1010|10.000000001010__________11001010___________100001010____________11001010_____________等等第一个足够大的余数是10000,在除以1010之后,余数是110,在末尾添加一个0,新的被除数是1100,除以1010后,余数是10,再添加3个0,新的被除数是10000,这等于进行第一次除法操作时被除数的值。从这点开始,商中重复出现0011,因此我们得知准确的商是无法得到的。0.2不能用有限个二进制位表示。0.2的单精度编码的尾数部分是00110011001100110011001把单精度值转换成十进制数下面是在把IEEE单精度值转换成十进制数时的推荐步骤:1。如果MSB(最高有效位)是1,该值为负数,否则为正数。2。接下来的8位表示指数,减去二进制值01111111(十进制值127),得到未调整的值数值,把未调整的指数值转换成十进制数。3。接下来的23位表示尾数,注意尾数小数点前面有一个被省略的“1.”,尾数尾部的0可以忽略。这时就可以使用第一步中得到的符号位、第二步中得到的指数以及本步骤中得到的尾数创建一个浮点二进制数了。4。对第三步得到的浮点数进行逆规格化操作(根据指数相应地移动小数点,指数为正则右移动,指数为负则左移动)。5。从左到右,使用位权表示方法得到浮点数的2的次幕之和的表示,进而得到相应的十进制数。例子:把IEEE浮点数(01000001001011000000000000000000)转换成十进制数1.该数是正数。2.未调整的指数值是00000011,也就是十进制数3。3.组合符号位、指数和尾数得到二进制数+1.01011X2^34.逆正规化后的二进制数是+1010.115.对应的十进制值是+103/4或+10.75提到编码,只要学过一点汇编的人都应该知道一些常用的汇编指令的编码,比如:B878563412,一看到B8就知道对应的汇编指令是MOVEAX,0X12345678占用5字节,一看到E8就知道是E8后面跟的是JMP的4字节偏移,一见90就知道是NOP,因为这些指令都很常用,编码也都很简单,想必大家对这些指令编码都熟记于心了。如果提到MOVEBX,XXXXXXXXMOVECX,XXXXXXX这些指令也许大家对指令编码就不怎么记得了,因为X86的编码太多了,要把他全记住那可不是件容易的事。其实要把X86的编码指令记住并不是件难事,因为X86编码指令看似复杂庞大,其实这大部分编码有是有规律可寻的。在这里先说组寄存器:01234567EAXECXEDXEBXESPEBPESIEDI01234567ALCLDLBLAHCHDHBH不知道各位同学当年学汇编的时候寄存器是不是按这个顺序记的,如果是按这个顺序记住的话,接下来讲的编码你可能一看就记住了B8是MOVEAX大家都很清楚的记得,那么B9呢?B9就是MOVECX,BAMOVEDX聪明的同学应该很快的看出规律出来了吧!BB是MOVEBX,BC是MOVESP

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论