浅结代码混淆1

文章介绍了栈指针平衡的重要性,特别是在逆向工程中如何处理栈指针不平衡以进行有效的静态分析。文章通过示例解释了如何在IDA中调整栈指针,并探讨了花指令的使用,包括单层和多层jmp、互补条件跳转(jz/jnz)以及永真条件跳转的实现。同时,提到了call和ret指令在函数调用中的作用,以及它们如何被用于代码混淆和反调试。
摘要由CSDN通过智能技术生成

前言

本来是单开坑来写怎么除去花指令的,写着写着内容就加了很多其他涉及反调试的了,刚好花指令也算re里代码混淆内容,那干脆全部一起写了吧,努力写完。。。

栈指针平衡

在这就不具体说明栈了,知道入栈出栈,栈顶指针和栈底,先进后出就ok
在做题的时候,静态分析谁不想F5大法啊,然后施法失败

在这里插入图片描述

无法查看伪代码,做题时就很费时间,od不耐用啊,如果不解决这个问题,然后就只能分析汇编代码,对于逻辑简单的题目来说还可以理解,对于复杂点的题目就只能望洋兴叹。
因此现在立刻马上就解决这个问题!并熟悉其原理!好好的“羞辱”一下出题人!
题目来源:2018年九月份安恒杯月赛之NewDriver
在打开题目的时候ida就已经提示了是在哪个地方出现了栈指针不平衡的地方

在这里插入图片描述
(啊原来这个红色的提示就是栈指针不平衡啊,之前在手脱花指令的时候经常出现)
先让ida显示栈指针

在这里插入图片描述

是个逆向手都知道,栈帧开始将其函数的返回地址esp压进栈,用ebp来保存该值,压栈和出栈过程栈指针都会随之变化,那么在清理栈的最后,将其返回地址重新赋给esp,此时esp和ebp的值都是一样的都是返回地址,最后,栈指针会回到初始状态。

但看下图,进行pop指令后的栈指针和栈帧开始的栈指针并不相同
(注意:每条语句前的栈指针是这条语句未执行时的栈指针。

在这里插入图片描述

原栈指针:

在这里插入图片描述

这就引起了栈指针不平衡,所以需要手动调节栈指针让其平衡
在ida中使用快捷键AIT+K来进行栈指针的修改
因为要修改最后两句的栈指针,所以要在这两个指令前修改
那么应该改成多少呢:0x21E-0x4=0x21A

在这里插入图片描述

成功反编译:

在这里插入图片描述

为啥会出现栈指针失衡问题?
IDA有栈跟踪的功能,它在函数内部遇到ret(retn)指令时会做判断:栈指针的值在函数的开头/结尾是否一致,如果不一致就会在函数的结尾标注"sp-analysisfailed"。
一般编程中,不同的函数调用约定(如stdcall&_cdcel call)可能会出现这种情况
另外,为了实现代码保护而加入代码混淆(特指用push/push+ret实现函数调用)技术也会出现这种情况。
想要保护自己的代码可以加入花指令混淆或者垃圾代码,除了花指令外,给软件加壳也是一种混淆代码的方式

花指令

接上:花指令又是怎样影响栈指针的呢?
这部分后续再写,环境还没配置好。。。

另外,好听点保护代码,难听点就是我们打题的时候,花指令就会被用来干扰ida和od等软件对程序的静态分析使这些软件无法正常反汇编出原始代码。
一些比较常见的去花总结如下:

jmp

单层jmp和多层jmp

例如:

jmp LABEL1
  db junk_code;
LABEL1:
  ....

甚至是多层嵌套的jmp

//简单花指令-多层JMP嵌套
void example1()
{
    __asm {
        jmp LABEL1;
        _emit 68h;
    LABEL1:
        jmp LABEL2;
        _emit 0CDh;
        _emit 20h;
    LABEL2:
        jmp LABEL3;
        _emit 0E8h;
    LABEL3:
    }
    a = 99;
}

互补条件代替JMP跳转: jzjnz

利用jz和jnz的互补条件跳转指令来代替jmp。
如下,无论如何都会跳转到LABEL1处

  jz LABEL1
  jnz LABEL1
  db junk_code
LABEL1:

永真条件跳转:jnzjz

通过设置永真或者永假的,导致程序一定会执行,由于ida反汇编会优先反汇编接下去的部分(false分支)。也可以调用某些函数会返回确定值,来达到构造永真或永假条件。

比如:先对eax进行自身xor后test,ZF标志位肯定为1,那么无论如何都不会跳转到LABEL1

void example2_1()
{
    __asm {
        push eax;
        xor eax, eax;
        test eax, eax;
        jnz  LABEL1;
        jz LABEL2;
    LABEL1:
        _emit 0xC7;
    LABEL2:
        pop eax;//恢复ebx寄存器
    }
    a = 21;
}

示例1:简单jmp

可以骗过dbg,但是放在ida中就很容易看出来,无效跳转

题目来源:[HDCTF2019]Maze

简单的检查程序,32位带壳,直接去壳
在这里插入图片描述

放ida,可以明显看到核心代码就是在这,但是直接反汇编没有反应

在这里插入图片描述

可以看到其中一个跳转指令是跳转到下一条指令,相当于是没跳转,废话指令,而call指令调用了一个不是地址的地址,可以简单猜测存在花指令,导致ida编译失败

将其跳转指令nop掉,重启程序

在这里插入图片描述

发现已经可以正常反汇编咯

示例2:永真条件跳转

题目来源:来自NSSCTF平台
[HZNUCTF 2023 final]虽然他送了我玫瑰花
基本流程:无壳32位,丢ida查找字符串,做了两次脱花也做了一下比较,后面看了看wp发现还是差点到位呜呜还是要理解原理和多加练习

在这里插入图片描述

来到该字符串处反编译失败

在这里插入图片描述

来到该函数的开头,逐行看看,明显的花指令,直接nop即可,通过push ebx 构造永真条件进行跳转,直接nop掉即可

在这里插入图片描述

nop掉之后将整个函数按P键使其被识别为一个函数

在这里插入图片描述

反编译成功:

在这里插入图片描述

这里记录一下前两次脱花自己出现的问题:

1)第一次脱没有将pop ebx给nop掉,该花指令通过push ebx等一系列指令进行永真条件的构造,让其无法步进后面的代码,而这些都是在堆栈中进行的,太久了都忘了压栈出栈的操作是一连套的了
2)脱花连后面的push和jmp都直接nop掉了,wwwwwwwwwwwwwhhhhaaatttt是有必要的

在这里插入图片描述

示例3:互补条件跳转

题目来源:某春秋平台CTF逆向题-Random
无壳32位

在这里插入图片描述

找到主函数反编译

在这里插入图片描述

来看这个ida没有识别出来的函数到底是什么
显然易见,互补条件jzjnz跳转

在这里插入图片描述
在地址0x40146A处的跳转无论如何都会跳转到0x40146F处,使得IDA未能识别,call指令调用了一个不存在的地址(另外的数据和跳转地址混合在了一起)因此IDA没有将该部分正确识别为正确的跳转地址。

解决方法:按D键将该部分转换为数据,然后在正确的跳转地址处按C键,使其正常识别出函数,此外,由于0x40146A~0x40146E都是人工添加的无用代码(其中xor test指令nop掉对程序也没什么影响顺手nop掉了,感觉就是影响ZF标志位 方便给下面的互补条件的jzjnz提供条件),我们可以直接将该部分数据全部转为空指令nop

在这里插入图片描述

成功反编译:

在这里插入图片描述
在这里插入图片描述

感觉永真条件构造和互补条件跳转其实差不多,反正都会跳

call&ret

这里利用call和ret,在函数中修改返回地址,达到跳过thunkcode到正常流程的目的。可以干扰ida的正常识别(这个让我想起了之前在学PE文件的时候写shellcode,手改程序入口执行shellcode后再跳转到真正的程序入口,异曲同工

__asm{
    call label1
    _emit junkcode
label1:
    add dword ptr ss:[esp],8//具体增加多少根据调试来
    ret
    _emit junkcode
}

call指令:将下一条指令地址压入栈,再跳转执行
ret指令:将保存的地址取出,跳转执行

环境还配好加上还没找到相适配的题,先搁置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值