欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[翻译]使用 IDAPython 写一个简单的x86模拟器

[翻译]使用 IDAPython 写一个简单的x86模拟器

使用 IDAPython 写一个简单的x86模拟器

我经常遇到一些IDAPython脚本,注意到这些脚本通常使用低效或者错误的IDAPython的API来反汇编或者解码指令(比如,使用idc.GetMenem()或者idc.GetDisasm())。因此,在本篇文章中,我将阐述如何使用IDAPython的指令解码函数来编写一个非常简单的x86模拟器。本文的目的是通过编写这个模拟器来演示IDAPython API的正确使用方式。读完本文后,你应该就能使用IDAPpython解决一些相似的问题了。

目录

  • 概览
  • 反汇编程序
  • 编写模拟器
  • 指令解码快速入门
  • 确定挑战函数的范围
  • 模拟执行指令

概览

让我们从下边的列表展示的示例代码开始,其中包含12个挑战函数,这些函数在循环中被调用并且显示出运行结果。我们的目标编写一个能够不依赖第三方模拟器而静态的计算出函数的返回值的模拟器。

#include <stdio.h>

#include <cstdint>

#include <time.h>

#include <stdlib.h>

typedef uint64_t (*challenge_proto_t)(uint32_t, uint32_t);

//-------------------------------------------------------------------------

uint64_t challenge_1(uint32_t c1, uint32_t c2) // 39 operation(s)

{

    uint64_t result;

    __asm

    {

        pushad

        mov eax, [c1]

        mov edx, [c2]

        not eax

        dec edx

        xor edx, eax

        xor edx, eax

        inc eax

        not eax

        sub edx, 0x27c12466

        inc eax

        dec edx

        not edx

        inc eax

        add eax, 0x273804ca

        xor edx, 0xaa5a1584

        sub eax, edx

        not edx

        xor eax, 0xf94f7d8c

        dec edx

        dec eax

        sub eax, edx

        not edx

        dec edx

        sub edx, 0xd7b41b83

        xor eax, 0xa551a9c7

        add eax, eax

        dec eax

        inc eax

        not eax

        add edx, 0xa551b974

        inc edx

        dec edx

        not edx

        xor eax, 0x200d519

        not edx

        not eax

        sub edx, 0xeb15b7ef

        xor eax, 0xb2558b8c

        xor eax, 0xda288d90

        not eax

        not edx

        mov dword ptr[result], eax

        mov dword ptr[result + 4], edx

        popad

    }

    return result;

}

// .

// .

// challenge_2 .. challenge_12

// .

// .

challenge_proto_t challenge_funcs[] = {

    challenge_1,

    challenge_2,

    challenge_3,

    challenge_4,

    challenge_5,

    challenge_6,

    challenge_7,

    challenge_8,

    challenge_9,

    challenge_10,

    challenge_11,

    challenge_12

};

//-------------------------------------------------------------------------

int main(int argc, char *argv[])

{

    if (argc < 4)

    {

        printf("challenge func[0..%d] challenge1-32 challenge2-32 -> result-64\n", _countof(challenge_funcs));

        return -1;

    }

    uint32_t f = atol(argv[1]) % _countof(challenge_funcs);

    uint32_t c1 = atol(argv[2]);

    uint32_t c2 = atol(argv[3]);

    printf("challenge_%d(%d, %d) -> %016I64X\n", f, c1, c2, challenge_funcs[f](c1, c2));

    return 0;

}

在实际工作中,大家可能会发现自己就是处于这种类似的情况,希望将某个函数视为黑盒子,而不是将其反编译成伪代码。在这种情况下,有很多选择:

  • 在运行期间使用Appcall然后直接调用挑战函数
  • 使用反编译器将其算法转化为伪C语言代码然后重新编译成可以使用的代码,最后使用
  • 得到挑战函数的反汇编代码,然后重新汇编,最后使用
  • 使用模拟器框架(比如说Unicorn engine)来模拟挑战函数

先让我们来编译一下这个程序,然后使用参数c1=123c2=456运行一下:

C:\>for /l %a in (0, 1, 11) do @test.exe %a 123 456

challenge_0(123, 456)  -> 8FDCE2E203FCAAF2

challenge_1(123, 456)  -> E0317E1AB061ED8B

challenge_2(123, 456)  -> A0A0E0C2279BE734

challenge_3(123, 456)  -> 5D18D0A79D07D7D8

challenge_4(123, 456)  -> 2583B4EEB62E6042

challenge_5(123, 456)  -> D5261E0275AB9805

challenge_6(123, 456)  -> F2B3282E143F7927

challenge_7(123, 456)  -> 9B9B3CBB0169F4CD

challenge_8(123, 456)  -> EF51086C5D1AF235

challenge_9(123, 456)  -> FC8A97125C0EA232

challenge_10(123, 456) -> EEAE8BEB7996D2E7

challenge_11(123, 456) -> 4F36E6A65AB03929

反汇编程序

先来反汇编一下测试程序然后定位到challenge_funcs函数列表,第一个挑战函数如下:

;

; The challenge functions as referenced from main()

; (12 functions)

;

.rdata:0041749C 00 10 40 00             challenge_funcs dd offset sub_401000

.rdata:0041749C                                   ; DATA XREF: _main+57↑r

.rdata:004174A0 90 10 40 00             dd offset sub_401090

.rdata:004174A4 30 11 40 00             dd offset sub_401130

.rdata:004174A8 D0 11 40 00             dd offset sub_4011D0

.rdata:004174AC 80 12 40 00             dd offset sub_401280

.rdata:004174B0 30 13 40 00             dd offset sub_401330

.rdata:004174B4 A0 13 40 00             dd offset sub_4013A0

.rdata:004174B8 30 14 40 00             dd offset sub_401430

.rdata:004174BC C0 14 40 00             dd offset sub_4014C0

.rdata:004174C0 30 15 40 00             dd offset sub_401530

.rdata:004174C4 E0 15 40 00             dd offset sub_4015E0

.rdata:004174C8 80 16 40 00             dd offset sub_401680

;

; The first challenge function

;

.text:00401000                         ; int __cdecl sub_401000(int c1, int c2)

.text:00401000                         sub_401000 proc near

.text:00401000 ; CODE XREF: _main+65↓p

.text:00401000 ; DATA XREF: .rdata:challenge_funcs↓o

.text:00401000

.text:00401000                         var_8= dword ptr -8

.text:00401000                         var_4= dword ptr -4

.text:00401000                         c1= dword ptr  8

.text:00401000                         c2= dword ptr  0Ch

.text:00401000

.text:00401000 55                      push    ebp

.text:00401001 8B EC                   mov     ebp, esp

.text:00401003 83 EC 08                sub     esp, 8

.text:00401006 53                      push    ebx

.text:00401007 56                      push    esi

.text:00401008 57                      push    edi

.text:00401009 60                      pusha

.text:0040100A 8B 45 08                mov     eax, [ebp+8]

.text:0040100D 8B 55 0C                mov     edx, [ebp+c2]

.text:00401010 F7 D0                   not     eax

.text:00401012 4A                      dec     edx

.text:00401013 33 D0                   xor     edx, eax

.text:00401015 33 D0                   xor     edx, eax

.text:00401017 40                      inc     eax

.text:00401018 F7 D0                   not     eax

.text:0040101A 81 EA 66 24 C1 27       sub     edx, 27C12466h

.text:00401020 40                      inc     eax

.text:00401021 4A                      dec     edx

.text:00401022 F7 D2                   not     edx

.

.

.

.text:00401076 F7 D2                   not     edx

.text:00401078 89 45 F8                mov     [ebp+var_8], eax

.text:0040107B 89 55 FC                mov     [ebp+var_4], edx

.text:0040107E 61                      popa

.text:0040107F 8B 45 F8                mov     eax, [ebp+var_8]

.text:00401082 8B 55 FC                mov     edx, [ebp+var_4]

.text:00401085 5F                      pop     edi

.text:00401086 5E                      pop     esi

.text:00401087 5B                      pop     ebx

.text:00401088 8B E5                   mov     esp, ebp

.text:0040108A 5D                      pop     ebp

.text:0040108B C3                      retn

.text:0040108B

.text:0040108B                         sub_401000 endp

.text:0040108B

可以轻松的定位到challenge_funcs表,因为这个表被main引用了。可以看见,第一个函数也包括其他所有的函数,具有非常独特的格式/模式,我们将基于该模式来设计模拟器。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功