逆向学习笔记(一)
来源:互联网 发布:linux中80端口被占用 编辑:程序博客网 时间:2024/05/16 10:17
本人从开始学习逆向已经有一段时间了,感觉逆向是一门很深的学问,需要长时间的积累和足够的耐心和细心。
下面是我个人的一些总结,还请大家指点。学习逆向是需要一定的汇编基础,学习汇编就想是学习一门外语,它的指令就像是单词一样,只有理解了这些单词的意思才可以理解汇编的代码的含义,由于汇编器/反汇编器的不同,现在x86汇编代码主要分为Intel和AT&T汇编,这两者在语法方面存在一定的差异,建议先从Intel汇编开始,它主要是在PC端,而且参考的资料较多。(本文部分内容来自网络)
一、基础的单位和字节序的介绍
bit 位(指的是 0 or 1)
byte 字节 1byte = 8 bit
word 字 1 word = 2 byte
dword (即double word)
1KB=1024B
1MB=1024KB
1GB=1024MB
1TB=1024GB
字节序:
大端序(Big endain):内存地址低位储存数据的高位,
小端序(Little endain):内存地址高位储存数据的高位(x86是基于Intel8086处理器的小端体系结构)
MZ 对应的是5A 4D,这里是十六进制,每两位代表一个字节。
二、寄存器
2、寄存器(Register):CPU 内部用来存放数据的一些小型储存区域,暂存指令、数据和地址。
一开始寄存器是8位的(也就是1个字节),到了8086的时期变成16位(像ax,bx),再到80386的时期发展为32位(eax,ebx等),再到现在主流的64位cpu采用的是两个32位的寄存器一起使用。它的规律可见下图
由这张图,我们可以发现寄存器是兼容的从 * al–>ax–>eax*
寄存器有分以下几类:
2.1、通用寄存器:(寄存器前面的E,代表extended,扩展)
在windows保护模式下的x86体系结构有8个通用寄存器
EBP,ESI,EDI,ESP:主要用作保存内存地址的指针
EAX,EBX,ECX,EDX(0-FFFFFFFF):主要用在算数运算指令中,常用来保存常量与变量的值。
部分通用寄存器的用途:
ECX:循环计数器ESI:字符串/内存操作的源`EDI:字符串/内存操作的目标EBP:栈帧基准ESP:(栈顶指针)指示栈区域的栈顶地址,指向当前进程的栈空间地址`。
EIP(instuction pointer):扩展的指令指针寄存器,总是指向下一条要被执行的指寄存针器。
2.1、标志位寄存器(Flag Register)
(IA-32)事实上所有的标志位归并与一个32位的标志位寄存器,也就是说有32个不同的标志位。
每个标志位都有两个属性:置1或置0
在逆向分析的过程中,你真正需要关心的标志位只有三个,也就是cmp指令能修改的那三个:Z/O/C。
因为跳转指令是否成立是于这三个标志寄存器的值有关的
Z标志位(zero flag),这个标志位是最常用的,运算结果为0时候,Z标志位置1,否则置0。O标志位(溢出标志 overflow flag),在运行过程中,有符号整数溢出时,OF置为1。C标志位(进位标志 carry flag),无符号整数溢出时,CF置为1,记录运算时从最高有效位产生的进位值。例如执行加法指令时,最高有效位有进位时置1,否则置0。
通常在选择、循环等语句中需要用到cmp和text指令去改变标志寄存器的值,以此来判断是否跳转
2.3、段寄存器(Segment)
CPU访问内存单元时要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间。我们将这个唯一的地址称为物理地址。
8086CPU(外部)有20位地址总线,可传送20位地址,寻址能力为1M。8086内部为16位结构,它只能传送16位的地址,表现出的寻址能力却只有64KB。
8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址
地址加法器合成物理地址的方法是:
**物理地址=段地址×16+偏移地址** 段地址和偏移地址都是16位的,'段地址 x 16'即向左移了4位(2^4).
一个数据的X进制形式左移1位,相当于乘以X。
由6种寄存器组成,分别为
CS(code segment)SS(stack segment)DS(data segment)ES(extra segment)FS(data segment)GS( data segment)`
ES,FS,GS存放程序使用的附加数据段的段基址
三、基础的指令(汇编指令不区分大小写)
几个概念
立即数:以常量的形式出现在指令中,只能作为源操作数。
寄存器数:将数据存放在寄存器中,指令直接使用寄存器名。
内存操作数:将数据放在内存中,指令中使用内存地址。、如:[BX]
3.1算数运算指令
ADD DEST,SRC //右加到左SUB DEST,SRC //DEST = DEST - SRCINC DEST //加一DEC DEST //减一NOG //NULLimul src //带符号乘idiv src //带符号除mul src 乘div src 除dec dest 自减inc dest 自加int 中断指令
指令有很多,不用死记,用到时查就好了
3.2.逻辑运算和关系运算指令
AND dest,src //按位与运算,后值赋给destOR dest,src //按位或运算XOR dest,src //按位异或NOT dest //按位取反text dest, src //这个指令和and指令差不多,对两个操作数进行按位的‘与’运算,但在逻辑与操作后,对两个操作数的内容均不进行修改,仅对标志位重新置位。CMP dest,src // 和SUB指令相似,只是不将dest-src的值放入dest
3.3 基础的指令
CALL XXXX //调用XXXX地址处的函数 JMP XXXX // 跳转到XXXX地址处 PUSH XXXX //保存XXXX到栈(入栈) POP XXXX //弹出XXXX地址(出栈) RETN XXXX //跳转到栈中保持的地址础的结构.
条件跳转指令有很多,通常情况下,都与CMP和TEXT匹配出现,但条件跳转指令是看标志位的值的,具体的条件跳转指令表可以自行百度
操作符 offert :取得标号的偏移地址
mov ax,offert start ;相当于 mov ax,0
x = [abc] 是指取abc 所在的地址的值,赋给x;
四、基础的结构
4.1、栈(Stack)
(1)暂时保存函数内的局部变量(2)调用函数时传递参数(3)保存函数返回后的地址
FILO,由下向上扩展 向栈压入数据,栈顶指针减小,向低地址移动
4.2、栈帧(Stack Frame)技术:(每个函数的每次调用,都有它自己独立的一个栈帧)
用EBP表示栈区域的基地址,函数被调用是保存ESP的值,函数返回时再把值返回ESP,保证栈不会崩溃
栈帧的基本结构如下
PUSH EBP //函数开始 (使用EBP前先把已有值保存到栈中)MOV EBP,ESP //保存当前ESP到EBP中... //这是函数体部分… //ESP的变化与EBP无关,可以安全访问函数的局部变量、参数MOV ESP, EBP //将函数的起始地址返回到ESP中POP EBP //函数返回前弹出保存在栈中的EBP值RETN //函数终止
一段简单的代码/汇编代码
1: #include<stdio.h>2: long add(long a,long b)3: {00401020 push ebp //栈帧开始00401021 mov ebp,esp00401023 sub esp,48h00401026 push ebx00401027 push esi00401028 push edi00401029 lea edi,[ebp-48h] //取出此函数可用栈空间首地址放入edi0040102C mov ecx,12h00401031 mov eax,0CCCCCCCCh //局部变量初始化00401036 rep stos dword ptr [edi] //根据ecx的值,将eax中的内容,以dw为单位写到edi指向的内存中4:long x = a,y = b;00401038 mov eax,dword ptr [ebp+8]0040103B mov dword ptr [ebp-4],eax0040103E mov ecx,dword ptr [ebp+0Ch]00401041 mov dword ptr [ebp-8],ecx5:return (x+y);00401044 mov eax,dword ptr [ebp-4]00401047 add eax,dword ptr [ebp-8] //x+y6:}0040104A pop edi0040104B pop esi0040104C pop ebx0040104D mov esp,ebp0040104F pop ebp00401050 ret/*......*/7:int main()8:{00401060 push ebp //栈帧00401061 mov ebp,esp00401063 sub esp,48h00401066 push ebx00401067 push esi00401068 push edi00401069 lea edi,[ebp-48h]0040106C mov ecx,12h00401071 mov eax,0CCCCCCCCh00401076 rep stos dword ptr [edi]9:long a = 1,b = 2;00401078 mov dword ptr [ebp-4],1 0040107F mov dword ptr [ebp-8],210: printf("%d\n",add(a,b));00401086 mov eax,dword ptr [ebp-8]00401089 push eax //eax = 2 = [ebp-8]0040108A mov ecx,dword ptr [ebp-4]0040108D push ecx //ecx = 1 = [ebp-4]0040108E call @ILT+0(add) (00401005) //调用add()函数00401093 add esp,800401096 push eax00401097 push offset string "%d\n" (0042201c)0040109C call printf (004010d0)004010A1 add esp,811: return 0;004010A4 xor eax,eax //将eax清零12:13: }004010A6 pop edi004010A7 pop esi004010A8 pop ebx004010A9 add esp,48h //降低栈顶esp,释放局部变量空间004010AC cmp ebp,esp //检测栈平衡,004010AE call __chkesp (00401150) //进入栈平衡错误检测函数004010B3 mov esp,ebp004010B5 pop ebp004010B6 ret
od中的代码
栈计算机中常见的结构,但是也存在着一些问题,如栈的溢出,常常会被人利用。
断点:设置断点后,调试运行到断点处将会暂停
返回地址:执行CALL命令进入被调用的函数之前,CPU会先把函数的返回地址压入栈
XOR:两个相同的值进行XOR运算结果为0,常用于寄存器的初始化操作。(相同的值连续执行2次XOR运算即变回原值)
以上所述的只是一些在逆向的过程中常见的部分基础内容,还有许多不足,在开始学习逆向的时候是较困难的,由于有大量陌生的东西,但是只要去多接触,是可以掌握的,就拿基础的汇编的一些语句来说,根本不用去死记,熟悉了就好了,就我自己来说就是平时多去写程序,先看汇编代码,然后在用工具去逆向。
- 逆向学习笔记(一)
- android逆向学习,笔记(一)
- 《iOS应用逆向工程》学习笔记(一)简介
- 逆向学习笔记(二)
- 【android逆向笔记】(一)简单登录逆向
- iOS 菜鸟逆向学习 (一)
- 软件逆向工程学习(一)
- 逆向学习一
- 逆向工程核心原理学习笔记(一):寻找程序的主函数(Main)
- 逆向工程核心原理学习笔记(一):寻找程序的主函数(Main)
- 安卓软件安全与逆向分析学习笔记<一>
- 逆向工程---Mybatis学习笔记(十二)
- 学习笔记(逆向汇编)Day1-Day5
- 学习笔记(逆向汇编)Day6-Day10
- 学习笔记(逆向汇编)Day11-Day15
- 学习笔记(逆向汇编)Day16-Day20
- 逆向工程---Mybatis学习笔记(十二)
- 学习笔记(逆向汇编)Day1-Day5
- HDOJ 1562 Guess the number
- JsonCpp的简单使用方法
- 事务回滚后,自增ID仍然增加
- 三分钟读懂TT猫分布式、微服务和集群之路
- Mapper(XML)文件不识别大于号 小于号
- 逆向学习笔记(一)
- tomcat:性能调优
- 算法-把n个数的每一种排列情况都列出来(排列组合)-全排列-字典序算法(一看就懂)
- 由"java.lang.OutOfMemoryError: unable to create new native thread"说起
- DBUS介绍
- Delphi编辑简单计算器
- Main Thread Checker: UI API called on a background thread:
- Markdown显示gif图片
- Win7 64位+tensorflow1.4.0-GPU版+CUDA8.0+cudnn6.0环境配置