Engineering Note

プログラミングなどの技術的なメモ

Python x86エミュレータの作成(org疑似命令)

cpu

本記事はPythonで簡単なx86エミュレータを作成します。

前回では簡単なオペコード(mov命令とjmp命令のみ)を読み込み、実行するプログラムを作成しました。

今回はorg疑似命令を実装し、プログラムの配置場所を調整する方法について学びます。

 

 

org疑似命令とは

前回では、mov命令とjmp命令のみでCPUが動作する雰囲気を味わってみました。


 

その際、読み込まれたプログラムは、メモリ配列の先頭に格納されていましたが、プログラムを配置する番地を変更することが可能で、その際に使用するのがorg疑似命令です。

前回のアセンブリ言語プログラムにorg疑似命令を追加し、アセンブラにプログラムの配置場所を指定します。

 

;test.asm
BITS 32
    org 0x7c00
    mov eax, 41
    mov ebx, 42
    mov ecx, 43
    mov edx, 44
    jmp 0

 

なお、BIOSでは標準的に0x7c00番地に配置するようです。

 

Pythonによるスクリプトの作成

それでは、Pythonでorg疑似命令に対応したスクリプトを作成します。
なお、前回のショートジャンプ命令では、ジャンプできる範囲が限定的のため、新しいジャンプ命令(ニアジャンプ命令)も実装します。

 

# emulator.py
class Emulator:
    def __init__(self):
        self.register_name = ["EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI"]
        self.registers = {
            "EAX": 0x00,
            "ECX": 0x00,
            "EDX": 0x00,
            "EBX": 0x00,
            "ESP": 0x00,
            "EBP": 0x00,
            "ESI": 0x00,
            "EDI": 0x00
            }
        self.eflags = None
        self.memory = None
        self.eip = None
        self.instructions = [None for i in range(256)]

    def init_instructions(self):
        for i in range(8):
            self.instructions[0xb8 + i] = self.mov_r32_imm32
        self.instructions[0xeb] = self.short_jump
        self.instructions[0xe9] = self.near_jump

    def create_emu(self, size, eip, esp):
        self.eip = eip
        self.registers["ESP"] = esp
        self.memory = [0x00 for _ in range(size)]

    def dump_registers(self):
        for i in range(len(self.registers)):
            name = self.register_name[i]
            print("{} = 0x{:08x}".format(name, self.registers[name]))
        print("EIP = 0x{:08x}".format(self.eip))

    def mov_r32_imm32(self):
        reg = self.get_code8(0) - 0xb8
        value = self.get_code32(1)
        reg_name = self.register_name[reg]
        self.registers[reg_name] = value
        self.eip += 5
        if self.eip >= 0x100000000:
            self.eip ^= 0x100000000

    def short_jump(self):
        diff = self.get_sign_code8(1)
        if diff & 0x80:
            diff -= 0x100
        self.eip += (diff + 2)

    def get_code8(self, index):
        code = self.memory[self.eip + index]
        if not type(code) == int:
            code = int.from_bytes(code, 'little')
        return code

    def get_sign_code8(self, index):
        code =  self.memory[self.eip + index]
        code = int.from_bytes(code, 'little')
        return code & 0xff

    def get_code32(self, index):
        ret = 0
        for i in range(4):
            ret |= self.get_code8(index + i) << (i * 8)
        return ret

    def get_sign_code32(self, index):
        return  self.get_code32(index)

    def near_jump(self):
        diff = self.get_sign_code32(1)
        if diff & 0x80000000:
            diff -= 0x100000000
        self.eip += (diff + 5)

mem_size = 1024 * 1024

emu = Emulator()
emu.create_emu(mem_size, 0x7c00, 0x7c00)
binary = open('test.bin', 'rb')
offset = 0x7c00
while True:
    b = binary.read(1)
    if b == b'':
        break
    emu.memory[offset] = b
    offset += 1
binary.close()

emu.init_instructions()
while emu.eip < mem_size:
    code = emu.get_code8(0)
    print("EIP = 0x{:02x}, Code = 0x{:02x}".format(emu.eip, code))
    if emu.instructions[code] == None:
        print("\n\nNot Implemented: 0x{:02x}".format(code))
        break
    emu.instructions[code]()
    if emu.eip == 0x00:
        print("\n\nend of program.\n\n")
        break

emu.dump_registers()

 

動作確認

それでは、上記で作成したスクリプトを実行してみます。

なお、事前にアセンブリ言語のプログラムはbinファイルとしてビルドしておきます。

 

 > python emulator.py
 EIP = 0x7c00, Code = 0xb8
 EIP = 0x7c05, Code = 0xbb
 EIP = 0x7c0a, Code = 0xb9
 EIP = 0x7c0f, Code = 0xba
 EIP = 0x7c14, Code = 0xe9


 end of program.


 EAX = 0x00000029
 ECX = 0x0000002b
 EDX = 0x0000002c
 EBX = 0x0000002a
 ESP = 0x00007c00
 EBP = 0x00000000
 ESI = 0x00000000
 EDI = 0x00000000
 EIP = 0x00000000

 

eip初期値を0x7c00に変更し、問題なく各レジスタに即値がコピーされたことが確認できました。

 

参考書籍

自作エミュレータで学ぶx86アーキテクチャ-コンピュータが動く仕組みを徹底理解!