LOADING

加载过慢请开启缓存 浏览器默认开启

IDApy学习和花指令的初步学习

ida版本为IDA9.0

用VScode编写IDApython

  • 找到这个项目IDAcode,把下来的文件加到你ida的plugins文件夹中。
  • 修改idacode_utils/settings.py中的PYTHON路径为本地的python路径
  • 在本地中用控制台安装依赖
python -m pip install --user debugpy tornado
  • 在VScode中安装IDAcode插件
  • 在VScode按CTRL+Shift+P输入”Open User Settings(JSON)”打开后添加如下代码。
"python.autoComplete.extraPaths": [
        "E:\\CTFtoolsNEW\\Reverse\\IDA90\\python\\3"   //你的ida库路径
     ],
     "python.analysis.extraPaths": [
        "E:\\CTFtoolsNEW\\Reverse\\IDA90\\python\\3"   //你的ida库路径
   ],
  • 在VScode按CTRL+Shift+P,搜索ida即可找到想要的功能。
  • 在ida插件中打开idacode,显示Listening on 127.0.0.1:7065即成功。
  • 如果出现找不到依赖的报错,请在ida目录下找到并运行idapyswitch.exe,选择同样的python解释器(切换解释器可能会导致库的缺失,请自行安装缺失的库)。
  • 如果写的命令发到ida执行报错,请在VScode中选择同样的python解释器,不要用虚拟解释器。

花指令混淆以及原理

插入操作码

常见的花指令字节码
9A:CALL immed32
E8:CALL immed16
E9:JMP immed16
EB:JMP immed8
21:add
add esp,1; 对esp进行操作

必定跳转

cpu的指令集规定了许多汇编指令,如0XE8表示的是call这个指令,并规定后面的几个字节是call的参数(跳转的地址)。
IDA通过线性扫描从开始到结尾把每一个字节反汇编成汇编指令,例如IDA识别到了一个字节0xE8(0xE8不属于前一条汇编的参数里),那么就把这个字节汇编成call,并把后面的字节识别成参数,然后继续往下分析。
那么我们是不是可以在程序中插入一个0xE8来干扰IDA的解析呢。很明显是可以的,这就是花指令的一种实现方法。如下程序中我们用_asm{}插入一段汇编代码,并用_emit插入一个0xE8的字节码。

__asm{
   jz start;
   jnz start;
   _emit 0xE8;
}
start:
   printf("yes");

在上面的代码中,因为jz与jnz都是跳转到start所以程序本身就不会执行到0xE8这个位置,但是在IDA反汇编时却会被扫描出来,从而导致把我们的0xE8识别成call,把后面的字节码识别成参数,就会导致接下来的分析错误。那么构建一个花指令其实就是想帮法构建一段内联汇编,并插入一些不会被程序执行汇编指令字节码。
有时候跳转会有很多次,有较多的花指令。

通过xor eax, eax;得到结果为0,从而使zf标志为1,造成jz必然跳转。

int main()
{
    _asm {
    xor eax, eax;// eax ^ eax = 0 
    jz s; // 必然成立跳转,一定会跳转
    add esp,1; 
    _emit 0x22; 
    _emit 0x33; 
    s:
    }
    printf("test \n");
}

jmp跳转

$+5表示跳到当前地址后偏移5的位置

int main()
{
    _asm {
    
        jmp $+5
        _emit 0x71
        _emit 2
        _emit 0xE9
        _emit 0xED
   }
 lable:
    printf("ok2");

    return 0;
}

解决方法:找到垃圾代码按U然后nop掉,再按C,最后再在函数开头按P创建函数。


操作栈

栈的操作是需要平衡的,一个入栈后面必要一个出栈平衡,但是如果我们不用pop出栈,那么IDA可能就会识别到栈的不平衡而无法反编译。

不执行的栈操作

在下面的代码中我们插入了对栈顶进行操作的操作码,实际上因为永恒跳转的原因,这个操作是不会执行的,但是如果被ida识别到,那么就会认为栈是不平衡的,从而导致上面的问题。

int main() {
    
    int main()
{
    _asm {
    xor eax, eax;// eax ^ eax = 0 
    jz s; // 必然成立跳转,一定会跳转
    add esp,0x100;   //操作栈顶指针,实际上不会执行
    _emit 0x22; 
    _emit 0x33; 
    s:
    }
    printf("test \n");
}
start:
    printf("hello world");
}

我们在option中的general中右上角勾选Starck pointer和设置number of byte 为8。来显示字节码和栈指针以便我们分析。

image-20241215140918996

我们可以轻易发现我们写的花指令,发现ida已经识别到了栈顶指针的异常,这个时候我们只要把add的那个操作码nop掉就行。

image-20241215141351396

会执行的栈操作

在下面的代码中我们push了寄存器eax,这回让esp-4然后再把eax的值放入esp指向的地址中,正常来说,我们在后面要pop eax(把esp指向地址的值赋值给eax再把esp+4)来平衡栈,但是我们这里直接用mov来操作esp来平衡,这就会导致ida识别到栈不平衡的问题。

int main() {
    
    __asm {
        push eax;
        add esp, 4;
    }
start:
    printf("hello world");
}

这个花指令在高版本的IDA(IDA9)中好像已经不管用了,如果有这种花指令的话直接nop就行。

CALL/RET跳转

call在执行时会把返回地址入栈,并跳转到函数执行,但是如果在后面的过程中修改这个返回地址,那么当ret返回的时候就会跳转到其他的方执行。如下代码实现了这种效果,这会增加我们的分析难度,但本质还是跳转,nop掉垃圾数据就行。可以直接把call和ret部分全nop掉。

__asm {
    call a;
    _emit 0xE8;
a:
    add dword ptr ss:[esp], 0x1; //表示以四字节(dword)写入栈段(ss)到esp指向的值(ptr表示把后面的数当成指针使用)
    ret;