• 免杀笔记 ----->汇编基础


    免杀的第一件事,当然是学汇编啦,小编也是从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  ,在用户层只是起寻址的作用,在内核层涉及到段,页有别的作用

    cs :代码段
    ds :数据段
    ss :堆栈段
    es :附加段

    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,是则跳转
    举个栗子,首先我们构造一个Z标志位为1的一种情况
    然后我们直接构造一个JCC语句
    因为我们的Z位置是1,所以他是会直接跳到我们对应的位置的
    JNZ/JNE         jump if not zero;jump if  not equal     如果ZF为0则跳转
    这个其实和上面的那个差不多,我就不给大家演示了。

    10.堆栈图

    这个算是有点难吧,不过也是理解就好了,其实你把上面的指令都理解清除的话,就不会有问题。

    我们之前说的栈平衡其实有点问题,就是并不是说call回来之后esp一定会与原来相同,有些则是通过 add esp,0x...... 这样来实现ESP不变    ------->这样我们也叫他是栈平衡(看他是在函数外平衡堆栈还是在函数内平衡堆栈)


    具体堆栈图就不演示了,因为会非常麻烦,而且混乱(除非视频讲解),但是原理还是一样

  • 相关阅读:
    【JavaEE】Servlet API 详解(HttpServlet类)
    视觉大模型调研(Survey of Visual Foundation Model)
    JMeter 扩展开发:自定义 Java Sampler
    SQLite3 操作命令以及c/c++编程API和例子
    AI大模型时代网络安全攻防对抗升级,瑞数信息变革“下一代应用与数据安全”
    Go的简单入门:写一个Web应用
    基于Redis Cluster的分布式锁实现以互斥方式操作共享资源
    linux centos出现No space left on device解决方案
    【Paper】2022_离散时间多智能体系统编队-包围控制研究_李博凡
    2310d编译不过
  • 原文地址:https://blog.csdn.net/huohaowen/article/details/139701585