欢迎光临
箫启灵个人站点

【C语言,反汇编】 Switch为什么比if高效

admin阅读(1470)

以下内容,都是在debug模式下的反汇编分析。

总结:

Switch条件低于4个的时候,是不产生任何索引的,跟if差不多

当条件超过四个,且条件是连续的时候,将产生一个索引表,我们称之为大表

如果这些条件有很多,且不连续,就可能出现另外一张小表,一张大表

***********************************************连续值且低于4个判断的swicth反编译分析********************************************

QQ截图20180531160641.png

********************************************连续值且大于4个判断的swicth反编译分析********************************************


55555.png

66666666666.png

********************************************断断续续带连续性的时候swith反编译该是什么样的*******************************

777777.png

888888.png

99999.png

【C语言,反汇编】整数,浮点数存储区别。

admin阅读(936)

C中,整数在汇编中的存储:

有符号数跟无符号数的区别

1、存储没有区别。

2、赋值没有区别

1、扩展的时候有区别  数据宽度小的类型扩展到数据宽度高的类型,会判断有符号数符号位例如 int =char时

2、比较的时候有区别   无符号数的判断会有很大的区别 补码做判断

总结:

所有的整数一定可以完整转换成2进制。

C中,浮点型在汇编中是怎么存储的,为什么会有精度丢失的问题:

QQ截图20180531155717.png

QQ截图20180531155748.png

总结:

用二进制描述小数,不可能做到完全精确。

就好比用10进制来表示1/3也不可能完全精确是一个道理

浮点类型的精度

float和double的精度是由尾数的位数来决定的:

float:2^23 = 8388608,一共7位,这意味着最多能有7位有效数字;

double:2^52 = 4503599627370496,一共16位,这意味着最多能有16位有效数字;

[汇编基础] GCC指令初步认识

admin阅读(1013)

GCC是什么?

可以理解为汇编里面的各种条件判断,各种符号运算的语法。

要想学会GCC首先要了解EFL寄存器

1、CF (bit 0) [Carry flag] 这个就是无符号数进行判断计算的时候,如果内存溢出,则变成1

       若算术操作产生的结果在最高有效位(most-significant bit)发生进位或借位则将其置1,反之清零。

       这个标志通常用来指示无符号整型运算的溢出状态。

2、PF(bit 2) [Parity flag] 最低有效字节为1字节,bit/8位

        如果结果的最低有效字节(least-significant byte)包含偶数个1位则该位置1,否则清零。

        利用PF可进行奇偶校验检查:

        需要传输"11001110",数据中含5个"1",所以其奇校验位为"0",同时把"110011100"传输给接收方,接收方收到数据后再一次计算奇偶性,"110011100"中仍然含有5个"1",所以接收方计算出的奇校验位还是"0",与发送方一致,表示在此次传输过程中未发生错误

3、AF(bit 4) 只有下标为3的那个字符(8bit)位置如果进位,或者借位,则1,否则0

        如果算术操作在结果的第3位发生进位或借位则将该标志置1,否则清零。

        这个标志在BCD(binary-code decimal)算术运算中被使用。

4、ZF(bit 6) [Zero flag] 这个是用的最多的一个寄存器

        若结果为0则将其置1,反之清零。

        经常与CMP或者TEST等指令一起使用:

例1:判断2个值是否相等

MOV EAX,100

MOV ECX,100

CMP EAX,ECX

(CMP 指令相当于SUB指令,但是相减的结果并不保存到第一个操作数中) 如果相等的话,就是0,如果结果是0,ZF位自然就变成1

例2:判断某个值是否为0

TEST EAX,EAX

(TEST指令相当于AND指令,但是与的结果并不保存到第一个操作数中) 如果EAX=0,这个eax&eax 才会等于0,于是ZF自然也变成1了

5、SF(bit 7) [Sign flag]  一般用于检查有符号数是正数还是负数

        该标志被设置为有符号整型的最高有效位。

        (0指示结果为正,反之则为负)

6、OF(bit 11) [Overflow flag] 有符号数计算的过程中,如果溢出,则为1

        溢出标志OF用于反映有符号数加减运算所得结果是否溢出。

        可以这样理解:

  如果是无符号数运算,是否溢出看CF位。

如果是有符号数运算,是否溢出看OF位。

两个符号位不同的数相加,of一定是0,不会溢出

两个正数相加,最高位为0,所以OF位看次高位,如果次高位进位,则溢出 of=1

两个负数相加

7、DF(bit 10) [Direction Flag]

        这个方向标志控制串指令(MOVS, CMPS, SCAS, LODS以及STOS)。设置DF标志使得串指令自动递减(从高地址向低地址方向处理字符串),清除该标志则使得串指令自动递增。

        STD以及CLD指令分别用于设置以及清除DF标志。 

8、TF,暂时用不到 Trace flag 置1CPU进入单步模式

GCC指令介绍(表)

QQ截图20180425165230.png

常见,常用语法(记一下最好):

JE, JZ          结果为零则跳转(相等时跳转)

JNE, JNZ    结果不为零则跳转(不相等时跳转)

JS               结果为负则跳转

JNS            结果为非负则跳转

JL, JNGE    小于则跳转 (有符号数)

JNL, JGE    大于等于则跳转 (有符号数)

JG,JNLE   大于则跳转(有符号数)

JNG,JLE  小于等于则跳转 (有符号数)

例1,两个值做比较

MOV EAX,100

MOV ECX,100

CMP EAX,ECX

(CMP 指令相当于SUB指令,但是相减的结果并不保存到第一个操作数中)

例2:判断某个值是否为0

TEST EAX,EAX

(TEST指令相当于AND指令,但是与的结果并不保存到第一个操作数中)


MUL: 无符号乘 IMUL: 有符号乘

DIV:无符号除 IDIV:有符号除

[汇编基础] 修改EIP指令学习,利用指令破解软件登录(练习exe)

admin阅读(2082)评论(0)

EIP寄存器的作用:

CPU下一次要执行的代码指令的地址

1、 JMP指令:

MOV EIP,寄存器/立即数/内存    简写为   JMP 寄存器/立即数/内存 

jmp的原理就是:把eip的值修改成你想设置的值

2、 CALL指令:

PUSH 下一行地址

MOV EIP,立即数/寄存器/内存     简写为:CALL 立即数/寄存器/内存

与JMP唯一的区别:

在堆栈中存储Call指令下一行地址 ,就是多了一个push 下一行地址

原理:

1、把cll指令的下一行地址放入栈顶。

2、esp的值-4

3、eip的值改成你想跳的指令地址

3、 RET指令:

ADD ESP,4

MOV EIP,[ESP-4]简写为:RET 

原理:

1、esp+4

2、eip的值改成[esp-4]。

逻辑上:

ret = pop eip

————————————练习01.exe——————————-

如图,有个demo程序,密码必须是123456才会提示成功,其他的提示都是提示失败。

demo下载地址:

链接: https://pan.baidu.com/s/1DbIAbTt77Tn0JUuSGvZ5lA 密码: gwkj

QQ截图20180423131648.png

QQ截图20180423131658.png

打开od,载入程序反汇编

QQ截图20180423135724.png

判断的结果的汇编地址:004096DB

成功登录CALL起始地址:004096E1 (有传参)

成功登录的CALL地址   :00409709 

登录失败call执行的起始地址:00409749  (有传参)

登录失败的CALL地址:00409771 

———————————————————–

简单破解方式:

1、nop 004096DB判断结果那一行

2、把00409749 行的代码改成 jmp 004096E1 (直接修改eip,把程序执行到成功的代码里面)

破解成功后,任何密码都可以登录成功。

后续的文章中将会教大家怎么查找call,怎么找参

[汇编基础] 常见汇编指令,内存操作

admin阅读(1439)评论(0)

常见汇编指令

1、 MOV指令

指令格式: 

1、MOV r/m8,r8  r 通用寄存器

2、MOV r/m16,r16  m 代表内存

3、MOV r/m32,r32  imm 代表立即数

4、MOV r8,r/m8  r8 代表8位通用寄存器

5、MOV r16,r/m16  m6 代表16位内存

6、MOV r32,r/m32  imm8 代表8位立即数

7、MOV r8, imm8

8、MOV r16, imm16

9、MOV r32, imm32

2、 ADD指令  加法 A=A+B

指令格式: 

ADD r/m8, imm8

ADD r/m16,imm16

ADD r/m32,imm32

ADD r/m16, imm8

ADD r/m32, imm8

ADD r/m8, r8

ADD r/m16, r16

ADD r/m32, r32

ADD r8, r/m8

ADD r16, r/m16

ADD r32, r/m32 

3、 SUB指令 减法   A = A-B

指令格式: 

SUB r/m8, imm8

SUB r/m16,imm16

SUB r/m32,imm32

SUB r/m16, imm8

SUB r/m32, imm8

SUB r/m8, r8

SUB r/m16, r16

SUB r/m32, r32

SUB r8, r/m8

SUB r16, r/m16

SUB r32, r/m32 

4、 AND指令        A = A & B

AND r/m8, imm8

AND r/m16,imm16

AND r/m32,imm32

AND r/m16, imm8

AND r/m32, imm8

AND r/m8, r8

AND r/m16, r16

AND r/m32, r32

AND r8, r/m8

AND r16, r/m16

AND r32, r/m32 

5、 OR指令                 A=A | B   

OR r/m8, imm8

OR r/m16,imm16

OR r/m32,imm32

OR r/m16, imm8

OR r/m32, imm8

OR r/m8, r8

OR r/m16, r16

OR r/m32, r32

OR r8, r/m8

OR r16, r/m16

OR r32, r/m32 

6、 XOR指令            A

XOR r/m8, imm8

XOR r/m16,imm16

XOR r/m32,imm32

XOR r/m16, imm8

XOR r/m32, imm8

XOR r/m8, r8

XOR r/m16, r16

XOR r/m32, r32

XOR r8, r/m8

XOR r16, r/m16

XOR r32, r/m32 

7、 NOT指令 非

NOT r/m8

NOT r/m16

NOT r/m32 

——————————————————————————–

内存复制指令

——————————————————————————–

有关联的寄存器:

变址和指针寄存器(ESI和EDI)

标志寄存器(EFlags)32位

1、 MOVS指令:移动数据   内存-内存   从esi地址编号的内存复制到edi地址编号的里面去

BYTE/WORD/DWORD 

MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]  简写为:MOVSB

MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]  简写为:MOVSW

MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]  简写为:MOVSD

注释:

movs指令执行后,EDI跟ESI会自动改变值,值的多少跟你数据宽度和DF位方向有关  byte +1 word+2 dword+4

movsb 每次+1

movsw 每次+2

movsd 每次+4

EFl拆成2进制后,从低位到高位,第10位叫DF位。方向位

如果DF位0 

movs指令是+

如果DF位为1

movs指令是-


EFl浅解:

OF overflow flag 溢出标志 操作数超出机器能表示的范围表示溢出,溢出时为1.

SF sign Flag 符号标志 记录运算结果的符号,结果负时为1.

ZF zero flag 零标志 运算结果等于0时为1,否则为0.

CF carry flag 进位标志 最高有效位产生进位时为1,否则为0.

AF auxiliary carry flag 辅助进位标志 运算时,第3位向第4位产生进位时为1,否则为0.

PF parity flag 奇偶标志 运算结果操作数位为1的个数为偶数个时为1,否则为0.

DF direcion flag 方向标志 用于串处理.DF=1时,每次操作后使SI和DI减小.DF=0时则增大.

IF interrupt flag 中断标志 IF=1时,允许CPU响应可屏蔽中断,否则关闭中断.

TF trap flag 陷阱标志 用于调试单步操作.

2、STOS指令:将Al/AX/EAX的值存储到[EDI]指定的内存单元

STOS BYTE PTR ES:[EDI] 简写为STOSB       把al值放到指定内存地址

STOS WORD PTR ES:[EDI] 简写为STOSW    把ax的值放到指定地址

STOS DWORD PTR ES:[EDI] 简写为STOSD  把eax的值放入指定地址

MOV EAX,12345678 观察EDI的值 

MOV EDI,12FFC4 

STOS BYTE PTR ES:[EDI] 

STOS WORD PTR ES:[EDI] 

STOS DWORD PTR ES:[EDI]

修改标志寄存器中D位的值,然后在执行下面的指令: 

MOV EAX,12345678 

MOV EDI,12FFC4 

STOS BYTE PTR ES:[EDI] 观察EDI的值 

STOS WORD PTR ES:[EDI] 

STOS DWORD PTR ES:[EDI] 

注释:stos指令执行后,EDI会自动改变值,值的多少跟你数据宽度和DF位方向有关

3、REP指令:按计数寄存器(ECX)中指定的次数重复执行字符串指令 (依赖ecx)

ECX计数器

MOV ECX,10

REP MOVSD   执行完后,会改动的指令 esi  edi  ,ecx会变成0

REP STOSD    执行完后,会改动的指令  edi, ecx会变成0

用户空间         系统空间

0-7FFFFFFF , 80000000-FFFFFFFF

系统的地址是从哪里开始啊

内核空间的地址80000000——FFFFFFFF  高2G

用户空间是(应用程序) 0——7FFFFFFF                     低2G

std cld  操作df位  

std df位 1 

sld df位 0

[汇编基础] 存储模式

admin阅读(1000)评论(0)

存储模式的理解

判断高低位

数据: 0X1A2B   1A属于数据高位  2B属于数据低位

内存地址 [0x00000000] [0x00000001]    [0x00000000]低位 [0x00000001]高位

大端模式:

数据高位在低位,数据低位在高位

内存地址低位存储数据高位,内存地址高位存数据低位。

[0x00000000] 0x1A      ↓  大端存储模式(数据存储从大到小)

[0x00000001] 0x2B      ↓

小端模式:

数据低位在低位,数据高位在高位

内存地址低位,存数据低位,内存地址高位存数据高位。

[0x00000000] 0x2b     ↑ 小端存储模式(数据存储从小到大)

[0x00000001] 0x1A     ↑

绝大部分 X86  大部分应用都是小端存储模式   手机RAM是大端存储

编译器可以修改存储模式

mov esi,0x11223344   ESI:11223344

mov dword ptr ds:[],0x11223344

OD显示,在同一宽度内,是从高到低排序。  在所有宽度内,从低到高。

(od操作中很容易让新手懵逼,请看下图)

—————————————————————————————————-

QQ图片20180403212732.png

QQ图片20180403212738.png

QQ图片20180403212741.png

[汇编基础] 内存 内存编号表现形式

admin阅读(6013)评论(106)

clipboard.png

每个应用程序的4GB内存相当于空投支票,只有真正使用的时候,才会吧内存映射到物理内存中。 然后物理内存,再映射硬件内存。

为什么会有内存编号

<1> 内存太大没法起名字,所以只能用编号。当我们想向内存中存储数据,或者从内存中读取数据时,必须用到这个编号,就像写信必须要写收信人地址一样。每个编号,对应一个字节。8位

<2> 每个内存,都会有个内存编号,这个编号又称为内存地址(32位,前面0可以省略)。  32位的编码代表一块内存。每个编号对应一个字节,1byte,8bit 8个0和1

为什么一个应用程序的有4GB内存(因为他的寻址宽度,是32位,32bit  如果是64位的电脑,那么就会是64bit)

因为他的编码是  0x00000000 到 0xFFFFFFFF    0X00000000 也算一个编码。所以,总编码数为 0XFFFFFFFF+1 = 0x100000000 每一块内存是8bit。所以,整个应用程序所有分配的编号,所能寻找的内存总数为 0x100000000 * 8 = 0x800000000  

转成十进制 34359738368bit

34359738368 ÷  8 = 4294967296 byte

4294967296  ÷ 1024 = 4194304 KB

4194304    ÷ 1024 = 4096M

4096     ÷ 1024= 4GB

内存地址的五种形式

形式一 立即数

读取内存的值:

MOV EAX,DWORD PTR DS:[0x13FFC4]

向内存中写入数据:

MOV DWORD PTR DS:[0x13FFC4],EAX

形式二:[reg]  

reg代表寄存器 可以是8个32位通用寄存器中的任意一个

读取内存的值:

MOV ECX,0x13FFD0

MOV EAX,DWORD PTR DS:[ECX]

向内存中写入数据:

MOV EDX,0x13FFD8

MOV DWORD PTR DS:[EDX],0x87654321

形式三:[reg+立即数] 

读取内存的值:

MOV ECX,0x13FFD0

MOV EAX,DWORD PTR DS:[ECX+4]

向内存中写入数据:

MOV EDX,0x13FFD8

MOV DWORD PTR DS:[EDX+0xC],0x87654321

形式四:[reg+reg*{1,2,4,8}] 

为了读取内存更高,所以规定只能是1 2 4 8 内存对齐  第二个reg,不能是esp

读取内存的值:

MOV EAX,13FFC4

MOV ECX,2

MOV EDX,DWORD PTR DS:[EAX+ECX*4]

向内存中写入数据:

MOV EAX,13FFC4

MOV ECX,2

MOV DWORD PTR DS:[EAX+ECX*4],87654321

形式五:[reg+reg*{1,2,4,8}+立即数]

读取内存的值:

MOV EAX,13FFC4

MOV ECX,2

MOV EDX,DWORD PTR DS:[EAX+ECX*4+4]

向内存中写入数据:

MOV EAX,13FFC4

MOV ECX,2

MOV DWORD PTR DS:[EAX+ECX*4+4],87654321

[汇编基础] 寄存器

admin阅读(1774)评论(0)

首先介绍大家一个工具,这个工具叫OD,全名OllyDbg

QQ截图20170811150219.png

OD介绍跟相关操作:http://www.ithtw.com/9155.html

寄存器是CPU中,用来存储数据用的。

通用寄存器
32位 16位 8位
EAX AX AL
ECX CX CL
EDX DX DL
EBX BX BL
ESP SP AH
EBP BP CH
ESI SI DH
EDI DI BH

clipboard.png

4个数据寄存器(EAX、EBX、ECX和EDX)

2个变址/指针寄存器(ESI和EDI)

2个指针寄存器(ESP和EBP)

6个段寄存器(ES、CS、SS、DS、FS和GS)

1个指令指针寄存器(EIP)

1个标志寄存器(EFlags)

AH&AL=AX(accumulator):累加寄存器 

BH&BL=BX(base):基址寄存器 

CH&CL=CX(count):计数寄存器 

DH&DL=DX(data):数据寄存器 

SP(Stack Pointer):堆栈指针寄存器 

BP(Base Pointer):基址指针寄存器 

SI(Source Index):源变址寄存器 

DI(Destination Index):目的变址寄存器 

IP(Instruction Pointer):指令指针寄存器 

CS(Code Segment)代码段寄存器 

DS(Data Segment):数据段寄存器 

SS(Stack Segment):堆栈段寄存器 

ES(Extra Segment):附加段寄存器 

OF overflow flag 溢出标志 操作数超出机器能表示的范围表示溢出,溢出时为1. 

SF sign Flag 符号标志 记录运算结果的符号,结果负时为1. 

ZF zero flag 零标志 运算结果等于0时为1,否则为0. 

CF carry flag 进位标志 最高有效位产生进位时为1,否则为0. 

AF auxiliary carry flag 辅助进位标志 运算时,第3位向第4位产生进位时为1,否则为0. 

PF parity flag 奇偶标志 运算结果操作数位为1的个数为偶数个时为1,否则为0. 

DF direcion flag 方向标志 用于串处理.DF=1时,每次操作后使SI和DI减小.DF=0时则增大. 

IF interrupt flag 中断标志 IF=1时,允许CPU响应可屏蔽中断,否则关闭中断. 

TF trap flag 陷阱标志 用于调试单步操作.

[汇编基础] 位运算 计算机最底层的加减乘除是如何实现的。

admin阅读(3081)评论(0)

为什么有位运算?

因为计算机底层,是没有加减乘除的运算的,底层都是二进制数据,二进制数据要实现我们常用的加减乘除,就需要通过位运算来实现。

那么,如果想学逆向,到底要不要学位运算呢?

答案:需要了解,不是特别重要。

位运算的用途

有些特殊场景,可能需要你对某段二进制数据中的某一位值进行修改,这个时候就需要用到位运算。 

位运算是最底层的运算方式,速度也是最快的。例如,有些面试题会问 2*8的最高效的实现方式是什么? 

位运算介绍

1、与运算

两个位都为1时,结果才为1

比如:

       1011 0001 

  and(&)   1101 1000

——————————————

       1001 0000       

2、或运算

只要有一个为1就是1

比如:

       1011 0001

  or(|)  1101 1000

——————————————

      1111 1001

3、异或运算  

不一样的时候是1 

比如:

       1011 0001 

  xor(^)   1101 1000

——————————————

   0110 1001

4、非运算

0就是1  1就是0

比如:

  not(~)        1101 1000

——————————————

            0010 0111

5、左移

各二进位全部左移若干位,高位丢弃,低位补0

比如:

  shl(<<)         1101 1000  左移2位为:0110 0000

6、右移

各二进位全部右移若干位,低位丢弃,高位补0或者补符号位

比如:

shr    1101 0101       0011 0101 高位补0

对应C语言(>>)

unsigned int a = 10; 

printf("%d\n",a>>2); 

sar    1101 0101      1111 0101   高位补符号位

对应C语言(>>)

int a = 10;

printf("%d\n",a>>2); 

计算机底层,加法 减法 运算

A+b

先A异或B。得到结果C

再A与(&)B 运算 得到结果D   判断D是否进位,如果进位,左移一位。  如果没有进位,C就是A+B的结果。

D进位后结果,跟C异或。得到结果E

D进位后结果,跟C &运算 得到结果F

判断F是否进位,如果没有,则E就是A+B结果,如果进位了,就继续进位,异或……………..

下图是 4+5 的位运算流程

clipboard.png

减法跟加法一样。例如 1-2 其实就是1+(-2)

乘法运算 本身就是加法运算。 除法运算,本身就是减法运算。

2*8的最高效的实现方式

2*8 其实就是 2+2+2+2+2+2+2+2

2的二进制为 0010
2+2+2+2+2+2+2+2 的位运算结果是  0001 0000    (10,16进制)
所以,2*8的最高效实现方式就是直接  左移 3位


位运算实现简单的加密与解密

原文 0010
秘钥 1100

加密
     0010

xor(^) 1110

————-
     1100 

解密
     1100 

xor(^) 1110

————-
     0010

[汇编基础] 进制,数据宽度,有符号数,无符号数,源码反码补码

admin阅读(1589)评论(0)

计算机进制

3进制,就是由三个符号 0 1 2组成,逢三进一

10进制,就是由十个符号组成,逢十进一

N进制,就是由N个符号组成,逢N进一

符号随便定

任何进制,都是可以独立进行加减乘除运算。  进制如果用得好,以后可以用于加密自己的数据,增大破解难度。

在计算机中,只认2进制(除去某些特殊计算机)

数据宽度概念

计算机中,受硬件约束,存储数据都是有长度限制的。这个限制,就称之为数据宽度。如果你存储的数据,超过了数据宽度,就会被丢弃。 

通常,数据宽度有这么几个单位 

 bit(位)        代表最小单元 

 byte(字节)       1 byte = 8 bit

 world(字)      1 world = 2 byte

 doubleworld(双字) 1doubleworld=2world

其他语言,例如java 数据类型数据宽度如下:
byte 1个字节
short 2个字节
char 2个字节
int 4个字节
long 8个字节
float 4个字节
double 8个字节

堆栈缓冲区溢出才会终止程序报错

正常的数据宽度溢出,只能算是丢失精度,并不会报错

有符号数,无符号数  源码反码补码

内存中存储数据的时候,都是补码,至于你这个是不是有符号数,都是你怎么去使用,怎么申明

无符号数的编码规则

1、无符号数,是什么就存什么

2、所有的数都是补码存储的

3、有符号数正数的原码反码补码都一样。 跟无符号数一致

有符号数的编码规则

原码:

    最高位为符号位,其余各位为数值本身的绝对值,首位为0则为正数,首位为1则为负数

反码:

   正数:反码与原码相同

   负数:符号位为1,其余位对原码取反

补码:

   正数:补码与原码相同

   负数:符号位为1,其余位对原码取反加

有符号数

正数最大范围 00-7F    

负数最大范围 FF-80    (FF就是-1)

这也是编程语言中 byte类型的取值范围   正127 – 负128

刺蝴蝶De箫启灵

作死购站点VIP视频站站点