免杀的第一件事,当然是学汇编啦,小编也是从0开始呢!!!一起来看看吧!
免责声明,本人所有教学内容仅供参考,无任何不良引导,内容资料来自网络,如有侵权请联系删除!!!!
1.数据宽度
学计算机的大家一定不会陌生,比如经常听什么一个字节等于8个比特 ....这种之类的。
其实可以分为以下这些单位
- 比特 比如像我们平时所看见的那些 0101101001010这样子,一位就是一个bit
- 字节 也可以叫做Byte,就好像我们上面说的,一个字节就是8个比特
- 字 是计算机进行数据存储和数据处理的运算的单位。而一个字又是两个字节
这里就要说一下,如果你往一个32位寄存器里面存很大的数,肯定是不行的,超过了数据宽度
2.进制
我们在生活中用的最多的就是十进制了 ,但是计算机可不是用的十进制 而是二进制01010这些
其中我们在免杀中需要熟悉的,就是2进制和十进制吧,8进制的话少一点
就比如 0x08 = 8 = 0100 , 0x10 = 16 = 10000 0xC = 12 = 1100
这种简单的计算还是要会
3.通用寄存器
我们一开始先是去研究32位下的寄存器
- EAX
- ECX
- EDX
- EBX
- ESP
- EBP
我们去OD看一下mimikatz的就知道了 ,这个OD只能打开32位的哦,你别拿64位的mimikatz 那当然是打不开的
我们看见最右边那个就是对应的通用的寄存器
4.OD的使用
虽然在这里插入感觉有的点突兀,但是既然提到了OD,那还是要给大家去讲一下怎么使用的
- F8 单步步过
当我们想要从0075ECF7走到0075ECFA的时候只需要F8 一下就好了!!!
- F7 单步步入
就拿我们的c++来举例子
当我们输出完1之后我们就要进入函数,如果f8的话就直接略过函数,f7则是单步进入
那这个举例子,如果我们继续f8的话,就会直接执行这个函数的结果,然后走到0075ED0E
如果我们F7的话,就会进入到栈(可以看见前面的地址都不一样了)
- F2 打断点
假如我现在有一个程序在 0075EF12这个位置我想让他跑去 0075EF27这个位置
我们只需要在 0075EF27 这个位置点一下,然后直接F2
他变红了,但是此时我们程序还是运行到了0075EF27这个位置而已
- F4 运行到指定位置
继续上面,我们只要F4一下,就能让他跑到0075EF27这个位置
5.汇编指令
1.mov
其实就是move,我来操作一遍你就懂了
这个指令就是将334的值移动到eax这个寄存器里面
看,瞬间就变红了 其中eax就是寄存器,然后0x334我们称之为立即数
当然了,除了我们直接这样子赋值,也可以直接这么写
意思就是把Dword(32位数据类型)ptr 在 0x00BF5F9D4这个地址的值给拿出来,丢到EAX里面
不出什么以外的话,EAX应该是变成了00B5F9E8
当然,也是可以直接把寄存器当作一个地址来用的,但这时候寄存器的值就变成了一个地址,而不是寄存器的值 我们后买你lea会讲到
2.add sub
这个看起来都很明白了吧,我就直接用一个示例来演示一下得了
这个意思就是EAX=EAX+0x11 有没有编成里面的+=的意思捏,嘻嘻嘻
不出意外就是 00CFFC55
那么同理 SUB也是一样的!!! 我就不过多演示了
3.and or xor
这个可能对于学过离散数学的会比较好理解,我们还是来举一个例子
4 and 7 也就是离散数学上的 4 和取7 (二进制)
- 4:0100
- 7:0111 这两个合取结果应该是 0100 也就是4
结果也是如此
然后就是我们的XOR 也就是异或 为我们还是拿上面的来举例子
- 4:0100
- 7:0111 这两个异或结果应该是 0011 (异或就是只有01同时存在才为1)
那么答案不出意外的话,应该就是 3
4.lea
这个用的多,最简单的就是这样
将0x446这个地址(不是这个地址的数)直接给到EAX,所以EAX应该会变成446
但是一般没人会这么用,我们更多的是直接用的寄存器
至于为什么要这样子用,我们讲到堆栈的时候你就懂了
最常用的更是我们的立即数配和寄存器
至于为什么,我们后面再说
5.push pop
这两个都是涉及到了堆栈,我们下面再讲
6.cmp
这个我们也放在下面讲
5.内存寻址
这个其实我们在上面的add的时候就已经提到了一些了
其实也就是通过内存地址来获取或者得到一些值,这个内存地址,你可以直接写地址,或者直接写寄存器。
不过还是要说一下,我们刚才的那个 dword ptr ds:[0x2121312]这个例子
我们里面提到的ds ,在用户层只是起寻址的作用,在内核层涉及到段,页有别的作用
6.代码是怎么执行的
我们一定用过VS或者devcpp等其他编译器写过代码,那么他的底层逻辑是什么呢???
因为我们的CPU只能看的懂0 1 ,所以一般是这样的一个过程
c/cpp代码 ---> 汇编代码 ------> 硬编码 ------> CPU
我们去VS看一下,就像这段最简单的hello world
我们右键转到反汇编
就能看见我们的汇编代码
然后我们再把他转换成硬编码
然后就是到了CPU那里了,我们的 "Hello World!" 就是这么被执行的
7.标志寄存器
我们再OD里面除了能看见我们的通用寄存器,我们还能看见这个标志寄存器
对于32位下,我们的标志寄存器叫做EFlag ,也就是我们看见上面的EFL
在64位下,我们的标志寄存器就叫做RFlag
其中EFlag寄存器各个位置有各个位置的不同作用,特别是ZF这个位置,也就是上面我们看见的Z这个位置,我们来举个栗子
我们用CMP这个汇编指令的时候,我们总不能把返回结果存储在eax中吧,这时候就要用到标志寄存器中的ZF这一个位置
我们能看见Z这个位置变成了1 就是说明了结果相同,这个到后面的JCC语句会用
同样的还有CF
CF:进位标志CF(Carry Flag),如果运算结果的最高位产生了一个进位或借位,其值为1,否则为0
OF
如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。
8.堆栈
栈Stack,堆Heap 这两个对于程序员来说更是经典
栈:用于存储函数的局部变量和函数调用的上下文信息。栈上的数据在函数执行结束时自动释放。
这个其实很好理解,就像这个代码,我们的两个a都是开在了栈上面
堆:用于动态分配的内存,由程序员手动分配和释放。在C语言中,使用
malloc()
或calloc()
等函数分配的内存位于堆上。
这个也很好理解,像下图,就是在堆区上开辟了一个长度为三的整数数组,并且用p指向了首地址
而规定了,进出函数的前后堆栈的地址不能改变
Jmp
这个其实就是Jump的缩写,就是直接跳转到其他地址
我们之际JMP一下,就能直接跳到我们对应的地方了
但是JMP不会堆堆栈的位置进行改变,怎么看是否改变呢??? 还记得我们之前的通用寄存器码
- ESP 指向栈顶
- EBP 指向栈底
我们可以去尝试,堆栈在JMP下是不会发生改变的
Call Ret
这两个一般都是搭配使用的
- Call 跳转到指定地址并返回到当前指令的下一行,当我们调用这个的时候,他会先将esp提升四个字节,并将返回地址压入堆栈 (提栈)
- ret 返回到指定地址,当我们调用这个指令的时候,会将esp降低四个字节,并且从堆栈中取出返回地址,传递给EIP(运行的地址)
这么说似乎有点抽象,我们来举个栗子就懂了
首先我们遇到了一个call,然后有以下的信息
- 当前的地址是 EF12 下一步的地址是 EF17
- 当前的EBP是 FB34 ESP的地址是FB28
然后我们单步F7进入一下
然后又能收集到以下的信息
- 我们的ESP减少了4(提栈)ESP变成了FB24
- 并且我们的返回地址EF17被压栈,压入了FB24
然后我们直接跑到retn那里
可以看见函数跑了一通之后我们的ESP还是FB24里面还是EF17,然后我们F8走一下
还是能收集到以下的信息
- retn成功的返回了我们的地址
- ESP是FB28 EBP是FB34 和函数一开始调用时候的栈地址是一样的!!!!!
Push Pop
这两个其实和Call 和Ret有点像的,但是也有差异
在介绍这两个命令之前,还得讲一下栈的数据存储,是从高到低的,比如我上一个是94,那么我压栈存数据之后就应该存在90这个地址!
Push 压栈,将ESP减少4,然后将数据压入。
有点类似call的功能,只是将返回的地址更换成了数据
Pop 出栈 将ESP加4,并且将原来ESP的数值取出放到指定的地址或者寄存器
有了上面的Push的基础,我们就不难理解我们的pop了,直接举例子吧
像这样,就是直接把ESP的值取出来,mov到EAX,然后将ESP的值加上4
9.JCC语句
JCC不知道能不能被翻译为Jump Condition Code
其实就是和标志寄存器实现了一个联动,一般格式如下
JCC target_address
JZ/JE jump if zero;jump if equal 其实就是检查ZF这个位是不是为1,是则跳转
![](https://img-blog.csdnimg.cn/direct/9d07c5e31bca4a1b925daac241a03db5.png)
![](https://img-blog.csdnimg.cn/direct/f3e7c6add58a4faeb42726fa13f0dce1.png)
![](https://img-blog.csdnimg.cn/direct/f043a320910d4dddaa4c39ebb6162ad6.png)
JNZ/JNE jump if not zero;jump if not equal 如果ZF为0则跳转
10.堆栈图
这个算是有点难吧,不过也是理解就好了,其实你把上面的指令都理解清除的话,就不会有问题。
我们之前说的栈平衡其实有点问题,就是并不是说call回来之后esp一定会与原来相同,有些则是通过 add esp,0x...... 这样来实现ESP不变 ------->这样我们也叫他是栈平衡(看他是在函数外平衡堆栈还是在函数内平衡堆栈)
具体堆栈图就不演示了,因为会非常麻烦,而且混乱(除非视频讲解),但是原理还是一样