Giter Club home page Giter Club logo

libx86emu's Introduction

x86 emulation library

libx86emu is a small library to emulate x86 instructions. The focus here is not a complete emulation (go for qemu for this) but to cover enough for typical firmware blobs.

At the moment 'regular' 32-bit instructions are covered together with basic protected mode support.

Not done are fpu, mmx, or any of the other instruction set extensions.

The library lets you

  • intercept any memory access or directly map real memory ranges
  • intercept any i/o access, map real i/o ports, or block any real i/o
  • intercept any interrupt
  • provides hook to run after each instruction
  • recognizes a special x86 instruction that can trigger logging
  • has integrated logging to
    • trace code execution, including register content and decoded instruction
    • trace memory and i/o accesses
    • provide statistics about accessed memory locations, i/o ports, and interrupts

Major versions

Programs should generally work fine with newer library versions without any changes (except re-compiling).

Version 3

Extend API to include CPUID and MSR handlers.

Version 2

Essentially the same API as version 1. The major difference is that version 2 is re-entrant (no global state variable).

Version 1

Version 1 relies internally on a global variable x86emu holding the emulator state. It has been eliminated in version 2.

Downloads

Get the latest version from the openSUSE Build Service.

Examples

Have a look at this minimalistic demo program.

The library is used by hwinfo to emulate Video BIOS (VBE) calls.

API functions

x86emu_new

Create new emulation object

x86emu_t *x86emu_new(unsigned def_mem_perm, unsigned def_io_perm);

def_mem_perm are the default permissions for memory accesses, def_io_perm for io. See x86emu_set_perm(), x86emu_set_io_perm().

Free object later with x86emu_done().

x86emu_done

Delete emulation object

x86emu_t *x86emu_done(x86emu_t *emu);

Frees all memory; returns NULL;

x86emu_clone

Clone emulation object

x86emu_t *x86emu_clone(x86emu_t *emu);

Creates a copy of emu. Free the copy later with x86emu_done().

x86emu_reset

Reset cpu state

void x86emu_reset(x86emu_t *emu);

Does a normal cpu reset (clear registers, set cs:eip).

x86emu_run

Start emulation

unsigned x86emu_run(x86emu_t *emu, unsigned flags);

Flags:

X86EMU_RUN_TIMEOUT
X86EMU_RUN_MAX_INSTR
X86EMU_RUN_NO_EXEC
X86EMU_RUN_NO_CODE
X86EMU_RUN_LOOP
  • X86EMU_RUN_TIMEOUT: set emu->timeout to max. seconds to run.
  • X86EMU_RUN_MAX_INSTR: set emu->max_instr to max. instructions to emulate.

Return value indicates why x86emu_run() stopped (see flags).

x86emu_stop

Stop emulation

void x86emu_stop(x86emu_t *emu);

Use this function in callbacks (e.g. interrupt handler) to tell the emulator to stop. The emulator returns from x86emu_run() when the current instruction has been finished.

x86emu_set_log

Set log buffer

void x86emu_set_log(x86emu_t *emu, char *buffer, unsigned buffer_size, x86emu_flush_func_t flush);
typedef void (* x86emu_flush_func_t)(x86emu_t *emu, char *buf, unsigned size);

If the log buffer is full, flush() is called (if not NULL). The buffer is freed in x86emu_done().

x86emu_log

Write to log

void x86emu_log(x86emu_t *emu, const char *format, ...) __attribute__ ((format (printf, 1, 2)));

x86emu_clear_log

Clear log

void x86emu_clear_log(x86emu_t *emu, int flush);

Clear log buffer. If flush != 0, write current log via flush() function (see x86emu_set_log()).

x86emu_dump

Dump emulator state

void x86emu_dump(x86emu_t *emu, int flags);

Flags:

X86EMU_DUMP_REGS
X86EMU_DUMP_MEM
X86EMU_DUMP_ACC_MEM
X86EMU_DUMP_INV_MEM
X86EMU_DUMP_ATTR
X86EMU_DUMP_ASCII
X86EMU_DUMP_IO
X86EMU_DUMP_INTS
X86EMU_DUMP_TIME

Writes emulator state to log.

x86emu_set_perm

Memory permissions

void x86emu_set_perm(x86emu_t *emu, unsigned start, unsigned end, unsigned perm);

perm is a bitmask of:

X86EMU_PERM_R
X86EMU_PERM_W
X86EMU_PERM_X
X86EMU_PERM_VALID
X86EMU_ACC_R
X86EMU_ACC_W
X86EMU_ACC_X
X86EMU_ACC_INVALID
  • X86EMU_PERM_{R,W,X}: memory is readable, writable, executable
  • X86EMU_PERM_VALID: memory has been initialized (say, been written to)
  • X86EMU_ACC_{R,W,X}: memory has been read, written, executed
  • X86EMU_ACC_INVALID: there was an invalid access (e.g. tried to read but not readable)

x86emu_set_page

Direct memory access

void x86emu_set_page(x86emu_t *emu, unsigned offset, void *address);

Map memory area of X86EMU_PAGE_SIZE size at address into emulator at offset. offset must be X86EMU_PAGE_SIZE aligned, address needs not.

Memory permissions still apply (via x86emu_set_perm()).

If address is NULL, switch back to emulated memory.

x86emu_set_io_perm

io permissions

void x86emu_set_io_perm(x86emu_t *emu, unsigned start, unsigned end, unsigned perm);

perm: see x86emu_set_perm().

x86emu_reset_access_stats

Reset memory access statistics

void x86emu_reset_access_stats(x86emu_t *emu);

Resets the X86EMU_ACC_* bits for the whole memory (see x86emu_set_perm()).

x86emu_set_code_handler

Execution hook

x86emu_code_handler_t x86emu_set_code_handler(x86emu_t *emu, x86emu_code_handler_t handler);
typedef int (* x86emu_code_handler_t)(x86emu_t *emu);

If defined, the function is called before a new instruction is decoded and emulated. If logging is enabled the current cpu state has already been logged. If the function returns a value != 0, the emulation is stopped.

x86emu_set_intr_handler

Set interrupt handler

x86emu_intr_handler_t x86emu_set_intr_handler(x86emu_t *emu, x86emu_intr_handler_t handler);
typedef int (* x86emu_intr_handler_t)(x86emu_t *emu, u8 num, unsigned type);

type:

INTR_TYPE_SOFT
INTR_TYPE_FAULT

and bitmask of:

INTR_MODE_RESTART
INTR_MODE_ERRCODE

If defined, the interrupt handler is called at the start of the interrupt handling procedure. The handler should return 1 to indicate the interrupt handling is complete and the emulator can skip its own interrupt processing or 0 to indicate the emulator should continue with normal interrupt processing.

x86emu_set_memio_handler

Set alternative callback function that handles memory and io accesses

x86emu_memio_handler_t x86emu_set_memio_handler(x86emu_t *emu, x86emu_memio_handler_t handler);

typedef unsigned (* x86emu_memio_handler_t)(x86emu_t *emu, u32 addr, u32 *val, unsigned type);

type: one of

X86EMU_MEMIO_8
X86EMU_MEMIO_16
X86EMU_MEMIO_32
X86EMU_MEMIO_8_NOPERM

and one of:

X86EMU_MEMIO_R
X86EMU_MEMIO_W
X86EMU_MEMIO_X
X86EMU_MEMIO_I
X86EMU_MEMIO_O

Returns old function.

x86emu_set_cpuid_handler

Execution hook

x86emu_cpuid_handler_t x86emu_set_cpuid_handler(x86emu_t *emu, x86emu_cpuid_handler_t handler);
typedef void (* x86emu_cpuid_handler_t)(x86emu_t *emu);

Set a callback function that handles the CPUID instruction. Allows the user to use the host's CPUID or provide a custom implementation to emulate a specific CPU.

Returns old function.

There's no default implementation. Without the handler installed the programm will raise an #UD exception.

x86emu_set_rdmsr_handler

Execution hook

x86emu_rdmsr_handler_t x86emu_set_rdmsr_handler(x86emu_t *emu, x86emu_rdmsr_handler_t handler);
typedef void (* x86emu_rdmsr_handler_t)(struct x86emu_s *);

Set alternative callback function that handles the RDMSR instruction. Allows the user to use the host's MSR or provide a custom implementation to emulate a specific platform.

Returns old function.

The default callback function uses the msr array in the x86emu_t structure to read MSRs from and updates the msr_perm array.

x86emu_set_wrmsr_handler

Execution hook

x86emu_wrmsr_handler_t x86emu_set_wrmsr_handler(x86emu_t *emu, x86emu_wrmsr_handler_t handler);
typedef void (* x86emu_wrmsr_handler_t)(struct x86emu_s *);

Set alternative callback function that handles the WRMSR instruction. Allows the user to use the host's MSR or provide a custom implementation to emulate a specific platform.

Returns old function.

The default callback function uses the msr array in the x86emu_t structure to write MSRs to and updates the msr_perm array.

x86emu_intr_raise

Raise an interrupt

void x86emu_intr_raise(x86emu_t *emu, u8 intr_nr, unsigned type, unsigned err);

The interrupt is handled before the next instruction. For type see x86emu_set_intr_func(); if INTR_MODE_ERRCODE is set, err is the error code pushed to the stack.

memory access functions

unsigned x86emu_read_byte(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_byte_noperm(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_word(x86emu_t *emu, unsigned addr); 
unsigned x86emu_read_dword(x86emu_t *emu, unsigned addr);
void x86emu_write_byte(x86emu_t *emu, unsigned addr, unsigned val); 
void x86emu_write_byte_noperm(x86emu_t *emu, unsigned addr, unsigned val); 
void x86emu_write_word(x86emu_t *emu, unsigned addr, unsigned val); 
void x86emu_write_dword(x86emu_t *emu, unsigned addr, unsigned val);

Convenience functions to access emulator memory. Memory access restrictions (see x86emu_set_perm()) apply except for x86emu_*_noperm() which do not check permissions.

x86emu_set_seg_register

Set segment register

void x86emu_set_seg_register(x86emu_t *emu, sel_t *seg, u16 val);
R_CS_SEL, R_DS_SEL, R_ES_SEL, R_FS_SEL, R_GS_SEL, R_SS_SEL

Example:

x86emu_set_seg_register(emu, emu->x86.R_CS_SEL, 0x7c0);

Debug instruction

If the X86EMU_TRACE_DEBUG flags is set, the emulator interprets a special debug instruction:

db 0x67, 0xeb, LEN, DATA...

which is basically a short jump with address size prefix (an instruction normally not used). LEN is the size of DATA.

DATA can be:

db 0x01
db STRING

Print STRING to log. STRING is not 0-zerminated.

db 0x02
dd flags

Set trace flags.

db 0x03
dd flags

Clear trace flags.

db 0x04
dd flags

Dump emulator state. For flags, see x86emu_dump().

db 0x05

Reset memory access stats. See x86emu_reset_access_stats().

openSUSE Development

To build, simply run make. Install with make install.

Basically every new commit into the master branch of the repository will be auto-submitted to all current SUSE products. No further action is needed except accepting the pull request.

Submissions are managed by a SUSE internal jenkins node in the InstallTools tab.

Each time a new commit is integrated into the master branch of the repository, a new submit request is created to the openSUSE Build Service. The devel project is system:install:head.

*.changes and version numbers are auto-generated from git commits, you don't have to worry about this.

The spec file is maintained in the Build Service only. If you need to change it for the master branch, submit to the devel project in the build service directly.

Development happens exclusively in the master branch. The branch is used for all current products.

You can find more information about the changes auto-generation and the tools used for jenkis submissions in the linuxrc-devtools documentation.

libx86emu's People

Contributors

fstirlitz avatar imobachgs avatar patrickrudolph avatar patrickvl avatar wfeldt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libx86emu's Issues

Question about illegal opcode

libx86emu/ops.c

Line 5483 in 0aed125

/* 0x64 */ x86emuOp_illegal_op, /* FS: */

I'm looking for a emulator that can run on Win10 and then I found this. But this is only for linux. I plan to learn from this code to make a windows version but I found some opcode like 0x64,0x65 are marked as illegal , who can tell me why is that???

x86emu-demo decodes the third emulated instruction incorrectly

Test.obj  
000000B0 00 0A 00 00 55 8B EC 51 C7 45 FC 00 00 00 00 8B 
000000C0 45 FC 50 E8 09 00 00 00 83 C4 04 33 C0 8B E5 5D 
000000D0 C3 55 8B EC B8 01 00 00 00 5D C3 00 07 00 00 00 

0:  55                      push   ebp
1:  8b ec                   mov    ebp,esp
3:  51                      push   ecx
4:  c7 45 fc 00 00 00 00    mov    DWORD PTR [ebp-0x4],0x0
b:  8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
e:  50                      push   eax
f:  e8 09 00 00 00          call   0x1d
14: 83 c4 04                add    esp,0x4
17: 33 c0                   xor    eax,eax
19: 8b e5                   mov    esp,ebp
1b: 5d                      pop    ebp
1c: c3                      ret
1d: 55                      push   ebp
1e: 8b ec                   mov    ebp,esp
20: b8 01 00 00 00          mov    eax,0x1
25: 5d                      pop    ebp
26: c3                      ret

./x86emu-demo -l 0 -s 0xB4 Test.obj

eax 00000000, ebx 00000000, ecx 00000000, edx 00000000
esi 00000000, edi 00000000, ebp 00000000, esp 00000000
cs 0000, ss 0000, ds 0000, es 0000, fs 0000, gs 0000
eip 000000b4, eflags 00000002
x [000000b4] = 55
w [0000fffe] = 0000
0 0000:00b4 55                       push bp

eax 00000000, ebx 00000000, ecx 00000000, edx 00000000
esi 00000000, edi 00000000, ebp 00000000, esp 0000fffe
cs 0000, ss 0000, ds 0000, es 0000, fs 0000, gs 0000
eip 000000b5, eflags 00000002
x [000000b5] = 8b
x [000000b6] = ec
1 0000:00b5 8bec                     mov bp,sp

eax 00000000, ebx 00000000, ecx 00000000, edx 00000000
esi 00000000, edi 00000000, ebp 0000fffe, esp 0000fffe
cs 0000, ss 0000, ds 0000, es 0000, fs 0000, gs 0000
eip 000000b7, eflags 00000002
x [000000b7] = 51
w [0000fffc] = 0000
2 0000:00b7 51                       push cx

eax 00000000, ebx 00000000, ecx 00000000, edx 00000000
esi 00000000, edi 00000000, ebp 0000fffe, esp 0000fffc
cs 0000, ss 0000, ds 0000, es 0000, fs 0000, gs 0000
eip 000000b8, eflags 00000002
x [000000b8] = c7
x [000000b9] = 45
x [000000ba] = fc
x [000000bb] = 0000
w [0000fffc] = 0000
3 0000:00b8 c745fc0000               mov [di-04],0000

eax 00000000, ebx 00000000, ecx 00000000, edx 00000000
esi 00000000, edi 00000000, ebp 0000fffe, esp 0000fffc
cs 0000, ss 0000, ds 0000, es 0000, fs 0000, gs 0000
eip 000000bd, eflags 00000002
x [000000bd] = 00
x [000000be] = 00
r [00000000] = 4c
w [00000000] = 4c
4 0000:00bd 0000                     add [bx+si],al
* no proper code

Build fails with GCC14

Build fails with GCC14.
Error log:

[   12s] gcc -c -g -O2 -fPIC -fvisibility=hidden -fomit-frame-pointer -Wall mem.c
[   12s] mem.c: In function ‘vm_i_byte’:
[   12s] mem.c:581:12: error: implicit declaration of function ‘inb’; did you mean ‘ins’? [-Wimplicit-function-declaration]
[   12s]   581 |     return inb(addr);
[   12s]       |            ^~~
[   12s]       |            ins
[   12s] mem.c: In function ‘vm_i_word’:
[   12s] mem.c:619:10: error: implicit declaration of function ‘inw’; did you mean ‘ins’? [-Wimplicit-function-declaration]
[   12s]   619 |   return inw(addr);
[   12s]       |          ^~~
[   12s]       |          ins
[   12s] mem.c: In function ‘vm_i_dword’:
[   12s] mem.c:657:10: error: implicit declaration of function ‘inl’; did you mean ‘ins’? [-Wimplicit-function-declaration]
[   12s]   657 |   return inl(addr);
[   12s]       |          ^~~
[   12s]       |          ins
[   12s] mem.c: In function ‘vm_o_byte’:
[   12s] mem.c:676:5: error: implicit declaration of function ‘outb’; did you mean ‘outs’? [-Wimplicit-function-declaration]
[   12s]   676 |     outb(val, addr);
[   12s]       |     ^~~~
[   12s]       |     outs
[   12s] mem.c: In function ‘vm_o_word’:
[   12s] mem.c:711:3: error: implicit declaration of function ‘outw’; did you mean ‘outs’? [-Wimplicit-function-declaration]
[   12s]   711 |   outw(val, addr);
[   12s]       |   ^~~~
[   12s]       |   outs
[   12s] mem.c: In function ‘vm_o_dword’:
[   12s] mem.c:748:3: error: implicit declaration of function ‘outl’; did you mean ‘outs’? [-Wimplicit-function-declaration]
[   12s]   748 |   outl(val, addr);
[   12s]       |   ^~~~
[   12s]       |   outs
[   12s] make: *** [Makefile:30: mem.o] Error 1

Likely a matter of adding #include <sys/io.h>.

Python Binding

Hello,

I have developed a python binding for this code; would this be something that I should create a PR for, or should I create a separate repository?

Also, is licensing on this project is OSI approved?

Thanks!

x86emu-demo.c fails regression test unless it uses 2015 version of x86emu.h

x86emu-demo.c does not work correctly with my test data under Linux unless it uses the 2015 version of x86emu.h.
Everything else seems to work correctly under both Linux and Windows including the regression tests under Linux.
I forced x86test.c and x86emu-demo.c to use "../include/x86emu.h" and only the demo quit working. I found this error when I was trying to augment x86emu_t with additional features.

There are four lines of code in x86emu.h that make the difference. The general regression tests will not pass without these four lines and my own regression test of x86emu-demo.c will not pass with these fours lines.

These lines of x86emu.h make x86emu-demo.c flunk regression
(420) struct i386_see_regs sse; // 2020-07-19
(522) x86emu_cpuid_handler_t cpuid; // 2020-07-19
(525) x86emu_wrmsr_handler_t wrmsr; // 2020-07-19
(526) x86emu_wrmsr_handler_t rdmsr; // 2020-07-19

The test data only fails on x86emu-demo.c with the current version of x86emu.h when linked to the shared library. It works correctly as a single executable under Linux, Windows and linked to the library as a DLL under Windows.

This problem was very difficult to detect because of #include <x86emu.h> referenced a copy of the file that was not in any of the source directories.

Missing license file / notice

According to the openSUSE spec, this is a 3-clause BSD licensed program. However, no LICENSE or COPYING file spells out the exact terms. Please correct this, as the BSD licenses require full notice and attribution.

Failed test when building on i686

I have compiled libx86emu in a chrooted environment (my host was x86_64 and the chrooted environment was i686). The 0032_rol16.init test has failed. The logs are here: 0032_rol16.tar.gz.
Does this mean that libx86emu shouldn't be used on i686?

I got a port to Windows working if you are interested

It only took a few very minor changes to adapt the code to compile under Visual Studio 2017.
I am pretty sure that this is a comprehensive list all all of the changes it was derived from a diff.

(1) API_SYM was removed everywhere
(2) decode.c-------------void decode_hex(x86emu_t *emu, char **p, u64 ofs)
(3) api.c -----------------emu->x86.R_EAX = (uint32_t) emu->x86.msr[u];
(4) mem.c---------------six low level port io functions were swapped

#if (_MSC_VER >= 1900)  // (Visual Studio 2015 version 14.0)
  #include <intrin.h>
#endif 
return inb(addr); // becomes __inbyte(addr);
return inw(addr); // becomes __inword(addr);
return inl(addr); // becomes __indword(addr);
outb(val, addr);  // becomes __outbyte(val, addr);
outw(val, addr);  // becomes __outword(val, addr);
outl(val, addr);  // becomes __outdword(val, addr);

(5) x86emu-demo.c-----if(!(f = fopen(file, "rb"))) return 0;
(6) I found a good getopt.h that works under Windows. It has a compatible license.
https://github.com/skandhurkat/Getopt-for-Visual-Studio/blob/master/getopt.h

To make things very simple I compiled the demo code together with the library files into a single executable. The adapted code also compiles under Linux as a shared library.

Microsoft Visual Studio 2017 Community Edition is freeware.
Thanks for adding the --32 option to the demo program.
All of these changes have been applied to the most recent commit.

With additional changes it now compiles as c++ in Visual Studio 2017 and gcc.

Hey libx86emu instruction stepping mode

Hey I don't know how to email you I wanted to ask you..

I may end up using your library for cpu emulation because it's very simple short and not too hard to understand others are way to complicated I need to add to this library floating-point instructions emulation to make it complete x86 emu emulator for my needs.

Now I just ask how do I step code line by line?.. I tried quickfix but EIP never changes?? how come.

I use this at the moment and EIP never changes and loop just crashes Segmentation fault error.

    //Set the emulator to start at this instruction. [EIP register]
    emu->x86.R_EIP = 0x00457D60; //start of function 0x457D60

    //Set the CPU emulator flags
    unsigned flags;
    flags = X86EMU_RUN_LOOP | X86EMU_RUN_NO_CODE;

        unsigned instructions_run_max = 1;
    if(instuctions_run_max) {
        emu->max_instr = instuctions_run_max; //max instructions to run
        flags |= X86EMU_RUN_MAX_INSTR;
    }

    //Run the CPU Emulator!
    while(emu->x86.R_EIP != 0x00458BD0) //where CPU emulator must stop address
    {
        x86emu_run(emu, flags);
        //I debug here line by line to see where CPU emulator needs more implmentation of new instructions.. I will try to put in floating-point operations myself using this.
    }

Have any way to step instructions line by line? not always running x86_emu_run(...);

I look at it more in depth it looks like X86EMU_RUN_LOOP only detect JMP for EIP? if(op1 == 0xeb) and else if(op1 == 0xe9) should detect every line?? i dont understand sorry.
But I really like your library

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.