寄存器

寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。
寄存器对大小写不敏感,所以ax和AX是一样的

寄存器整体结构理解

主要含有八大寄存器:AX、BX、CX、DX、SI、DI、BP、SP
这是16位的表示方法,32位前加E,64位前加R,即EAX和RAX
八大寄存器高位可以拆分为低位,四个16位的寄存器每一个都可以拆开称为两个独立的8位寄存器使用,分别用高字节和属低字节表示,即AH,AL等,按8位使用时只能用于存放数据。

拆分

寄存器作用

  • AX:累积暂存器(操作数和结果数据累加器,返回值运算结果一般都存储在这里)
  • BX:基底暂存器(DS段的数据指针,在内存寻址的时候存放基地址)
  • CX:计数暂存器(字符串和循环操作的计数器,控制循环次数)
  • DX:资料暂存器(用于存储部分乘法结果和部分出发被除数)
  • SI:来源索引暂存器(字符串操作的源指针,SS段的数据指针)
  • DI:目的索引暂存器(字符串操作的目标指针,ES段的数据指针)
  • SP:堆叠指标暂存器(栈帧指针,一般指向栈顶,所以也被成为栈顶指针)
  • BP:基底指标暂存器(基址指针,SS段的数据指针)

寄存器

扩展寄存器

例如R8-R15,还有MMX和其他的种类寄存器,都是为了更方便的使用CPU所针对某一功能开发的特定寄存器。

例如

  • EFLAGS寄存器
  • MMX寄存器
  • XMM寄存器

内存

在冯诺依曼结构中用来存储程序和数据的部件叫做存储器
存储器分为主存储器和辅助存储器,主存储器就是内存,辅助存储器可以理解为硬盘

内存寻址范围

32位系统的内存寻址范围0x00000000-0xFFFFFFFF
32位系统的最大寻址范围是0xFFFFFFFF+1(4294967296Byte)=4GB
也可以理解为2的32次方

64位系统的寻址范围是0x0000000000000000-0xFFFFFFFFFFFFFFFF
理论最大能支持2147483648GB即为2的64次方,16EB内存

寄存器和内存区别

  • 寄存器:数量少,在cpu内部,速度极快,但是价格昂贵
  • 内存:数量庞大,相对寄存器而言,速度较慢,但是价格便宜

内存的五种表现形式

  1. 立即数

    MOV EAX , DWORD PTR DS:[0x????????]
    把后面内存器中的数,移动给EAX

  2. 寄存器

    MOV EBX,0x????????
    对该寄存器赋予一个指定的数值

  3. 寄存器—+立即数

    MOV EBX,0x????????
    MOV EAX,DWORD PTR DS:[EBX+4]
    对该寄存器赋值一个含有偏移位的值

  4. 比例因子

    [REG+REG*{1,2,4,8}]
    数组元素地址=数组首地址+元素索引*数组元素占用空间
    MOV EAX,0x????????
    MOV EBX,0X2
    MOV ECX,DWORD PTR DS:[EAX+EBX*4]
    用来寻址用的,在高级语言里相当于一种赋值

  5. 比例因子+立即数

    MOV EAX,0x????????
    MOV EBX,0X2
    MOV ECX,DWORD PTR DS:[EAX+EBX*4+1]
    在上述条件加一个偏移

数据存储模式

  • 大端序:数据高位存储在内存低位,数据低位存储在内存高位
  • 小端序:数据高位存储在内存高位,数据低位存储在内存低位

PS:内存的高位是1,数据的高位是数值大的内个

地址0x77 66 55 44
大端序:念的时候是77 66 55 44
小端序:念的时候是66 55 66 77
大端序通常用于ARM架构
小端序通常用于AMD64架构,x86架构

数据存储模式

EFLAGS寄存器

EFLAGS寄存器

  • CF:进位标志,出现进位,标志位为1
  • PF:运算结果的值中,1的个数为偶数的话(二进制),标志位为1
  • AF:辅助进位标志位,出现进位或者借位则为1
  • ZF:零标志,运算结果为0的话则为1
  • SF:最高位如果是1则为1,是零则为零
  • TF:单目则为1,可以进行单目调试
  • IF:中断标志,响应可以停止的一个中断,这个时候会为1
  • DF:方向标志,字符串指定,从高地址到低地址的的时候,标志位为1
  • OF:当运算出现结果是溢出的时候会为1(进位不代表溢出,只有超过数据宽度极限的时候才算溢出,比如说32位的机器,出现33位的数值)

VS2015常用快捷键

设置/取消断点:F9
开始/继续调试:F5
逐语句(执行当前层次的内层的语句):F8 / F11
逐过程(执行当前层次的语句):(shift+F8) / F10
逐层跳出内层次:(ctrl+shift+F8)
监视变量:(shift+F9)


第一个小程序

代码如下,起到验证环境配置的作用,也可以对基础有进一步的理解

第一个小程序代码

.586
;高于386都可以,是一种指令集

.MODEL flat, stdcall
;使用的是win32的内存模式,stdcall是函数调用约定

includelib user32.lib
includelib kernel32.lib
;使用这两个导入函数

ExitProcess PROTO, dwExitCode : DWORD
MessageBoxA PROTO hWnd : DWORD, lpText : BYTE, lpCaption : BYTE, uType : DWORD

.data  
Number DWORD 0
text db "shellcode", 0
.data?
.const
;段,data段一般存放在程序的自身文件,一个自身的区段里,声明多大就多大
;data?是一种缓冲段,需要动态运行的。
;显示的字符串信息,const常量段,比如用来显示的信息,可读不可写,整个使用过程中不会改变的信息
;coad是代码段,所有执行的代码都放在这个段。
;堆栈段现在一般不会用。


.code
main proc
mov eax, 5
mov ebx, 6
add eax, ebx
add eax, Number
push 0
push offset text
push offset text
push 0
call    MessageBoxA
sub  esp, 16
call ExitProcess
main ENDP
END main
;最后是结束整个程序的流程

汇编语言基础指令

数学运算指令

  • 加法指令ADD(Addition)
  • 减法指令SUB(SUBtract)
  • 乘法指令MUL(MULtiply)
  • 除法指令DIV(DIVision)
  • 自增运算INC(INCrement by 1)
  • 自减运算DEC(Decrement by 1)

逻辑运算指令

  • 与运算AND
  • 或运算OR
  • 异或操作XOR
  • 非运算NOT

代码如下

.586
.MODEL flat,stdcall
.code
    main proc
    mov eax, 5
    mov ebx, 6
    not eax
    mov eax,ebx
main ENDP
END main

简单的循环指令

循环控制指令为loop
例子:现尝试使用循环编写一个程序,计算0+1+2+3+4+...+100的答案。

代码如下

.586
.MODEL flat,stdcall
.code
main proc
    mov eax,0
    mov ecx,100
s:
    add eax,ecx
    loop s
    mov eax,eax
main ENDP
END main


循环利用
解析:最后的答案的16进制是13BA,换算成十进制是5050,就是用每一个0+100和1+99和2+88,来依次进行计算得出的。

堆栈

什么是堆栈

  1. 栈是一个后进先出的存储区域,位于堆栈段中,SS段寄存器描述的就是堆栈段的段地址
  2. 栈的数据出口位于栈顶,也就是esp寄存器所指向的位置
  3. 栈顶是低位,也就是地址较小的一侧,同时也会上下移动
  4. 由ebp寄存器指向的栈底,并不会改变

栈的指令

  • PUSH:压栈指令,32位汇编首先ESP-4,留出一个空间,然后把要压入栈的内容压入
  • POP:出栈指令,32位汇编首先将栈顶的数据弹出给指定的目标,然后ESP+4,清掉空间

栈的作用

  1. 存储少量数据
  2. 保存寄存器环境
  3. 传递参数

栈的实际操作

这个建议使用olldbg,vs2015的操作不明显,基本看不出来,所以使用OD来搞。
如下图所示,随便打开一个dll文件,然后在文件头的位置汇编(space),使用push和pop指令进行操作,然后观察右侧的竖直和右下角的变化。
栈的操作

数据指令

  • 数据移动指令

MOV
格式:MOV OPRD1,OPRD2
功能:本指令将一个源操作数送到目的操作数中,即OPRD1<--OPRD2
OPRD1为目的操作数,可以是寄存器,存储器,累加器
OPRD2为源操作数,可以使寄存器,存储器,累加器,立即数

代码如下

.586
.MODEL flat,stdcall
.code
main proc
       mov eax,ebx
       mov eax,ecx
       mov ecx,0
       mov eax,eax
main ENDP
END main


14

  • 有效地址传送指令

LEA
格式:LEA OPRD1,OPRD2
功能:将源操作数给出的有效地址传送到指定的寄存器中。
OPRD1必须是寄存器
word是16位,dward是32位的

代码如下

.586
.MODEL flat,stdcall
.code
main proc
       lea eax,dword ptr ss:[esp-4]
       mov eax,eax
main ENDP
END main

  • 数据交换指令

XCHG
格式:XCHG OPRD1,OPRD2
其中的OPRD1为目的操作数,OPRD2为源操作数
功能:将两个操作数相互交换位置,该指令把源操作数OPRD2与目的操数OPRD1交换

代码如下

.586
.MODEL flat,stdcall
.code
main proc
       xchg eax,dword ptr ss:[esp-4]
       mov eax,eax
main ENDP
END main

  • 数据比较指令

CMP(CoMPare)
格式:CMP OPRD1,OPRD2
功能:对两数进行相减,进行比较。
他主要影响的是falg寄存器,可以看一下右侧的eflag寄存器变化。
15

  • 测试指令

TEST
格式:TEST OPRD1,OPRD2
功能:其中OPRD1、OPRD2的含义同AND指令一样,也是对两个操作数进行按位的与运算,唯一不同之处是不将’与的结果送目的操作数,即本指令对两个操作数的内容均不进行修改,仅是在逻辑与操作后,对标志位重新置位。

  • 条件转移指令

JCG
前期这个东西可以看做使用CMP进行比较,来看前后的数值大小是否进行跳转

JMP:无条件跳转 JZ/JE:ZF=1等于0或相等跳转
JNZ/JNE:ZF=0不等于0或者不相等跳转
JBE/JNA:CF=1/ZF=1低于等于/不高于跳转
JNBE/JA:CF=0/ZF=0不低于等于/高于跳转
JL/JNGE:SF!=OF小于/不大于等于跳转
JNL/JGE:SF=OF 不小于/大于等于跳转

17
16

JMP指令

.586
.MODEL flat,stdcall
.code
main proc
       mov eax,0
jmpflag:
       add eax,1
       jmp jmpflag
main ENDP
END main

JZ相等跳转

.586
.MODEL flat,stdcall
.code
main proc
       mov eax,5
jmpflag:
       cmp eax,5
       jz jmpflag
main ENDP
END main

JNZ不相等跳转

.586
.MODEL flat,stdcall
.code
main proc
       mov eax,5
jmpflag:
       cmp eax,6
       jnz jmpflag
main ENDP
END main

JBE小于跳转

.586
.MODEL flat,stdcall
.code
main proc
       mov eax,5
jmpflag:
       cmp eax,6
       jBE jmpflag
       mov eax,eax
main ENDP
END main

  • 串操作指令

MOVS
格式:MOVS OPRD1,OPRD2
MOVSB MOVSW
功能:OPRD1<--OPRD2,传送操作数
说明:1.其中OPRD2为源串符号地址,OPRD1为目的串符号地址.
自己理解:可以简单的理解为一种赋值。

串操作指令

.586
.MODEL flat,stdcall
.code
main proc
       mov edi,esp
       mov dword ptr ss:[esp],0
       mov esi,ebp
       movs dword ptr es:[edi],dword ptr ds:[esi]
       mov eax,eax
main ENDP
END main

  • 字符串存储指令

STOS
格式:STOS OPRD
功能:把AL(字节)或AX(字)中的数据存储到DI为目的串地址指针所寻址的存储器单元中去指针DI将根据DF的值进行自动调整。
自我理解: 就是将eax的数据送到edi所指向的地址空间中, stos 相当于 mov [edi],eax
PS: add edi,4,至于为什么加上4 原因就是 eax的大小是4字节,当把一个数据流传送到edi的时候,如果要进行下一个数据流的操作的话,edi的地址就要加上4,指向下一个4字节的空间

字符串存储指令

.586
.MODEL flat,stdcall
.code
main proc
       mov eax,11223344
       mov edi,esp
       stos dword ptr es:[edi]
       mov eax,eax
main ENDP
END main

  • 重复前缀指令

REP
格式:
REP; CX<>0重复执行字符串指令
-—-REPZ/REPE; CX<>0且ZF=1重复执行字符串指令
REPNZ/REPNE; CX->0 ZF=0重复执行字符率指令
功能:在串操作指令前加上重复前缀,可以对字符串进重复处理.由于加上重复前缀后,对应的指令代码是不同的,所以指令的功能便具有重复处理的功能,重复的次数存放在CX寄存器中.
自己理解:这个东西基本上是跟loop循环指令差不多的。

dword ptr 解释

最近前一段老师出现这个东西,一直在用,却不知道他到底是个什么,问了一下大佬,终于获得了解答。
这个命令就相当于一个容器,指定了32位,4字节的大小,其实就是指定了数据宽度,就是说我要取多少数据

mov dword ptr ss:[esp],0

例如这个指令的意思就是:将0这个值,赋予到ss段,esp所指向的地址的双字空间的值变为0,这里的双字空间就是32位的意思。

  • 过程调用指令

CALL
格式:CALL OPRD
功能:过程调用指令,跳过去来执行这段函数
相当于:
push eip
jmp OPRD

  • 返回指令

RETN
相当于
pop eip
jmp eip
call retn

过程调用-函数

main主函数就是函数的一种
proc(函数的一个定义)和endp(结束这个定义)伪指令,masm的编译器是这么约定的,其他的编译器不一定是这么约定的。
过程调用的方式:
function proc
code
function endp

参数传递方式:
1.寄存器传参:把值压到寄存器里,利用寄存器在函数里直接调用寄存器的值
2.堆栈传参:把值压入栈中,在过程调用中取出来再使用 自我理解:可以理解为一种函数的创建,创建一个新的函数,然后在主函数执行的时候进行调用。

寄存器传参式

.586
.MODEL flat,stdcall
.code
addx proc
       add eax,ebx
       ret
addx ENDP
main proc
       mov eax,1
       mov ebx,2
       call addx
       mov eax,eax
main endp
END main

堆栈传参

.586
.MODEL flat,stdcall
.code
addx proc
       mov eax,[esp+4]
       mov ebx,[esp+8]
       add eax,ebx
       ret
addx ENDP
main proc
       push 1
       push 2
       call addx
       mov eax,eax
main endp
END main

Win32汇编入门

  • 什么是API

API(Application Programming Interface,应用程序接口)
是一些预先定义的函数,或指软件系统不同组成部分衔接的约定。目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问原码,或理解内部工作机制的细节。

操作系统的用户接口API函数包含在Windows系统目录下的动态连接库文件中。WindowsAPI是一套用来控制Windows的各个部件的外观和行为的预先定义的Windows函数。用户的每个动作都会引发一个或几个函数的运行以告诉Windows发生了什么。这在某种程度上很像Windows的天然代码。而其他的语言只是提供一种能自动而且更容易的访问API的方法当你点击窗体上的一个按钮时,Windows会发送一个消息给窗体,VB获取这个调用并经过分析后生成一个特定事件。
更易理解来说:Windows系统除了协调应用程序的执行、内存的分配、系统资源的管理外,同时他也是一个很大的服务中心。调用这个服务中心的各种服务(每一种服务就是一个函数)可以帮助应用程序达到开启视窗、描绘图形和使用周边设备等目的,由于这些函数服务的对象是应用程序,所以称之为Application
Programming Interface,简称API函数。
WIN32的API也就是MicrosoftWindows32位平台的应用程序编程接口。
凡是在Windows工作环境底下执行的应用程序,都可以调用Windows API。

主要调用API的代码流程如下

ExitProcess PROTO,dwExitCode:DWORD 
MessageBoxA PROTO  hWnd:DWORD,IpText:BYTE,IpCaption:BYTE,uType: DWORD

api名字,proto,参数名,类型,参数名,类型,参数名,类型,参数名,类型。

简单案例如下

.586
.MODEL flat,stdcall
includelib user32.lib
includelib kernel32.lib
ExitProcess PROTO, dwExitCode : DWORD
MessageBoxA PROTO hWnd : DWORD, lpText : BYTE, lpCaption : BYTE, uType : DWORD
.data
string db "HelloWord!",0
.code
main proc
       push 0
       lea eax,string
       push eax
       push eax
       push 0
       call MessageBoxA
       add esp,16
       call ExitProcess
main endp
END main

其中当push不一样的时候会有多种变化。
0的时候是确定,1的时候是是或否 2的时候是终止,重试,忽略 3的时候是是,否,取消

20
21
22


总结

本次从5.7-5.13期间学习了这些,还可以,难度不高,主要是为了C语言做铺垫,然后汇编暂时就到这里,等C语言初级学完之后,开始逆向的时候,就可以继续学习更深层次的汇编了。


参考网站链接

创建文件基础配置教程
Olldbg快捷键

Last modification:July 28th, 2020 at 09:24 pm
如果你觉得我的文章帮到你的话,不要白嫖,一毛两毛也是爱。