diff --git a/bindings/haskell/samples/SampleX86.hs b/bindings/haskell/samples/SampleX86.hs index fad686af..e395ed75 100644 --- a/bindings/haskell/samples/SampleX86.hs +++ b/bindings/haskell/samples/SampleX86.hs @@ -40,6 +40,10 @@ x86Code32JmpInvalid = BS.pack [0xe9, 0xe9, 0xee, 0xee, 0xee, 0x41, 0x4a] x86Code32InOut :: BS.ByteString x86Code32InOut = BS.pack [0x41, 0xe4, 0x3f, 0x4a, 0xe6, 0x46, 0x43] +-- inc eax +x86Code32Inc :: BS.ByteString +x86Code32Inc = BS.pack [0x40] + x86Code64 :: BS.ByteString x86Code64 = BS.pack [0x41, 0xbc, 0x3b, 0xb0, 0x28, 0x2a, 0x49, 0x0f, 0xc9, 0x90, 0x4d, 0x0f, 0xad, 0xcf, 0x49, 0x87, 0xfd, 0x90, @@ -494,6 +498,70 @@ testI386InOut = do Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++ strerror err +-- Emulate code and save/restore the CPU context +testI386ContextSave :: IO () +testI386ContextSave = do + putStrLn "===================================" + putStrLn "Save/restore CPU context in opaque blob" + + result <- runEmulator $ do + -- Initialize emulator in X86-32bit mode + uc <- open ArchX86 [Mode32] + + -- Map 8KB memory for this emulation + memMap uc address (8 * 1024) [ProtAll] + + -- Write machine code to be emulated to memory + memWrite uc address x86Code32Inc + + -- Initialize machine registers + regWrite uc X86.Eax 0x1 + + -- Emulate machine code in infinite time + emuPutStrLn ">>> Running emulation for the first time" + + let codeLen = codeLength x86Code32Inc + start uc address (address + codeLen) Nothing Nothing + + -- Now print out some registers + emuPutStrLn ">>> Emulation done. Below is the CPU context" + + eax <- regRead uc X86.Eax + + emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax + + -- Allocate and save the CPU context + emuPutStrLn ">>> Saving CPU context" + + context <- contextAllocate uc + contextSave uc context + + -- Emulate machine code again + emuPutStrLn ">>> Running emulation for the second time" + + start uc address (address + codeLen) Nothing Nothing + + -- Now print out some registers + emuPutStrLn ">>> Emulation done. Below is the CPU context" + + eax <- regRead uc X86.Eax + + emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax + + -- Restore CPU context + contextRestore uc context + + -- Now print out some registers + emuPutStrLn ">>> Emulation done. Below is the CPU context" + + eax <- regRead uc X86.Eax + + emuPutStrLn $ ">>> EAX = 0x" ++ showHex eax + case result of + Right _ -> return () + Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++ + strerror err + testX8664 :: IO () testX8664 = do putStrLn "Emulate x86_64 code" @@ -660,6 +728,7 @@ main = do ["-32"] -> do testI386 testI386InOut + testI386ContextSave testI386Jump testI386Loop testI386InvalidMemRead diff --git a/bindings/haskell/src/Unicorn.hs b/bindings/haskell/src/Unicorn.hs index c45a5198..19c07f8c 100644 --- a/bindings/haskell/src/Unicorn.hs +++ b/bindings/haskell/src/Unicorn.hs @@ -38,7 +38,7 @@ module Unicorn -- * Context operations , Context - , contextAlloc + , contextAllocate , contextSave , contextRestore @@ -273,9 +273,9 @@ memRegions uc = do -- CPU context, which includes registers and some internal metadata. Contexts -- may not be shared across engine instances with differing architectures or -- modes. -contextAlloc :: Engine -- ^ 'Unicon' engine handle - -> Emulator Context -- ^ A CPU context -contextAlloc uc = do +contextAllocate :: Engine -- ^ 'Unicon' engine handle + -> Emulator Context -- ^ A CPU context +contextAllocate uc = do (err, contextPtr) <- lift $ ucContextAlloc uc if err == ErrOk then -- Return a CPU context if ucContextAlloc completed successfully diff --git a/samples/sample_x86.c b/samples/sample_x86.c index 2e1d73ec..4c5da3e3 100644 --- a/samples/sample_x86.c +++ b/samples/sample_x86.c @@ -41,6 +41,7 @@ #define X86_CODE32_JMP_INVALID "\xe9\xe9\xee\xee\xee\x41\x4a" // JMP outside; INC ecx; DEC edx #define X86_CODE32_INOUT "\x41\xE4\x3F\x4a\xE6\x46\x43" // INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx +#define X86_CODE32_INC "\x40" // INC eax //#define X86_CODE64 "\x41\xBC\x3B\xB0\x28\x2A \x49\x0F\xC9 \x90 \x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9" // <== still crash //#define X86_CODE64 "\x41\xBC\x3B\xB0\x28\x2A\x49\x0F\xC9\x90\x4D\x0F\xAD\xCF\x49\x87\xFD\x90\x48\x81\xD2\x8A\xCE\x77\x35\x48\xF7\xD9" @@ -668,6 +669,105 @@ static void test_i386_inout(void) uc_close(uc); } +// emulate code and save/restore the CPU context +static void test_i386_context_save(void) +{ + uc_engine *uc; + uc_context *context; + uc_err err; + + int r_eax = 0x1; // EAX register + + printf("===================================\n"); + printf("Save/restore CPU context in opaque blob\n"); + + // initialize emulator in X86-32bit mode + err = uc_open(UC_ARCH_X86, UC_MODE_32, &uc); + if (err) { + printf("Failed on uc_open() with error returned: %u\n", err); + return; + } + + // map 8KB memory for this emulation + uc_mem_map(uc, ADDRESS, 8 * 1024, UC_PROT_ALL); + + // write machine code to be emulated to memory + if (uc_mem_write(uc, ADDRESS, X86_CODE32_INC, sizeof(X86_CODE32_INC) - 1)) { + printf("Failed to write emulation code to memory, quit!\n"); + return; + } + + // initialize machine registers + uc_reg_write(uc, UC_X86_REG_EAX, &r_eax); + + // emulate machine code in infinite time + printf(">>> Running emulation for the first time\n"); + + err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 1, 0, 0); + if (err) { + printf("Failed on uc_emu_start() with error returned %u: %s\n", + err, uc_strerror(err)); + } + + // now print out some registers + printf(">>> Emulation done. Below is the CPU context\n"); + + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + printf(">>> EAX = 0x%x\n", r_eax); + + // allocate and save the CPU context + printf(">>> Saving CPU context\n"); + + err = uc_context_alloc(uc, &context); + if (err) { + printf("Failed on uc_context_alloc() with error returned: %u\n", err); + return; + } + + err = uc_context_save(uc, context); + if (err) { + printf("Failed on uc_context_save() with error returned: %u\n", err); + return; + } + + // emulate machine code again + printf(">>> Running emulation for the second time\n"); + + err = uc_emu_start(uc, ADDRESS, ADDRESS + sizeof(X86_CODE32_INC) - 1, 0, 0); + if (err) { + printf("Failed on uc_emu_start() with error returned %u: %s\n", + err, uc_strerror(err)); + } + + // now print out some registers + printf(">>> Emulation done. Below is the CPU context\n"); + + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + printf(">>> EAX = 0x%x\n", r_eax); + + // restore CPU context + err = uc_context_restore(uc, context); + if (err) { + printf("Failed on uc_context_restore() with error returned: %u\n", err); + return; + } + + // now print out some registers + printf(">>> CPU context restored. Below is the CPU context\n"); + + uc_reg_read(uc, UC_X86_REG_EAX, &r_eax); + printf(">>> EAX = 0x%x\n", r_eax); + + // free the CPU context + err = uc_context_free(context); + if (err) { + printf("Failed on uc_context_free() with error returned: %u\n", err); + return; + } + + uc_close(uc); +} + static void test_x86_64(void) { uc_engine *uc; @@ -906,6 +1006,7 @@ int main(int argc, char **argv, char **envp) test_i386(); test_i386_map_ptr(); test_i386_inout(); + test_i386_context_save(); test_i386_jump(); test_i386_loop(); test_i386_invalid_mem_read();