From 089e6d5e7ed28a8f5a4130eb29ca0ca171b69e6d Mon Sep 17 00:00:00 2001 From: "BITDEFENDER\\vlutas" Date: Thu, 2 Feb 2023 21:46:24 +0200 Subject: [PATCH] Significant cleanup in disasmtool: the obsolete search functionality, and supplying registers for shemu from a file are no longer supported. --- bdshemu_test/bdshemu_test.zip | Bin 96410 -> 97496 bytes bdshemu_test/test_all.py | 4 +- disasmtool/disasmtool.c | 1243 ++++++++++--------------- disasmtool/disasmtool.h | 59 ++ disasmtool/disasmtool.vcxproj | 3 + disasmtool/disasmtool.vcxproj.filters | 5 + 6 files changed, 536 insertions(+), 778 deletions(-) create mode 100644 disasmtool/disasmtool.h diff --git a/bdshemu_test/bdshemu_test.zip b/bdshemu_test/bdshemu_test.zip index 762d702f9fcff98cb4b4a86468316834d5cb76b3..ad95dc9e95a2e6198bc44156e3968e98d5b32cea 100644 GIT binary patch delta 39551 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 u(SXq3Ytmr)TKpd?A&hd=`_MQs> delta 38417 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 IAtA;80@bBR^#A|> diff --git a/bdshemu_test/test_all.py b/bdshemu_test/test_all.py index d961424..f50dec1 100644 --- a/bdshemu_test/test_all.py +++ b/bdshemu_test/test_all.py @@ -27,7 +27,7 @@ def test_dir(dir): mod += ' -k' print(' * Running test case %s...' % f) - os.system('disasm -shemu %s -f %s >%s.temp' % (mod, f, f)) + os.system('disasm shemu %s -f %s >%s.temp' % (mod, f, f)) try: res = open('%s.result' % f).read() except: @@ -63,7 +63,7 @@ def regenerate(dir): mod += ' -k' print(' * Regenerating test case %s...' % f) - os.system('disasm -exi -shemu %s -f %s >%s.result' % (mod, f, f)) + os.system('disasm -exi shemu %s -f %s >%s.result' % (mod, f, f)) for f in glob.glob('%s\\*_decoded.bin' % dir): os.remove(f) diff --git a/disasmtool/disasmtool.c b/disasmtool/disasmtool.c index 4084eb2..58c0660 100644 --- a/disasmtool/disasmtool.c +++ b/disasmtool/disasmtool.c @@ -9,41 +9,14 @@ #include #include -typedef uint64_t QWORD, *PQWORD; - // Main disasm header file. #include "bdshemu.h" #include "bddisasm.h" +#include "disasmtool.h" -#define PAGE_SIZE 0x1000 -#define PAGE_MASK 0xFFFFFFFFFFFFF000 -typedef struct _DISASM_OPTIONS -{ - uint8_t *Buffer; // The buffer containing the instructions. - size_t Size; // Buffer size. - size_t Offset; // Offset inside the buffer. - size_t Rip; // Virtual RIP. - char *Target; // If in search mode, this indicates the instruction to be searched for. - BOOLEAN Highlight; // Highlight instruction components, if true. - BOOLEAN ExtendedInfo; // Display extended instruction info, if true. - BOOLEAN BitFields; // Display the various bitfields inside the instruction, if true. - BOOLEAN Skip16; // Automatically jump over 16 bytes after each instruction. - BOOLEAN Stats; // Display disassembly stats (clocks / instruction, instructions / second), if true. - BOOLEAN Search; // Search for the Target instruction in the provided buffer. - BOOLEAN Print; // Print instruction disassembly, if true. - uint8_t Mode; // Mode - 16, 32 or 64 bit mode. - uint8_t Ring; // Ring - 0, 1, 2 or 3. - uint8_t Vendor; // Preffered vendor. - uint8_t Feature; // Used features. - char *FileName; // Input file, if any. - size_t ShemuRegs[NDR_R15 + 1]; - BOOLEAN UseShemuRegs; - BOOLEAN BypassSelfWrites; // If true, shemu emulation will ignore self-modifications made by the shellcode. -} DISASM_OPTIONS, *PDISASM_OPTIONS; - -char *gSpaces[16] = +const char *gSpaces[16] = { "", " ", @@ -68,7 +41,8 @@ char *gSpaces[16] = // // nd_vsnprintf // -int nd_vsnprintf_s( +int +nd_vsnprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, @@ -81,16 +55,49 @@ int nd_vsnprintf_s( #endif // !defined(BDDISASM_HAS_VSNPRINTF) #if !defined(BDDISASM_HAS_MEMSET) -void* nd_memset(void *s, int c, size_t n) +void* +nd_memset(void *s, int c, size_t n) { return memset(s, c, n); } #endif // !defined(BDDISASM_HAS_MEMSET) -// -// set_to_string -// -const char* set_to_string( +void +ShemuLog( + __in PCHAR Data + ) +{ + printf("%s", Data); +} + +bool +ShemuAccessMem( + __in PSHEMU_CONTEXT Ctx, + __in uint64_t Gla, + __in size_t Size, + __inout uint8_t *Buffer, + __in bool Store + ) +{ + UNREFERENCED_PARAMETER(Ctx); + UNREFERENCED_PARAMETER(Gla); + + if (!Store) + { + // On loads, always return 0. + memset(Buffer, 0, Size); + } + else + { + // On stores, do nothing. + } + + return true; +} + + +const char* +set_to_string( __in ND_INS_SET Set ) { @@ -227,10 +234,8 @@ const char* set_to_string( } -// -// category_to_string -// -const char* category_to_string( +const char* +category_to_string( __in ND_INS_CATEGORY Category ) { @@ -349,10 +354,8 @@ const char* category_to_string( } -// -// optype_to_string -// -const char* optype_to_string( +const char* +optype_to_string( __in ND_OPERAND_TYPE OpType ) { @@ -370,10 +373,8 @@ const char* optype_to_string( } -// -// regtype_to_string -// -const char* regtype_to_string( +const char* +regtype_to_string( __in ND_REG_TYPE RegType ) { @@ -404,10 +405,8 @@ const char* regtype_to_string( } -// -// encoding_to_string -// -const char* encoding_to_string( +const char* +encoding_to_string( __in ND_OPERAND_ENCODING Encoding ) { @@ -430,10 +429,8 @@ const char* encoding_to_string( } -// -// tuple_to_string -// -const char *tuple_to_string( +const char* +tuple_to_string( __in ND_TUPLE Tuple ) { @@ -460,10 +457,8 @@ const char *tuple_to_string( } -// -// exception_to_string -// -const char *exception_evex_to_string( +const char* +exception_evex_to_string( __in ND_EX_TYPE_EVEX ExClass ) { @@ -495,10 +490,8 @@ const char *exception_evex_to_string( } -// -// hex_to_bin -// -BYTE hex_to_bin( +BYTE +hex_to_bin( __in char HexByte ) { @@ -519,72 +512,8 @@ BYTE hex_to_bin( } -// -// str_strip -// Copies the InputString in OutputString, while eliminating the characters specified in Tokens -// -VOID str_strip( - __in char* InString, - __in DWORD LenInString, - __in char* Tokens, - __in DWORD LenTokString, - __out char* OutString, - __in DWORD CapOutString // capacity of out string including null character -) -{ - DWORD lenInStr, lenOutStr, lenTokStr; - DWORD itInStr, itTokStr; - lenTokStr = 0; - lenOutStr = 0; - - if ((NULL == InString) || (NULL == Tokens) || (NULL == OutString)) - { - return; - } - - lenInStr = (DWORD)strnlen_s(InString, ND_MIN_BUF_SIZE); - lenTokStr = (DWORD)strnlen_s(Tokens, ND_MIN_BUF_SIZE); - - lenInStr = (lenInStr > LenInString) ? LenInString : lenInStr; - lenTokStr = (lenTokStr > LenTokString) ? LenTokString : lenTokStr; - - - if ((0 == lenInStr) || (0 == lenTokStr)) - { - return; - } - - for (itInStr = 0; itInStr < lenInStr; itInStr++) - { - BOOLEAN found = FALSE; - for (itTokStr = 0; itTokStr < lenTokStr; itTokStr++) - { - if (InString[itInStr] == Tokens[itTokStr]) - { - found = TRUE; - break; - } - } - - if (!found) - { - // not enough space, bail out - if (lenOutStr >= CapOutString - 1) - { - return; - } - - OutString[lenOutStr] = InString[itInStr]; - lenOutStr++; - } - } -} - - -// -// regstr_to_idx -// -INT32 regstr_to_idx( +INT32 +regstr_to_idx( __in const char* Reg ) { @@ -606,13 +535,11 @@ INT32 regstr_to_idx( } -// -// match_gpr -// +_Success_(return) BOOLEAN match_gpr( __in const char* Arg, - __in DWORD* Index + __out DWORD* Index ) { if (Arg[0] == '-') @@ -628,9 +555,7 @@ match_gpr( return FALSE; } -// -// print_instruction -// + void print_instruction( __in SIZE_T Rip, @@ -716,7 +641,7 @@ print_instruction( printf("%02x", Instrux->InstructionBytes[k]); } - printf("%s", gSpaces[16 - Instrux->Length]); + printf("%s", gSpaces[16 - (Instrux->Length & 0xF)]); NdToText(Instrux, Rip, ND_MIN_BUF_SIZE, instruxText); @@ -1056,7 +981,7 @@ print_instruction( printf("\n"); } - if (Options->BitFields) + if (Options->BitFields && Instrux->HasModRm) { printf(" Instruction bit fields:\n"); @@ -1114,145 +1039,6 @@ print_instruction( } -// -// handle_search -// -void -handle_search( - __in PDISASM_OPTIONS Options - ) -{ - NDSTATUS status; - INSTRUX instrux; - ND_CONTEXT ctx; - SIZE_T rip = 0, i; - char text[ND_MIN_BUF_SIZE], target[ND_MIN_BUF_SIZE]; - char *token1, *token2, *ctx1, *ctx2; - BOOLEAN match; - - NdInitContext(&ctx); - - ctx.DefCode = Options->Mode; - ctx.DefData = Options->Mode; - ctx.DefStack = Options->Mode; - ctx.VendMode = Options->Vendor; - ctx.FeatMode = Options->Feature; - - // Disassemble - rip = 0; - while (rip < Options->Size) - { - status = NdDecodeWithContext(&instrux, Options->Buffer + rip, Options->Size - rip, &ctx); - if (!ND_SUCCESS(status)) - { - goto _continue; - } - - NdToText(&instrux, rip, ND_MIN_BUF_SIZE, text); - - // Copy the target. - memcpy(target, Options->Target, ND_MIN_BUF_SIZE); - for (i = 0; i < ND_MIN_BUF_SIZE; i++) - { - if (target[i] == ',') - { - target[i] = ' '; - } - - if (text[i] == ',') - { - text[i] = ' '; - } - } - - _strlwr_s(target, ND_MIN_BUF_SIZE); - _strlwr_s(text, ND_MIN_BUF_SIZE); - - // Try to match. - token1 = strtok_s(target, " ", &ctx1); - token2 = strtok_s(text, " ", &ctx2); - - match = TRUE; - - while (token1 && token2) - { - if (0 == strcmp(token1, "*")) - { - goto _continue_match; - } - - if (strlen(token1) != strlen(token2)) - { - match = FALSE; - break; - } - - for (i = 0; i < strlen(token1); i++) - { - if ((token1[i] != '?') && (token1[i] != token2[i])) - { - match = FALSE; - break; - } - } - - if (!match) - { - break; - } - -_continue_match: - token1 = strtok_s(NULL, " ", &ctx1); - token2 = strtok_s(NULL, " ", &ctx2); - } - - // Unfinished token - leave. - if (token1 || token2) - { - goto _continue; - } - - if (match) - { - SIZE_T rip2 = rip, count = 0; - - print_instruction(rip, &instrux, Options); - rip2 += instrux.Length; - - instrux.DefCode = Options->Mode; - instrux.DefData = Options->Mode; - instrux.DefStack = Options->Mode; - instrux.VendMode = Options->Vendor; - instrux.FeatMode = Options->Feature; - - while (rip2 < Options->Size && count++ < 8) - { - status = NdDecodeWithContext(&instrux, Options->Buffer + rip2, Options->Size - rip2, &ctx); - if (!ND_SUCCESS(status)) - { - printf("%p ERROR\n", (void*)rip2); - rip2++; - } - else - { - print_instruction(rip2, &instrux, Options); - rip2 += instrux.Length; - } - } - - printf("-----------------------------------------------------------------------\n"); - } - -_continue: - rip++; - } -} - - - -// -// handle_disasm -// void handle_disasm( __in PDISASM_OPTIONS Options @@ -1260,7 +1046,7 @@ handle_disasm( { INSTRUX instrux; ND_CONTEXT ctx = { 0 }; - unsigned long long icount = 0, istart, iend, start, end, itotal = 0; + unsigned long long icount = 0, istart, iend, start, end, itotal = 0, tilen = 0, ticount = 0; SIZE_T rip, fsize = Options->Size; PBYTE buffer = Options->Buffer; @@ -1282,9 +1068,19 @@ handle_disasm( icount++; +#if defined(ND_ARCH_X86) || defined(ND_ARCH_X64) istart = __rdtsc(); +#else + istart = 0; +#endif + status = NdDecodeWithContext(&instrux, buffer + rip, fsize - rip, &ctx); + +#if defined(ND_ARCH_X86) || defined(ND_ARCH_X64) iend = __rdtsc(); +#else + iend = 1; +#endif itotal += iend - istart; if (!ND_SUCCESS(status)) @@ -1311,6 +1107,9 @@ handle_disasm( } else { + tilen += instrux.Length; + ticount++; + if (Options->Print) { print_instruction(rip + Options->Rip, &instrux, Options); @@ -1331,169 +1130,12 @@ handle_disasm( if (Options->Stats) { - printf("Disassembled %llu instructions in %llums, %4.4f instructions/second, %4.6f clocks/instruction\n", - icount, end - start, icount / (double)(end - start) * 1000, itotal / (double)icount); + printf("Disassembled %llu instructions in %llums, %4.4f instructions/second, %4.6f clocks/instruction, average ilen %4.6f bytes\n", + icount, end - start, icount / (double)(end - start) * 1000, itotal / (double)icount, tilen / (double)ticount); } } -// -// set_shemuctx_file -// -void set_shemuctx_file( - __in const char *FileName, - __in DISASM_OPTIONS *Options - ) -{ - HANDLE hFile; - DWORD fileSize; - BOOL readFileSuccess; - PCHAR fileBuf = NULL; - DWORD bytesRead; - - PCHAR lineStart, lineToken, ctx, lineBufCtx; - char lineBuf[ND_MIN_BUF_SIZE] = { 0 }; - INT32 regIdx; - QWORD regVal; - - // pre-init - hFile = INVALID_HANDLE_VALUE; - fileSize = 0; - regIdx = 0; - regVal = 0; - ctx = lineBufCtx = NULL; - - if ((NULL == FileName) || (NULL == Options)) - { - goto cleanup_and_exit; - } - - hFile = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - goto cleanup_and_exit; - } - - fileSize = GetFileSize(hFile, NULL); - if (INVALID_FILE_SIZE == fileSize) - { - goto cleanup_and_exit; - } - - fileBuf = (PCHAR)VirtualAlloc(NULL, fileSize + 3ull, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - if (NULL == fileBuf) - { - goto cleanup_and_exit; - } - - readFileSuccess = ReadFile(hFile, fileBuf, fileSize, &bytesRead, NULL); - if (!readFileSuccess) - { - printf("ReadFile failed: GLA: %x\n", GetLastError()); - goto cleanup_and_exit; - } - - lineStart = strtok_s(fileBuf, "\r\n", &ctx); - while (lineStart) - { - char regStr[ND_MIN_BUF_SIZE] = { 0 }; - char valStr[ND_MIN_BUF_SIZE] = { 0 }; - BOOLEAN isRip = FALSE; - - if (0 == strnlen_s(lineStart, ND_MIN_BUF_SIZE)) - { - goto next_line; - } - strcpy_s(lineBuf, ND_MIN_BUF_SIZE, lineStart); - - lineToken = strtok_s(lineBuf, ":", &lineBufCtx); - if (NULL == lineToken) - { - goto next_line; - } - - str_strip(lineToken, (DWORD)strnlen_s(lineToken, ND_MIN_BUF_SIZE), " ,\"", - (DWORD)strnlen_s(" ,\"", ND_MIN_BUF_SIZE), regStr, ND_MIN_BUF_SIZE); - - // first element is the register - regIdx = regstr_to_idx(regStr); - if (regIdx < 0 || regIdx >= (INT32)ARRAYSIZE(Options->ShemuRegs)) - { - if (!_stricmp(regStr, "rip")) - { - isRip = TRUE; - } - else - { - printf("Corrupt line: %s\n", lineStart); - goto next_line; - } - } - - // second element is the value - lineToken = strtok_s(NULL, ":", &lineBufCtx); - if ((NULL == lineToken)) - { - printf("Corrupt line: %s\n", lineStart); - goto next_line; - } - - str_strip(lineToken, (DWORD)strnlen_s(lineToken, ND_MIN_BUF_SIZE), " ,\"", - (DWORD)strnlen_s(" ,\"", ND_MIN_BUF_SIZE), valStr, ND_MIN_BUF_SIZE); - regVal = strtoull(valStr, NULL, 0); - - if (!isRip) - { - Options->ShemuRegs[regIdx] = (size_t)regVal; - } - else - { - Options->Rip = (size_t)regVal; - } - - next_line: - lineStart = strtok_s(NULL, "\r\n", &ctx); - memset(lineBuf, 0, ND_MIN_BUF_SIZE); - } - -cleanup_and_exit: - if (INVALID_HANDLE_VALUE != hFile) - { - CloseHandle(hFile); - } - if (NULL != fileBuf) - { - VirtualFree(fileBuf, 0, MEM_RELEASE); - } - -} - -void ShemuLog(PCHAR data) -{ - printf("%s", data); -} - -bool ShemuAccessMem(PSHEMU_CONTEXT Ctx, uint64_t Gla, size_t Size, uint8_t *Buffer, bool Store) -{ - UNREFERENCED_PARAMETER(Ctx); - UNREFERENCED_PARAMETER(Gla); - - if (!Store) - { - // On loads, always return 0. - memset(Buffer, 0, Size); - } - else - { - // On stores, do nothing. - } - - return true; -} - -// -// handle_shemu -// void handle_shemu( PDISASM_OPTIONS Options @@ -1521,8 +1163,8 @@ handle_shemu( { decFileNameLength = strlen(fileName) + sizeof("_decoded.bin"); fNameDecoded = (char *)malloc(sizeof(char) * decFileNameLength); - } + if (NULL == fNameDecoded) { printf("Could not allocate file name.\n"); @@ -1552,8 +1194,7 @@ handle_shemu( if (NULL == ctx.Shellcode) { printf("Memory error: couldn't allocated %zu bytes!\n", fsize); - free(fNameDecoded); - return; + goto cleanup_and_exit; } #define STACK_SIZE 0x2000 @@ -1562,16 +1203,14 @@ handle_shemu( if (NULL == ctx.Stack) { printf("Memory error: couldn't allocated %zu bytes!\n", fsize); - free(fNameDecoded); - return; + goto cleanup_and_exit; } ctx.Intbuf = (uint8_t *)malloc(shellSize + STACK_SIZE); if (NULL == ctx.Intbuf) { printf("Memory error: couldn't allocated %zu bytes!\n", fsize); - free(fNameDecoded); - return; + goto cleanup_and_exit; } memset(ctx.Shellcode, 0, shellSize); @@ -1581,30 +1220,63 @@ handle_shemu( ctx.ShellcodeBase = (rip != 0 ? rip & PAGE_MASK : 0x200000); ctx.ShellcodeSize = (DWORD)shellSize; - ctx.StackBase = 0x100000; + ctx.StackBase = (ctx.ShellcodeBase & PAGE_MASK) - STACK_SIZE - 0x1000; ctx.StackSize = STACK_SIZE; - ctx.Registers.RegRsp = 0x101000; + ctx.IntbufSize = (DWORD)shellSize + STACK_SIZE; + ctx.Mode = Options->Mode; + ctx.Ring = Options->Ring; + ctx.Registers.RegFlags = NDR_RFLAG_IF | 2; ctx.Registers.RegRip = ctx.ShellcodeBase + offset; + ctx.Registers.RegRsp = ctx.StackBase + STACK_SIZE / 2; + + if (ctx.Mode == ND_CODE_64) + { + ctx.Segments.Cs.Selector = (ctx.Ring == 3) ? 0x33 : 0x10; + ctx.Segments.Ds.Selector = (ctx.Ring == 3) ? 0x2b : 0x18; + ctx.Segments.Es.Selector = (ctx.Ring == 3) ? 0x2b : 0x18; + ctx.Segments.Ss.Selector = (ctx.Ring == 3) ? 0x2b : 0x18; + ctx.Segments.Fs.Selector = (ctx.Ring == 3) ? 0x2b : 0x00; + ctx.Segments.Gs.Selector = (ctx.Ring == 3) ? 0x53 : 0x00; - ctx.Segments.Cs.Selector = 0x10; - ctx.Segments.Ds.Selector = 0x28; - ctx.Segments.Es.Selector = 0x28; - ctx.Segments.Ss.Selector = 0x28; - ctx.Segments.Fs.Selector = 0x30; - ctx.Segments.Fs.Base = 0x7FFF0000; - ctx.Segments.Gs.Selector = 0x30; - ctx.Segments.Gs.Base = 0x7FFF0000; + ctx.Segments.Fs.Base = 0; + ctx.Segments.Gs.Base = 0x7FFF0000; + } + else + { + ctx.Segments.Cs.Selector = (ctx.Ring == 3) ? 0x1b : 0x08; + ctx.Segments.Ds.Selector = (ctx.Ring == 3) ? 0x23 : 0x10; + ctx.Segments.Es.Selector = (ctx.Ring == 3) ? 0x23 : 0x10; + ctx.Segments.Ss.Selector = (ctx.Ring == 3) ? 0x23 : 0x10; + ctx.Segments.Fs.Selector = (ctx.Ring == 3) ? 0x3b : 0x30; + ctx.Segments.Gs.Selector = (ctx.Ring == 3) ? 0x23 : 0x00; + + ctx.Segments.Fs.Base = 0x7FFF0000; + ctx.Segments.Gs.Base = 0; + } // Dummy values, to resemble regular CR0/CR4 values. ctx.Registers.RegCr0 = 0x0000000080050031; ctx.Registers.RegCr4 = 0x0000000000170678; - ctx.Mode = Options->Mode; - ctx.Ring = Options->Ring; - ctx.TibBase = Options->Mode == ND_CODE_32 ? ctx.Segments.Fs.Base : ctx.Segments.Gs.Base; + if (Options->UseShemuRegs) + { + // Copy the new GPRs + memcpy(&ctx.Registers.RegRax, Options->ShemuRegs, sizeof(Options->ShemuRegs)); + + // Update the stack to point to the new RSP, if one exists + if (ctx.Registers.RegRsp != 0) + { + // Consider the stack base at least with a page before the current RSP. In case of pushes or operations + // which decrease the RSP, we will always give SHEMU_ABORT_BRANCH_OUTSIDE otherwise. + ctx.StackBase = ctx.Registers.RegRsp - 0x1000; + } + } + + + ctx.TibBase = 0x7FFF0000; ctx.MaxInstructionsCount = 4096; ctx.Flags = 0; ctx.Options = SHEMU_OPT_TRACE_EMULATION; @@ -1619,7 +1291,9 @@ handle_shemu( // Check for AES support. int regs[4] = { 0 }; +#if defined(ND_ARCH_X86) || defined(ND_ARCH_X64) __cpuid(regs, 1); +#endif // CPUID leaf function 1, register ECX, bit 25 indicates AES-NI support. if (!!(regs[2] & (1UL << 25))) @@ -1632,24 +1306,10 @@ handle_shemu( ctx.Options |= SHEMU_OPT_BYPASS_SELF_WRITES; } - if (Options->UseShemuRegs) - { - // Copy the new GPRs - memcpy(&ctx.Registers.RegRax, Options->ShemuRegs, sizeof(Options->ShemuRegs)); - - // Update the stack to point to the new RSP, if one exists - if (ctx.Registers.RegRsp != 0) - { - // Consider the stack base at least with a page before the current RSP. In case of pushes or operations - // which decrease the RSP, we will always give SHEMU_ABORT_BRANCH_OUTSIDE otherwise. - ctx.StackBase = ctx.Registers.RegRsp - 0x1000; - } - } - shstatus = ShemuEmulate(&ctx); - printf("Emulation terminated with status 0x%08x, flags: 0x%llx, %u NOPs\n", - shstatus, (unsigned long long)ctx.Flags, ctx.NopCount); + printf("Emulation terminated with status 0x%08x, flags: 0x%llx, %u NOPs, %u total instructions\n", + shstatus, (unsigned long long)ctx.Flags, ctx.NopCount, ctx.InstructionsCount); if (ctx.Flags & SHEMU_FLAG_NOP_SLED) { printf(" SHEMU_FLAG_NOP_SLED\n"); @@ -1662,10 +1322,6 @@ handle_shemu( { printf(" SHEMU_FLAG_WRITE_SELF\n"); } - if (ctx.Flags & SHEMU_FLAG_TIB_ACCESS) - { - printf(" SHEMU_FLAG_TIB_ACCESS\n"); - } if (ctx.Flags & SHEMU_FLAG_SYSCALL) { printf(" SHEMU_FLAG_SYSCALL\n"); @@ -1718,7 +1374,7 @@ handle_shemu( if (INVALID_HANDLE_VALUE == hFile) { printf("Could not open the file %s : 0x%08x\n", fNameDecoded, GetLastError()); - return; + goto cleanup_and_exit; } WriteFile(hFile, (BYTE *)ctx.Shellcode, (DWORD)fsize, &outSize, NULL); @@ -1728,115 +1384,285 @@ handle_shemu( } CloseHandle(hFile); + } + +cleanup_and_exit: + if (NULL != fNameDecoded) + { free(fNameDecoded); } - free(ctx.Shellcode); - free(ctx.Stack); - free(ctx.Intbuf); + if (NULL != ctx.Shellcode) + { + free(ctx.Shellcode); + } + + if (NULL != ctx.Stack) + { + free(ctx.Stack); + } + + if (NULL != ctx.Intbuf) + { + free(ctx.Intbuf); + } } -// -// _tmain -// -int main( - __in int argc, - __in char* argv[] +void print_help() +{ + uint32_t major, minor, revision; + char *date, *time; + + NdGetVersion(&major, &minor, &revision, &date, &time); + + printf("bddisasm version %u.%u.%u, built on %s %s\n", major, minor, revision, date, time); + printf("\n"); + printf("IMPORTANT:\n"); + printf(" This tool is only meant to exemplify bddisasm integration.\n"); + printf("\n"); + printf("USAGE:\n"); + printf(" disasm COMMAND INPUT MODE [OPTIONS]\n"); + printf("\n"); + + printf("COMMAND can be one of:\n"); + printf(" decode - Will decode the input, and print each instruction (default).\n"); + printf(" shemu - Will run the shellcode-emulator on the input, and print the emulation trace.\n"); + printf("\n"); + + printf("INPUT is mandatory and can be one of:\n"); + printf(" -f file - Specify input `file` name.\n"); + printf(" -h hex - Specify input `hex` string. Accepted formats are:\n"); + printf(" Plain hex string; example: 33C090CCC3\n"); + printf(" Escaped hex string; example: \\x33\\xC0\\x90\\xCC\\xC3\n"); + + printf("\n"); + + printf("MODE sets the decode mode:\n"); + printf(" -b16 - Decode in 16-bit mode.\n"); + printf(" -b32 - Decode in 32-bit mode.\n"); + printf(" -b64 - Decode in 64-bit mode (default).\n"); + printf("\n"); + + printf("OPTIONS which are common among different modes:\n"); + printf(" -o offset - Start processing from the indicated `offset` (default is 0).\n"); + printf(" -r rip - Use the indicated `rip` for disassembly (default is 0).\n"); + printf(" -v vendor - Set prefered vendor (default is any). The following are valid `vendor` values:\n"); + printf(" intel, amd, cyrix, mpx, any\n"); + printf(" -t feature - Set prefered feature mode (default is all). The following are valid `feature` values (multiple can be used):\n"); + printf(" none, all, mpx, cet, cldm, piti\n"); + printf("\n"); + + printf("OPTIONS valid only with decode command:\n"); + printf(" -hl - Highlight instruction parts. The colors used are:\n"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY); + printf(" light white prefixes\n"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_INTENSITY); + printf(" light green opcodes\n"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY); + printf(" light yellow modrm and sib\n"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE|FOREGROUND_INTENSITY); + printf(" light blue displacement\n"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_INTENSITY); + printf(" light red relative offset, immediate, address\n"); + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED); + printf(" -nv - Don't print disassembly. Use this only for performance tests.\n"); + printf(" -iv - Print performance statistics.\n"); + printf(" -exi - Print extended info about instructions.\n"); + printf(" -bits - Print the instruction bit fields.\n"); + printf(" -skip16 - Skip 16 bytes after each decoded instruction. Useful when decoding invalid instructions.\n"); + printf("\n"); + printf("OPTIONS valid only with shemu command:\n"); + printf(" -reg val - Set register `reg` to value `val` for emulation. `reg` must be the plain 64-bit register name (ie: rax).\n"); + printf(" -k - Specify kernel mode for shemu emulation (default is user-mode).\n"); + printf(" -bw - Bypass self-modifications in shemu.\n"); + printf("\n"); + printf("\n"); + printf("EXAMPLES:\n"); + printf(" Decode 64-bit code from file test.bin:\n"); + printf(" disasm -f test.bin\n"); + printf(" disasm -b64 -f test.bin\n"); + printf(" disasm decode -b64 -f test.bin\n"); + printf(" Decode 64-bit from a hex-buffer, and display extended instruction information:\n"); + printf(" disasm -b64 -h 909033C0 -exi\n"); + printf(" Decode from hex-string, highlight instruction components & display instruction bitfields:\n"); + printf(" disasm -h 90505833C0E80000000058CC -hl -bits\n"); + printf(" Emulate a potential 32-bit shellcode from test file shell.bin:\n"); + printf(" disasm shemu -b32 -f shell.bin\n"); + printf(" Emulate a potential 32-bit shellcode from test file shell.bin, and specify some input registers:\n"); + printf(" disasm shemu -b32 -f shell.bin -rax 0x100 -rcx 0xABCD -rsp 0x1000\n"); + printf(" Run a quick benchmark on file test.bin:\n"); + printf(" disasm -f test.bin -nv -iv\n"); + printf("\n"); +} + + +void +cleanup_context( + __inout DISASM_OPTIONS *Options ) { - HANDLE hFile, hMapping; - DWORD fsize, offset; - SIZE_T rip; - char text[ND_MIN_BUF_SIZE], *fname, *target, *shemuCtxFname; - BYTE mode, print, highlight, fmode, hmode, stats, exi, vend, feat, search, isShemu, isShemuCtxf, isKernel, bitfields; - BYTE bypassw, skip16; - INT ret, i; - BYTE hexbuf[256], *buffer; - DISASM_OPTIONS options; - - // preinit - buffer = NULL; - memset(text, 0, sizeof(text)); - memset(&options, 0, sizeof(options)); - print = 1; - highlight = 0; - fmode = 0; - hmode = 0; - stats = 0; - exi = 0; - offset = 0; - search = 0; - feat = ND_FEAT_ALL; - vend = ND_VEND_ANY; - fname = NULL; - mode = ND_CODE_16; - hFile = NULL; - hMapping = NULL; - target = NULL; - shemuCtxFname = NULL; - rip = 0; - isShemu = 0; - isShemuCtxf = 0; - isKernel = 0; - bitfields = 0; - bypassw = 0; - skip16 = 0; - - if (NULL == argv) + if (Options->InputMode == inputFile) { - return -1; + if (NULL != Options->Buffer) + { + UnmapViewOfFile(Options->Buffer); + } + + if (NULL != Options->HandleMapping && INVALID_HANDLE_VALUE != Options->HandleMapping) + { + CloseHandle(Options->HandleMapping); + } + + if (NULL != Options->HandleFile && INVALID_HANDLE_VALUE != Options->HandleFile) + { + CloseHandle(Options->HandleFile); + } } +} - if (argc < 3) + +_Success_(return) +BOOLEAN +parse_input( + __inout DISASM_OPTIONS* Options + ) +{ + static BYTE hexbuf[4096]; + + if (inputNone == Options->InputMode) { - uint32_t major, minor, revision; - char *date, *time; - - NdGetVersion(&major, &minor, &revision, &date, &time); - - printf("Napoca Disassembler version %u.%u.%u, built on %s %s\n", major, minor, revision, date, time); - printf("Usage: disasm -f file|-h hex-string -b[16|32|64] [-nv] [-iv] [-hl] [-s] [-c] [-shctxf contextfile] " - "[-reg_name reg_val]\n"); - printf(" -f file specify input file\n"); - printf(" -h hexstring specify hex string with instructions\n"); - printf(" -o offset start disasm at specified offset\n"); - printf(" -r rip use the provided RIP\n"); - printf(" -b[16|32|64] set decoding mode; default is 16\n"); - printf(" -v[intel|amd|cyrix|mpx|any] set preferred vendor\n"); - printf(" -t[none|all|mpx|cet|cldm|piti] set preferred feature mode; default is all\n"); - printf(" -s \"ins\" search for the given instructions\n"); - printf(" -nv don't print disassembly\n"); - printf(" -iv display statistics\n"); - printf(" -exi print extended info about instructions\n"); - printf(" -shemu emulate the file/hex-string\n"); - printf(" -shctxf contextfile specify file that contains the context for shemu. " - "Ignored if shemu is not used. Overrides registers specified in command line\n"); - printf(" File format: each row contains the format: [regname]:[regvalue]\n"); - printf(" -regname regval specify registers to be set for the shemu context. Ignored if shemu is not used\n"); - printf(" Examples of valid command line register naming: \"RegRax\" ; \"rax\" ; \"reg_rax\"\n"); - printf(" -k specify kernel mode for shemu emulation. Ignore if shemu is not specified.\n"); - printf(" -bw bypass self-modifications for shemu emulation.\n"); - printf(" -hl highlight instruction parts:\n"); - printf(" -bits display the instruction bit fields"); - printf(" -skip16 skip 16 bytes after each decoded instruction"); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), - FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY); - printf(" light white prefixes\n"); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_INTENSITY); - printf(" light green opcodes\n"); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY); - printf(" light yellow modrm and sib\n"); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE|FOREGROUND_INTENSITY); - printf(" light blue displacement\n"); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_INTENSITY); - printf(" light red relative offset, immediate, address\n"); - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED); - printf("Instrux size: 0x%zx bytes\n", sizeof(INSTRUX)); - ret = -1; - goto cleanup_and_exit; + printf("Expecting an input mode: either -f or -h!\n"); + return FALSE; } + if (inputFile == Options->InputMode) + { + // Open the file. + Options->HandleFile = CreateFileA(Options->FileName, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == Options->HandleFile) + { + printf("Couldn't open file '%s': 0x%08x\n", Options->FileName, GetLastError()); + cleanup_context(Options); + return FALSE; + } + + // Create a file mapping. + Options->HandleMapping = CreateFileMappingA(Options->HandleFile, NULL, PAGE_READWRITE, 0, 0, "DisasmFile"); + if (NULL == Options->HandleMapping) + { + printf("Couldn't create file mapping for '%s': 0x%08x\n", Options->FileName, GetLastError()); + cleanup_context(Options); + return FALSE; + } + + // Map the file. + Options->Buffer = (BYTE *)MapViewOfFile(Options->HandleMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (NULL == Options->Buffer) + { + printf("Couldn't map the view for '%s': 0x%08x\n", Options->FileName, GetLastError()); + cleanup_context(Options); + return FALSE; + } + + Options->Size = GetFileSize(Options->HandleFile, NULL); + } + else + { + DWORD idx, sx = 0, mx, of; + + Options->Size = (DWORD)strlen(Options->FileName); + + if (Options->Size < 2) + { + printf("Min 1-byte buffer needed!\n"); + return FALSE; + } + + if (Options->Size % 2 == 1) + { + printf("Even-sized hex buffer expected!\n"); + return FALSE; + } + + if (Options->FileName[0] == '\\' && Options->FileName[1] == 'x') + { + sx = 1; + } + + if (sx && Options->Size < 4) + { + printf("Min 1-byte buffer needed!\n"); + return FALSE; + } + + if (sx) + { + mx = 4; + of = 2; + } + else + { + mx = 2; + of = 0; + } + + if (Options->Size / mx > sizeof(hexbuf)) + { + printf("Max %zu bytes buffer accepted!\n", sizeof(hexbuf)); + return FALSE; + } + + for (idx = 0; idx < Options->Size / mx; idx++) + { + hexbuf[idx] = ((hex_to_bin(Options->FileName[idx * mx + of]) << 4) | + (hex_to_bin(Options->FileName[idx * mx + of + 1]))) & 0xFF; + } + + Options->FileName = NULL; + Options->Size /= sx ? 4 : 2; + Options->Buffer = hexbuf; + } + + if (Options->Offset >= Options->Size) + { + printf("The offset exceeds the buffer size!\n"); + cleanup_context(Options); + return FALSE; + } + + return TRUE; +} + + +_Success_(return) +BOOLEAN +parse_arguments( + __in int argc, + __in char* argv[], + __out DISASM_OPTIONS *Options + ) +{ + int i; + + if (argc < 2 || NULL == argv) + { + print_help(); + return FALSE; + } + + memset(Options, 0, sizeof(*Options)); + + // Initialize default options. + Options->Command = commandDecode; + Options->Mode = ND_CODE_64; + Options->Ring = 3; + Options->Print = TRUE; + Options->Vendor = ND_VEND_ANY; + Options->Feature = ND_FEAT_ALL; + i = 1; while (i < argc) { @@ -1850,32 +1676,21 @@ int main( } else { - options.ShemuRegs[gprIdx] = (size_t)strtoull(argv[i + 1], NULL, 0); - options.UseShemuRegs = TRUE; + Options->ShemuRegs[gprIdx] = (size_t)strtoull(argv[i + 1], NULL, 0); + Options->UseShemuRegs = TRUE; i++; } } - else if (strcmp(argv[i], "-shctxf") == 0) + else if (strcmp(argv[i], "shemu") == 0) { - if (i + 1 < argc) - { - isShemuCtxf = 1; - options.UseShemuRegs = TRUE; - - shemuCtxFname = argv[i + 1]; - i++; - } - } - else if (strcmp(argv[i], "-shemu") == 0) - { - isShemu = 1; + Options->Command = commandShemu; } else if (argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0) { if (i + 1 < argc) { - fmode = 1; - fname = argv[i + 1]; + Options->InputMode = inputFile; + Options->FileName = argv[i + 1]; i++; } } @@ -1883,8 +1698,8 @@ int main( { if (i + 1 < argc) { - hmode = 1; - fname = argv[i + 1]; + Options->InputMode = inputHex; + Options->FileName = argv[i + 1]; i++; } } @@ -1892,7 +1707,7 @@ int main( { if (i + 1 < argc) { - sscanf_s(argv[i + 1], "%x", &offset); + sscanf_s(argv[i + 1], "%zx", &Options->Offset); i++; } } @@ -1900,135 +1715,117 @@ int main( { if (i + 1 < argc) { - sscanf_s(argv[i + 1], "%zx", &rip); + sscanf_s(argv[i + 1], "%zx", &Options->Rip); i++; } } else if (argv[i][0] == '-' && argv[i][1] == 'k' && argv[i][2] == 0) { - isKernel = 1; + Options->Ring = 0; } else if (argv[i][0] == '-' && argv[i][1] == 'b' && argv[i][2] == 'w' && argv[i][3] == 0) { - bypassw = 1; + Options->BypassSelfWrites = TRUE; } else if (0 == strcmp(argv[i], "-b16")) { - mode = ND_CODE_16; + Options->Mode = ND_CODE_16; } else if (0 == strcmp(argv[i], "-b32")) { - mode = ND_CODE_32; + Options->Mode = ND_CODE_32; } else if (0 == strcmp(argv[i], "-b64")) { - mode = ND_CODE_64; + Options->Mode = ND_CODE_64; } - else if (0 == strcmp(argv[i], "-vintel")) + else if (0 == strcmp(argv[i], "-v intel")) { - vend = ND_VEND_INTEL; + Options->Vendor = ND_VEND_INTEL; } - else if (0 == strcmp(argv[i], "-vamd")) + else if (0 == strcmp(argv[i], "-v amd")) { - vend = ND_VEND_AMD; + Options->Vendor = ND_VEND_AMD; } - else if (0 == strcmp(argv[i], "-vgeode")) + else if (0 == strcmp(argv[i], "-v geode")) { - vend = ND_VEND_GEODE; + Options->Vendor = ND_VEND_GEODE; } - else if (0 == strcmp(argv[i], "-vcyrix")) + else if (0 == strcmp(argv[i], "-v cyrix")) { - vend = ND_VEND_CYRIX; + Options->Vendor = ND_VEND_CYRIX; } - else if (0 == strcmp(argv[i], "-vany")) + else if (0 == strcmp(argv[i], "-v any")) { - vend = ND_VEND_ANY; + Options->Vendor = ND_VEND_ANY; } - else if (0 == strcmp(argv[i], "-tall")) + else if (0 == strcmp(argv[i], "-t all")) { - feat = ND_FEAT_ALL; + Options->Feature = ND_FEAT_ALL; } - else if (0 == strcmp(argv[i], "-tmpx")) + else if (0 == strcmp(argv[i], "-t mpx")) { - if (feat == ND_FEAT_ALL) + if (Options->Feature == ND_FEAT_ALL) { - feat = 0; + Options->Feature = 0; } - feat |= ND_FEAT_MPX; + Options->Feature |= ND_FEAT_MPX; } - else if (0 == strcmp(argv[i], "-tcet")) + else if (0 == strcmp(argv[i], "-t cet")) { - if (feat == ND_FEAT_ALL) + if (Options->Feature == ND_FEAT_ALL) { - feat = 0; + Options->Feature = 0; } - feat |= ND_FEAT_CET; + Options->Feature |= ND_FEAT_CET; } - else if (0 == strcmp(argv[i], "-tcldm")) + else if (0 == strcmp(argv[i], "-t cldm")) { - if (feat == ND_FEAT_ALL) + if (Options->Feature == ND_FEAT_ALL) { - feat = 0; + Options->Feature = 0; } - feat |= ND_FEAT_CLDEMOTE; + Options->Feature |= ND_FEAT_CLDEMOTE; } - else if (0 == strcmp(argv[i], "-tpiti")) + else if (0 == strcmp(argv[i], "-t piti")) { - if (feat == ND_FEAT_ALL) + if (Options->Feature == ND_FEAT_ALL) { - feat = 0; + Options->Feature = 0; } - feat |= ND_FEAT_PITI; + Options->Feature |= ND_FEAT_PITI; } - else if (0 == strcmp(argv[i], "-tnone")) + else if (0 == strcmp(argv[i], "-t none")) { - feat = ND_FEAT_NONE; + Options->Feature = ND_FEAT_NONE; } else if (0 == strcmp(argv[i], "-nv")) { - print = 0; + Options->Print = FALSE; } else if (0 == strcmp(argv[i], "-hl")) { - highlight = 1; + Options->Highlight = TRUE; } else if (0 == strcmp(argv[i], "-iv")) { - stats = 1; + Options->Stats = TRUE; } else if (0 == strcmp(argv[i], "-exi")) { - exi = 1; - } - else if (0 == strcmp(argv[i], "-s")) - { - search = 1; - - if (i + 1 == argc) - { - printf("-s requires an argument!\n"); - return -1; - } - - target = argv[++i]; - - if (strlen(target) >= ND_MIN_BUF_SIZE) - { - printf("Target instruction too long! Max is %d bytes!\n", ND_MIN_BUF_SIZE); - return -1; - } + Options->ExtendedInfo = TRUE; } else if (0 == strcmp(argv[i], "-bits")) { - bitfields = 1; + Options->BitFields = TRUE; } else if (0 == strcmp(argv[i], "-skip16")) { - skip16 = 1; + Options->Skip16 = TRUE; } else { @@ -2038,145 +1835,39 @@ int main( i++; } - if (0 == fmode && 0 == hmode) - { - printf("Expecting -f or -h option!\n"); - return -1; - } - - if (fmode) + if (!parse_input(Options)) { - // Open the file. - hFile = CreateFileA(fname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == hFile) - { - printf("Couldn't open file '%s': 0x%08x\n", fname, GetLastError()); - ret = -1; - goto cleanup_and_exit; - } - - // Create a file mapping. - hMapping = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, "DisasmFile"); - if (NULL == hMapping) - { - printf("Couldn't create file mapping for '%s': 0x%08x\n", argv[1], GetLastError()); - ret = -1; - goto cleanup_and_exit; - } - - // Map the file. - buffer = (BYTE *)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); - if (NULL == buffer) - { - printf("Couldn't map the view for '%s': 0x%08x\n", argv[1], GetLastError()); - ret = -1; - goto cleanup_and_exit; - } - - fsize = GetFileSize(hFile, NULL); + printf("Could not find a valid input!\n"); + return FALSE; } - else - { - DWORD idx; - memset(hexbuf, 0, sizeof(hexbuf)); - - fsize = (DWORD)strlen(fname); - - if (fsize > 512) - { - printf("Max 256-bytes buffer accepted!\n"); - return -1; - } - - if (fsize % 2 == 1) - { - printf("Even-sized hex buffer expected!\n"); - return -1; - } + return TRUE; +} - for (idx = 0; idx < fsize / 2; idx++) - { - hexbuf[idx] = ((hex_to_bin(fname[idx * 2]) << 4) | (hex_to_bin(fname[idx * 2 + 1]))) & 0xFF; - } - fname = NULL; - fsize /= 2; - buffer = hexbuf; - } +int +main( + __in int argc, + __in char* argv[] + ) +{ + DISASM_OPTIONS options = { 0 }; - if (offset >= fsize) + if (!parse_arguments(argc, argv, &options)) { - printf("The offset exceeds the buffer size!\n"); - ret = -1; - goto cleanup_and_exit; + return -1; } - options.FileName = fname; - options.Buffer = buffer; - options.Size = fsize; - options.ExtendedInfo = exi; - options.BitFields = bitfields; - options.Skip16 = skip16; - options.Highlight = highlight; - options.Mode = mode; - options.Ring = isKernel ? 0 : 3; - options.Offset = offset; - options.Stats = stats; - options.Search = search; - options.Target = target; - options.Print = print; - options.Vendor = vend; - options.Feature = feat; - options.Rip = rip; - options.BypassSelfWrites = bypassw; - - if (isShemu) + if (options.Command == commandShemu) { - if (isShemuCtxf) - { - set_shemuctx_file(shemuCtxFname, &options); - } - handle_shemu(&options); } - else if (search) - { - handle_search(&options); - } else { handle_disasm(&options); } - // All should be good. - ret = 0; + cleanup_context(&options); -cleanup_and_exit: - if (fmode) - { - if (NULL != buffer) - { - UnmapViewOfFile(buffer); - - buffer = NULL; - } - - if (NULL != hMapping) - { - CloseHandle(hMapping); - - hMapping = NULL; - } - - if ((NULL != hFile) && (INVALID_HANDLE_VALUE != hFile)) - { - CloseHandle(hFile); - - hFile = NULL; - } - } - - return ret; + return 0; } diff --git a/disasmtool/disasmtool.h b/disasmtool/disasmtool.h new file mode 100644 index 0000000..1f9c586 --- /dev/null +++ b/disasmtool/disasmtool.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Bitdefender + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef DISASMTOOL_H +#define DISASMTOOL_H + + +typedef enum _DISASM_COMMAND +{ + commandNone = 0, + commandDecode, + commandShemu, +} DISASM_COMMAND; + + +typedef enum _INPUT_MODE +{ + inputNone = 0, + inputFile, + inputHex +} INPUT_MODE; + + +typedef struct _DISASM_OPTIONS +{ + DISASM_COMMAND Command; // Command. + uint8_t *Buffer; // The buffer containing the instructions. + size_t Size; // Buffer size. + size_t Offset; // Offset inside the buffer. + size_t Rip; // Virtual RIP. + BOOLEAN Highlight; // Highlight instruction components, if true. + BOOLEAN ExtendedInfo; // Display extended instruction info, if true. + BOOLEAN BitFields; // Display the various bitfields inside the instruction, if true. + BOOLEAN Skip16; // Automatically jump over 16 bytes after each instruction. + BOOLEAN Stats; // Display disassembly stats (clocks / instruction, instructions / second), if true. + BOOLEAN Print; // Print instruction disassembly, if true. + uint8_t Mode; // Mode - 16, 32 or 64-bit mode. + uint8_t Ring; // Ring - 0, 1, 2 or 3. + uint8_t Vendor; // Preffered vendor. + uint8_t Feature; // Used features. + char *FileName; // Input file, if any. + size_t ShemuRegs[ND_MAX_GPR_REGS]; + BOOLEAN UseShemuRegs; // If truue, the registers in ShemuRegs will be used for shemu input. + BOOLEAN BypassSelfWrites; // If true, shemu emulation will ignore self-modifications made by the shellcode. + + // Internal. + INPUT_MODE InputMode; + HANDLE HandleFile; + HANDLE HandleMapping; + +} DISASM_OPTIONS, *PDISASM_OPTIONS; + + +#define PAGE_SIZE 0x1000 +#define PAGE_MASK 0xFFFFFFFFFFFFF000 + + +#endif // DISASMTOOL_H diff --git a/disasmtool/disasmtool.vcxproj b/disasmtool/disasmtool.vcxproj index 5c6269b..f2cbf04 100644 --- a/disasmtool/disasmtool.vcxproj +++ b/disasmtool/disasmtool.vcxproj @@ -307,6 +307,9 @@ {3c9b2ca7-cf4f-471b-bb72-6490c476cdca} + + + diff --git a/disasmtool/disasmtool.vcxproj.filters b/disasmtool/disasmtool.vcxproj.filters index c7167ac..6cee788 100644 --- a/disasmtool/disasmtool.vcxproj.filters +++ b/disasmtool/disasmtool.vcxproj.filters @@ -19,4 +19,9 @@ Source Files + + + Header Files + + \ No newline at end of file