美文网首页
恶意代码分析实战 第六章 识别汇编中的C代码结构

恶意代码分析实战 第六章 识别汇编中的C代码结构

作者: doinb1517 | 来源:发表于2021-12-23 15:03 被阅读0次

    全局和局部变量

    全局变量可以被一个程序中的任意函数访问和使用
    局部变量只能在它被定义的函数中访问
    
    在反汇编代码中:
    全局变量通过内存地址引用
    局部变量通过栈地址引用
    

    代码清单6-1:两个全局变量

    #include<stdio.h>
    
    int x = 1;
    int y = 2;
    
    void main() {
        x = x + y;
        printf("total=%d", x);
    }
    

    代码清单6-2:两个局部变量

    #include<stdio.h>
    void main(){
        int x = 1;
        int y = 2;
        x = x+y;
        printf("total=%d", x);
    }
    

    使用gcc编译为64位程序,使用IDA打开(也可以使用gcc -S 查看汇编代码)


    demo1.png

    此部分代码为未经标记的汇编代码。


    demo22.png

    这部分代码是经过IDA标记的代码。

    demo2.png

    反汇编算数操作

    以下的C代码进行了加发,减法,自增自减,去模五种运算

    C语言代码

    suanshu1.jpg

    汇编代码

    suanshu2.jpg

    这里需要解释一下cdq的作用,cdq其实多出现在除法运算之前,这里使用除法来进行取模运算。

    CDQ 是一个让很多人感到困惑的指令。这个指令把 EAX 的第 31 bit 复制到 EDX 的每一个 bit 上。 它大多出现在除法运算之前。它实际的作用只是把EDX的所有位都设成EAX最高位的值。也就是说,当EAX <80000000, EDX 为00000000;当EAX >= 80000000, EDX 则为FFFFFFFF。
    
    例如 :
             假设 EAX 是 FFFFFFFB (-5) ,它的第 31 bit (最左边) 是 1,
             执行 CDQ 后, CDQ 把第 31 bit 复制至 EDX 所有 bit
             EDX 变成 FFFFFFFF
            这时候, EDX:EAX 变成 FFFFFFFF FFFFFFFB ,它是一个 64 bit 的大型数字,数值依旧是 -5。
    
    备注:
     EDX:EAX,这里表示EDX,EAX连用表示64位数
    

    在使用div或者idiv指令时,是在用edx:eax除操作数并将结果保存在eax中,余数保存在edx中,最后把余数赋值给b。

    void main(){
        int a = 0;
        int b = 1;
        a = a + 11;
        a = a - b;
        a--;
        b++;
        b = a % 3;
    }
    
    suanshu22.png

    识别if语句

    对于一个if语句必定有一个条件跳转
    不是所有的条件跳转都对应一个if语句
    

    C语言代码

    if1.jpg

    汇编代码

    if2.jpg

    基于cmp的结果进行判断是否继续执行还是跳转到40102B

    用IDA Pro图像化分析函数

    if3.jpg if4.png
    no为红色箭头
    yes为绿色箭头
    蓝色表示下一个执行块
    

    识别嵌套的if语句

    C语言代码

    if5.jpg

    汇编代码


    if6.jpg if7.jpg

    1,2,3处发生了三次跳转。

    doubleif.png

    这类多重if嵌套还是直接看IDA图像比较方便。

    识别循环

    找到for循环

    for循环的四个组件

    • 初始化
    • 比较条件‘
    • 执行代码
    • 递增递减

    C语言代码

    # include<stdio.h>
    void main(){
        int i;
        for(i=0;i<100;i++){
            printf("i equals %d.\n", i);
        }
    }
    

    汇编

    for1.jpg

    IDA Pro图形化


    for2.jpg for3.png

    当比较语句为false的时候,执行循环四步,有一个很明显的闭环。红的箭头部分是跳出循环,可以进行下一步操作,本示例程序中,没有进行下一步动作,仅清理栈并返回。

    找到while循环

    C语言代码

    while1.jpg

    汇编代码


    while2.jpg

    停止代码的时候应该关注1处的条件跳转。

    理解函数的调用约定

    函数调用在汇编代码中的表现可能不一样,调用约定决定了函数调用发生的方式。这些约定包含了参数被放在栈上或者寄存器中的顺序,以及是由调用者或者被调函数负责在函数执行完毕后清理栈。

    一个函数调用的伪代码
    
    call1.jpg

    最常见的三个调用约定:cdecl,stdcall,fastcall,下面讨论他们的关键区别。

    1. cdecl

    call2.jpg
    在cdecl约定中:
    参数从右到左按序被压入栈
    当函数完成时由调用者清理栈。
    将返回值保存在EAX中。
    

    2.stdcall

    stdcall主要约定了栈的清理是由被调函数来执行的。stdcall是Windows API的标准调用约定。任何调用这些API的代码都不需要清理栈,清理栈由实现API函数代码的DLL程序所承担

    3.fastcall

    在fastcall中,前面的一些参数被传到寄存器(典型的是前两个),备用的寄存器是EDX和ECX。如果需要,剩下的参数再以从右到左的次序被加载到栈上。
    使用fastcall比其他约定更高效,因为代码不需要涉及过多的栈操作

    4.压栈与移动

    C代码

    mov1.jpg

    adder函数汇编代码

    move2.jpg
    即使是同一种编译器,在调用约定方面也可能存在差异性,这依赖于各种选项和设置
    
    move3.jpg

    分析switch语句

    if语句通常以两种方式被编译:

    使用if样式和跳转表

    IF样式

    C代码

    switch1.jpg

    反汇编代码

    switch2.jpg
    switch3.jpg

    采用三对比较跳转,最后一句无条件跳转

    IDA Pro图形化

    switch4.jpg

    整个图线看上去就像是多层嵌套的IF,在if判断为False的情况下执行下一个判断。

    switch5.png

    跳转表

    上面三个case的switch会被编译器编译为if,else结构,但是如果case的数量线性增加,或者刚好我们需要的case在最后一个,那么算法的时间复杂度不就变成了O(n)吗,其实并不是这样,当case的数量增加,编译器会使用跳转表优化代码,降低算法的时间复杂度。

    C语言代码

    #include<stdio.h>
    void main(){
        int i=2;
        switch(i)
        {
            case 1:
            printf("1");
            break;
            case 2:
            printf("2");
            break;
            case 3:
            printf("3");
            break;
            case 4:
            printf("4");
            break;
            case 5:
            printf("5");
            break;
            default:
            break;
        }
    }
    
    jump0.jpg
    jump1.jpg jump2.jpg
    jump3-1.png

    [rbp+var_4]存储的是case 一共有六种case(从0到5)所以是和case-1做比较

    jump3-2.png

    lea rdx, ds:0[rax*4]这个指令中是rax*4加上跳转表的基址来确定要跳转到哪一个case块,乘以四是因为跳转表中每一项是一个4字节大小的地址。

    jump3-3.png

    IDA Pro上有标记这是跳转表。

    反汇编数组

    数组a是局部定义的,数组b是全局定义的

    C代码

    string1.jpg

    汇编代码

    string2.jpg string3.png

    eax是索 引,每个元素的大小是4,数据的基址加上偏移来访问正确的数组元素。

    string4.png

    识别结构体

    C代码

    struct1.jpg

    main函数反汇编代码

    struct2.jpg

    test函数反汇编代码

    struct3.jpg struct4.jpg

    分析链表遍历

    C代码

    list1.jpg list2.jpg

    汇编代码

    list3.jpg list4.jpg

    小结

    • 从细节中抽象
    • 不要过度死磕汇编,从全局角度看待问题

    相关文章

      网友评论

          本文标题:恶意代码分析实战 第六章 识别汇编中的C代码结构

          本文链接:https://www.haomeiwen.com/subject/oubhqrtx.html