Skip to content

Commit

Permalink
Specifically flag multi-byte NOP operands as not-accessed.
Browse files Browse the repository at this point in the history
New capability - bddisasm can now be instructed whether to decode some instructions as NOPs are as MPX/CET/CLDEMOTE. This is the case for instructions that are mapped onto the wide NOP space: in that case, an encoding might be NOP if the feature is off, but might be something else (even #UD) if the feature is on.
Added NdDecodeWithContext API - this becomes the base decode API; it received the input information filled in a ND_CONTEXT structure, whih has to be initialized only once, and can be reused across calls. The NdInitContext function must be used to initialize the context, as it ensures backwards compatibility by filling new options with default values.
Improvements to the README file.
  • Loading branch information
vlutas committed Jul 30, 2020
1 parent 4328dc4 commit ed564db
Show file tree
Hide file tree
Showing 14 changed files with 4,402 additions and 3,993 deletions.
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,22 @@ In order to build bddisasm and bdshemu run `make` in the root of the repository.

In order to build disasmtool_lix go to the disasmtool_lix directory and run `make`. The results will be in the bin directory in the disasmtool_lix/build directory.

## Example
## Decoding instructions

### Decoding API

There are 4 decoding functions, but internally, they all do the same, albeit some of them with implicit arguments:
* `NDSTATUS NdDecode(INSTRUX *Instrux, const uint8_t *Code, uint8_t DefCode, uint8_t DefData)` - this API should be used only if you don't care about the length of the input buffer;
* `NDSTATUS NdDecodeEx(INSTRUX *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode, uint8_t DefData);` - decode instruction from a buffer with maximum length `Size`;
* `NDSTATUS NdDecodeEx2(INSTRUX *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode, uint8_t DefData, uint8_t DefStack, uint8_t PreferedVendor);` - decode instructions with a preferred vendor;
* `NDSTATUS NdDecodeWithContext(INSTRUX *Instrux, const uint8_t *Code, size_t Size, ND_CONTEXT *Context);` - base decode API; the input parameters - `DefCode`, `DefData`, `DefStack`, `VendMode` and `FeatMode` must all be filled in the `Context` structure before calling this function. The Context structure should also be initialized using `NdInitContext` before the first decode call.

Note that by default, the default vendor `ND_VEND_ANY` is used for decoding (which means that bddisasm will try to decode as much as possible). Also, the default features mask is `ND_FEAT_ALL`, meaning that bddisasm will optimistically try to decode instructions which are mapped onto the wide NOP space as well (for example, MPX or CET). If these parameters must be changed, it is advised to use the `NdDecodeWithContext` API.

Converting decoded instructions to textual disassembly must be done using the `NdToText` API. bddisasm only supports Intel, masm-style syntax.


### Example

Working with bddisasm is very easy. Decoding and printing the disassembly of an instruction is quick & simple:

Expand Down Expand Up @@ -160,6 +175,26 @@ int main()
}
```
Working with the extended API is also trivial:
```c
INSTRUX ix;
ND_CONTEXT ctx;
uint8_t code[] = { 0x48, 0x8B, 0x48, 0x28 };
// This has to be done only once.
NdInitContext(&ctx);
ctx.DefCode = ND_CODE_64;
ctx.DefData = ND_DATA_64;
ctx.DefStack = ND_STACK_64;
ctx.VendMode = ND_VEND_ANY;
ctx.FeatMode = ND_FEAT_ALL; // Use ND_FEAT_NONE, if you wish to see NOPs instead of MPX/CET/CLDEMOTE instructions.
// From here one, the ctx can be reused for any number of NdDecodeWithContext calls.
NDSTATUS status = NdDecodeWithContext(&ix, code, sizeof(code), &ctx);
...
```

## Credits

The entire Bitdefender HVI team.
77 changes: 67 additions & 10 deletions bddisasm/bddisasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -3097,7 +3097,6 @@ NdFindInstruction(
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint8_t Vendor,
ND_INSTRUCTION **InsDef
)
{
Expand Down Expand Up @@ -3386,16 +3385,36 @@ NdFindInstruction(

case ND_ILUT_VENDOR:
// Vendor redirection. Go to the vendor specific entry.
if (NULL != pTable->Table[Vendor])
if (NULL != pTable->Table[Instrux->VendMode])
{
pTable = (const ND_TABLE *)pTable->Table[Vendor];
pTable = (const ND_TABLE *)pTable->Table[Instrux->VendMode];
}
else
{
pTable = (const ND_TABLE *)pTable->Table[ND_VEND_ANY];
}
break;

case ND_ILUT_FEATURE:
// Feature redirection. Normally NOP if feature is not set, but may be something else if feature is set.
if ((NULL != pTable->Table[ND_ILUT_FEATURE_MPX]) && !!(Instrux->FeatMode & ND_FEAT_MPX))
{
pTable = (const ND_TABLE *)pTable->Table[ND_ILUT_FEATURE_MPX];
}
else if ((NULL != pTable->Table[ND_ILUT_FEATURE_CET]) && !!(Instrux->FeatMode & ND_FEAT_CET))
{
pTable = (const ND_TABLE *)pTable->Table[ND_ILUT_FEATURE_CET];
}
else if ((NULL != pTable->Table[ND_ILUT_FEATURE_CLDEMOTE]) && !!(Instrux->FeatMode & ND_FEAT_CLDEMOTE))
{
pTable = (const ND_TABLE *)pTable->Table[ND_ILUT_FEATURE_CLDEMOTE];
}
else
{
pTable = (const ND_TABLE *)pTable->Table[ND_ILUT_FEATURE_NONE];
}
break;

case ND_ILUT_VEX_MMMMM:
pTable = (const ND_TABLE *)pTable->Table[Instrux->Exs.m];
break;
Expand Down Expand Up @@ -3816,6 +3835,28 @@ NdDecodeEx2(
uint8_t DefStack,
uint8_t Vendor
)
{
ND_CONTEXT opt;

NdInitContext(&opt);

opt.DefCode = DefCode;
opt.DefData = DefData;
opt.DefStack = DefStack;
opt.VendMode = Vendor;
opt.FeatMode = ND_FEAT_ALL; // Optimistically decode everything, as if all features are enabled.

return NdDecodeWithContext(Instrux, Code, Size, &opt);
}


NDSTATUS
NdDecodeWithContext(
INSTRUX *Instrux,
const uint8_t *Code,
size_t Size,
ND_CONTEXT *Context
)
{
NDSTATUS status;
PND_INSTRUCTION pIns;
Expand All @@ -3842,27 +3883,34 @@ NdDecodeEx2(
return ND_STATUS_INVALID_PARAMETER;
}

if (ND_CODE_64 < DefCode)
if (NULL == Context)
{
return ND_STATUS_INVALID_PARAMETER;
}

if (ND_CODE_64 < Context->DefCode)
{
return ND_STATUS_INVALID_PARAMETER;
}

if (ND_DATA_64 < DefData)
if (ND_DATA_64 < Context->DefData)
{
return ND_STATUS_INVALID_PARAMETER;
}

if (ND_VEND_CYRIX < Vendor)
if (ND_VEND_CYRIX < Context->VendMode)
{
return ND_STATUS_INVALID_PARAMETER;
}

// Initialize with zero.
nd_memzero(Instrux, sizeof(INSTRUX));

Instrux->DefCode = DefCode;
Instrux->DefData = DefData;
Instrux->DefStack = DefStack;
Instrux->DefCode = (uint8_t)Context->DefCode;
Instrux->DefData = (uint8_t)Context->DefData;
Instrux->DefStack = (uint8_t)Context->DefStack;
Instrux->VendMode = (uint8_t)Context->VendMode;
Instrux->FeatMode = (uint8_t)Context->FeatMode;

// Fetch prefixes. We peek at the first byte, if that's not a prefix, there's no need to call the main decoder.
if (ND_PREF_CODE_NONE != gPrefixesMap[Code[0]])
Expand All @@ -3882,7 +3930,7 @@ NdDecodeEx2(
}

// Start iterating the tables, in order to extract the instruction entry.
status = NdFindInstruction(Instrux, Code, Instrux->Length, Size, Vendor, &pIns);
status = NdFindInstruction(Instrux, Code, Instrux->Length, Size, &pIns);
if (!ND_SUCCESS(status))
{
return status;
Expand Down Expand Up @@ -5101,3 +5149,12 @@ NdGetFullAccessMap(

return ND_STATUS_SUCCESS;
}


void
NdInitContext(
ND_CONTEXT *Context
)
{
nd_memzero(Context, sizeof(*Context));
}

0 comments on commit ed564db

Please sign in to comment.