iced/README.md

898 lines
33 KiB
Markdown
Raw Normal View History

2019-10-05 15:57:58 +00:00
# Iced [![NuGet](https://img.shields.io/nuget/v/Iced.svg)](https://www.nuget.org/packages/Iced/) [![GitHub builds](https://github.com/0xd4d/iced/workflows/GitHub%20CI/badge.svg)](https://github.com/0xd4d/iced/actions) [![codecov](https://codecov.io/gh/0xd4d/iced/branch/master/graph/badge.svg)](https://codecov.io/gh/0xd4d/iced)
2018-09-07 20:48:32 +00:00
2018-09-21 19:30:08 +00:00
<img align="right" width="160px" height="160px" src="logo.png">
2020-01-05 12:12:25 +00:00
High performance x86 (16/32/64-bit) instruction decoder, disassembler and assembler.
2018-09-05 23:29:23 +00:00
It can be used for static analysis of x86/x64 binaries, to rewrite code (eg. remove garbage instructions), to relocate code or as a disassembler.
2018-09-12 21:33:12 +00:00
- Supports all Intel and AMD instructions
- High level [Assembler](#assemble-instructions) providing a simple and lean syntax (e.g `asm.mov(eax, edx)`))
- [Decoding](#disassemble-decode-and-format-instructions) and disassembler support:
2020-01-24 08:04:13 +00:00
- The decoder doesn't allocate any memory and is 2x-5x+ faster than other similar libraries written in C or C#
- Small decoded instructions, only 32 bytes
- The formatter supports masm, nasm, gas (AT&T), Intel (XED) and there are many options to customize the output
- Encoding support:
- The encoder can be used to re-encode decoded instructions at any address
- The block encoder encodes a list of instructions and optimizes branches to short, near or 'long' (64-bit: 1 or more instructions)
2018-09-05 23:29:23 +00:00
- API to get instruction info, eg. read/written registers, memory and rflags bits; CPUID feature flag, flow control info, etc
- All instructions are tested (decode, encode, format, instruction info)
- Supports `.NET Standard 2.0/2.1+` and `.NET Framework 4.5+`
- License: MIT
2018-09-05 23:29:23 +00:00
2018-09-07 19:33:05 +00:00
# Classes
2018-09-05 23:29:23 +00:00
2018-09-06 18:56:16 +00:00
See below for some examples. All classes are in the `Iced.Intel` namespace.
2018-09-05 23:29:23 +00:00
Decoder:
- `Decoder`
2019-12-30 21:10:38 +00:00
- `Instruction` (and `Instruction.Create()` methods)
2018-09-05 23:29:23 +00:00
- `CodeReader`
- `ByteArrayCodeReader`
- `StreamCodeReader`
2018-10-04 19:03:30 +00:00
- `InstructionList`
2018-09-05 23:29:23 +00:00
- `ConstantOffsets`
2019-02-23 11:15:10 +00:00
- `IcedFeatures.Initialize()`
2018-09-05 23:29:23 +00:00
Formatters:
- `Formatter`
- `MasmFormatter`
- `NasmFormatter`
- `GasFormatter`
- `IntelFormatter`
2018-09-05 23:29:23 +00:00
- `FormatterOptions`
- `MasmFormatterOptions`
- `NasmFormatterOptions`
- `GasFormatterOptions`
- `IntelFormatterOptions`
2018-09-05 23:29:23 +00:00
- `FormatterOutput`
- `StringOutput`
2018-10-27 18:50:44 +00:00
- `ISymbolResolver`
- `IFormatterOptionsProvider`
2018-09-05 23:29:23 +00:00
Assembler:
- `Assembler`
- `Label`
- `AssemblerRegisters` (use `using static` to have access directly to registers e.g `eax`, `rdi`, `xmm1`...)
2018-09-05 23:29:23 +00:00
Encoder:
- `Encoder`
- `BlockEncoder`
- `CodeWriter`
2020-01-24 08:04:13 +00:00
- `StreamCodeWriter`
2018-09-05 23:29:23 +00:00
- `ConstantOffsets`
2019-08-22 19:45:33 +00:00
- `OpCodeInfo` (`Instruction.OpCode` and `Code.ToOpCode()`)
2018-09-05 23:29:23 +00:00
Instruction info:
- `InstructionInfo`
- `InstructionInfoFactory`
- `InstructionInfoExtensions`
- `MemorySizeExtensions`
- `RegisterExtensions`
# How-tos
2018-09-05 23:29:23 +00:00
- [Disassemble (decode and format instructions)](#disassemble-decode-and-format-instructions)
- [Assemble instructions](#assemble-instructions)
- [Disassemble with a symbol resolver](#disassemble-with-a-symbol-resolver)
- [Disassemble with colorized text](#disassemble-with-colorized-text)
- [Move code in memory (eg. hook a function)](#move-code-in-memory-eg-hook-a-function)
- [Get instruction info, eg. read/written regs/mem, control flow info, etc](#get-instruction-info-eg-readwritten-regsmem-control-flow-info-etc)
## Disassemble (decode and format instructions)
2019-02-03 15:25:26 +00:00
2018-09-07 19:09:29 +00:00
```C#
using System;
using Iced.Intel;
static class HowTo_Disassemble {
/*
* This method produces the following output:
2019-02-03 15:25:26 +00:00
00007FFAC46ACDA4 48895C2410 mov [rsp+10h],rbx
00007FFAC46ACDA9 4889742418 mov [rsp+18h],rsi
00007FFAC46ACDAE 55 push rbp
00007FFAC46ACDAF 57 push rdi
00007FFAC46ACDB0 4156 push r14
00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]
00007FFAC46ACDBA 4881EC00020000 sub rsp,200h
00007FFAC46ACDC1 488B0518570A00 mov rax,[rel 7FFA`C475`24E0h]
00007FFAC46ACDC8 4833C4 xor rax,rsp
00007FFAC46ACDCB 488985F0000000 mov [rbp+0F0h],rax
00007FFAC46ACDD2 4C8B052F240A00 mov r8,[rel 7FFA`C474`F208h]
00007FFAC46ACDD9 488D05787C0400 lea rax,[rel 7FFA`C46F`4A58h]
00007FFAC46ACDE0 33FF xor edi,edi
*/
public static void Example() {
// You can also pass in a hex string, eg. "90 91 929394", or you can use your own CodeReader
// reading data from a file or memory etc
var codeBytes = exampleCode;
var codeReader = new ByteArrayCodeReader(codeBytes);
var decoder = Decoder.Create(exampleCodeBitness, codeReader);
decoder.IP = exampleCodeRIP;
ulong endRip = decoder.IP + (uint)codeBytes.Length;
// This list is faster than List<Instruction> since it uses refs to the Instructions
// instead of copying them (each Instruction is 32 bytes in size). It has a ref indexer,
// and a ref iterator. Add() uses 'in' (ref readonly).
var instructions = new InstructionList();
while (decoder.IP < endRip) {
// The method allocates an uninitialized element at the end of the list and
// returns a reference to it which is initialized by Decode().
decoder.Decode(out instructions.AllocUninitializedElement());
}
2018-10-04 19:03:30 +00:00
// Formatters: Masm*, Nasm*, Gas* (AT&T) and Intel* (XED)
var formatter = new NasmFormatter();
formatter.Options.DigitSeparator = "`";
formatter.Options.FirstOperandCharIndex = 10;
var output = new StringOutput();
// Use InstructionList's ref iterator (C# 7.3) to prevent copying 32 bytes every iteration
foreach (ref var instr in instructions) {
// Don't use instr.ToString(), it allocates more, uses masm syntax and default options
formatter.Format(instr, output);
Console.Write(instr.IP.ToString("X16"));
Console.Write(" ");
int instrLen = instr.Length;
int byteBaseIndex = (int)(instr.IP - exampleCodeRIP);
for (int i = 0; i < instrLen; i++)
Console.Write(codeBytes[byteBaseIndex + i].ToString("X2"));
int missingBytes = HEXBYTES_COLUMN_BYTE_LENGTH - instrLen;
for (int i = 0; i < missingBytes; i++)
Console.Write(" ");
Console.Write(" ");
Console.WriteLine(output.ToStringAndReset());
2018-09-07 19:09:29 +00:00
}
}
2018-09-07 19:09:29 +00:00
const int HEXBYTES_COLUMN_BYTE_LENGTH = 10;
const int exampleCodeBitness = 64;
const ulong exampleCodeRIP = 0x00007FFAC46ACDA4;
static readonly byte[] exampleCode = new byte[] {
0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D,
0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05,
0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B,
0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF
};
}
```
## Assemble instructions
```C#
using System;
using System.IO;
using Iced.Intel;
using static Iced.Intel.AssemblerRegisters;
static class HowTo_Assemble {
/*
* This method produces the following output:
10000000 = push r15
10000002 = add rax,r15
10000005 = mov rax,[rax]
10000008 = mov rax,[rax]
1000000B = cmp dword ptr [rax+rcx*8+10h],0FFFFFFFFh
2020-01-25 20:05:41 +00:00
10000010 = jne short 000000001000003Dh
10000012 = inc rax
2020-01-25 20:05:41 +00:00
10000015 = lea rcx,[10000040h]
1000001C = rep stosd
1000001E = xacquire lock add qword ptr [rax+rcx],7Bh
10000025 = vaddpd zmm1{k3}{z},zmm2,zmm3 {rz-sae}
1000002B = vunpcklps xmm2{k5}{z},xmm6,dword bcst [rax]
2020-01-25 20:05:41 +00:00
10000031 = inc rax
10000034 = je short 0000000010000031h
10000036 = inc rcx
10000039 = je short 000000001000003Ch
1000003B = nop
1000003C = nop
1000003D = pop r15
1000003F = ret
10000040 = pause
*/
public static MemoryStream Example() {
// The assembler supports all modes: 16-bit, 32-bit and 64-bit.
var c = new Assembler(64);
var label1 = c.CreateLabel();
2020-01-25 17:12:09 +00:00
var data1 = c.CreateLabel();
c.push(r15);
c.add(rax, r15);
// If the memory operand can only have one size, __[] can be used. The assembler ignores
// the memory size unless it's an ambiguous instruction, eg. 'add [mem],123'
c.mov(rax, __[rax]);
c.mov(rax, __qword_ptr[rax]);
// The assembler must know the memory size to pick the correct instruction
c.cmp(__dword_ptr[rax + rcx * 8 + 0x10], -1);
c.jne(label1); // Jump to Label1
c.inc(rax);
// Labels can be referenced by memory operands (64-bit only) and call/jmp/jcc/loopcc instructions
2020-01-25 17:12:09 +00:00
c.lea(rcx, __[data1]);
// The assembler has prefix properties that will be added to the following instruction
c.rep.stosd();
c.xacquire.@lock.add(__qword_ptr[rax + rcx], 123);
// The assembler defaults to VEX instructions. If you need EVEX instructions, set PreferVex=false
c.PreferVex = false;
// AVX-512 decorators are properties on the memory and register operands
c.vaddpd(zmm1.k3.z, zmm2, zmm3.rz_sae);
// To broadcast memory, use the __dword_bcst/__qword_bcst memory types
c.vunpcklps(xmm2.k5.z, xmm6, __dword_bcst[rax]);
2020-01-26 11:11:25 +00:00
// You can create anonymous labels, just like in eg. masm, @@, @F and @B
2020-01-25 20:05:41 +00:00
c.AnonymousLabel(); // same as @@: in masm
c.inc(rax);
c.je(c.@B); // reference the previous anonymous label
c.inc(rcx);
c.je(c.@F); // reference the next anonymous label
c.nop();
c.AnonymousLabel();
c.nop();
// Emit label1:
c.Label(ref label1);
c.pop(r15);
c.ret();
2020-01-25 17:12:09 +00:00
c.Label(ref data1);
c.db(0xF3, 0x90); // pause
const ulong RIP = 0x1000_0000;
var stream = new MemoryStream();
c.Assemble(new StreamCodeWriter(stream), RIP);
// Disassemble the result
stream.Position = 0;
var reader = new StreamCodeReader(stream);
var decoder = Decoder.Create(64, reader);
decoder.IP = RIP;
while (stream.Position < stream.Length) {
decoder.Decode(out var instr);
Console.WriteLine($"{instr.IP:X} = {instr}");
}
return stream;
}
}
```
## Disassemble with a symbol resolver
```C#
using System;
using System.Collections.Generic;
using Iced.Intel;
static class HowTo_SymbolResolver {
sealed class SymbolResolver : ISymbolResolver {
readonly Dictionary<ulong, string> symbolDict;
public SymbolResolver(Dictionary<ulong, string> symbolDict) {
this.symbolDict = symbolDict;
}
public bool TryGetSymbol(in Instruction instruction, int operand, int instructionOperand,
ulong address, int addressSize, out SymbolResult symbol) {
if (symbolDict.TryGetValue(address, out var symbolText)) {
// The 'address' arg is the address of the symbol and doesn't have to be identical
// to the 'address' arg passed to TryGetSymbol(). If it's different from the input
// address, the formatter will add +N or -N, eg. '[rax+symbol+123]'
symbol = new SymbolResult(address, symbolText);
return true;
2018-09-07 19:09:29 +00:00
}
symbol = default;
return false;
}
}
public static void Example() {
var symbols = new Dictionary<ulong, string> {
{ 0x5AA55AA5UL, "my_data" },
};
var symbolResolver = new SymbolResolver(symbols);
var decoder = Decoder.Create(64, new ByteArrayCodeReader("488B8AA55AA55A"));
decoder.Decode(out var instr);
var formatter = new GasFormatter(symbolResolver);
var output = new StringOutput();
formatter.Format(instr, output);
// Prints: mov my_data(%rdx),%rcx
Console.WriteLine(output.ToStringAndReset());
}
}
```
## Disassemble with colorized text
```C#
using System;
using System.Collections.Generic;
using Iced.Intel;
2018-09-07 19:09:29 +00:00
static class HowTo_ColorizedText {
public static void Example() {
var codeReader = new ByteArrayCodeReader(exampleCode);
var decoder = Decoder.Create(exampleCodeBitness, codeReader);
decoder.IP = exampleCodeRIP;
var formatter = new MasmFormatter();
var output = new FormatterOutputImpl();
while (codeReader.CanReadByte) {
decoder.Decode(out var instr);
output.List.Clear();
formatter.Format(instr, output);
foreach (var (text, kind) in output.List) {
Console.ForegroundColor = GetColor(kind);
Console.Write(text);
2018-09-07 19:09:29 +00:00
}
Console.WriteLine();
2018-09-07 19:09:29 +00:00
}
Console.ResetColor();
}
sealed class FormatterOutputImpl : FormatterOutput {
2020-01-25 17:12:09 +00:00
public readonly List<(string text, FormatterTextKind kind)> List =
new List<(string text, FormatterTextKind kind)>();
public override void Write(string text, FormatterTextKind kind) => List.Add((text, kind));
}
static ConsoleColor GetColor(FormatterTextKind kind) {
switch (kind) {
case FormatterTextKind.Directive:
case FormatterTextKind.Keyword:
return ConsoleColor.Yellow;
case FormatterTextKind.Prefix:
case FormatterTextKind.Mnemonic:
return ConsoleColor.Red;
case FormatterTextKind.Register:
return ConsoleColor.Magenta;
case FormatterTextKind.Number:
return ConsoleColor.Green;
default:
return ConsoleColor.White;
2018-09-07 19:09:29 +00:00
}
}
2018-09-07 19:09:29 +00:00
const int exampleCodeBitness = 64;
const ulong exampleCodeRIP = 0x00007FFAC46ACDA4;
static readonly byte[] exampleCode = new byte[] {
0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D,
0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05,
0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B,
0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF
};
}
```
## Move code in memory (eg. hook a function)
```C#
using System;
using System.Collections.Generic;
2020-01-25 17:12:09 +00:00
using System.Diagnostics;
using Iced.Intel;
2019-02-18 19:46:34 +00:00
static class HowTo_MoveCode {
// Decodes instructions from some address, then encodes them starting at some
// other address. This can be used to hook a function. You decode enough instructions
// until you have enough bytes to add a JMP instruction that jumps to your code.
// Your code will then conditionally jump to the original code that you re-encoded.
//
// This code uses the BlockEncoder which will help with some things, eg. converting
// short branches to longer branches if the target is too far away.
//
// 64-bit mode also supports RIP relative addressing, but the encoder can't rewrite
// those to use a longer displacement. If any of the moved instructions have RIP
// relative addressing and it tries to access data too far away, the encoder will fail.
// The easiest solution is to use OS alloc functions that allocate memory close to the
// original code (+/-2GB).
2020-01-25 17:12:09 +00:00
/*
* This method produces the following output:
Original code:
00007FFAC46ACDA4 mov [rsp+10h],rbx
00007FFAC46ACDA9 mov [rsp+18h],rsi
00007FFAC46ACDAE push rbp
00007FFAC46ACDAF push rdi
00007FFAC46ACDB0 push r14
00007FFAC46ACDB2 lea rbp,[rsp-100h]
00007FFAC46ACDBA sub rsp,200h
00007FFAC46ACDC1 mov rax,[rel 7FFAC47524E0h]
00007FFAC46ACDC8 xor rax,rsp
00007FFAC46ACDCB mov [rbp+0F0h],rax
00007FFAC46ACDD2 mov r8,[rel 7FFAC474F208h]
00007FFAC46ACDD9 lea rax,[rel 7FFAC46F4A58h]
00007FFAC46ACDE0 xor edi,edi
Original + patched code:
00007FFAC46ACDA4 mov rax,123456789ABCDEF0h
00007FFAC46ACDAE jmp rax
00007FFAC46ACDB0 push r14
00007FFAC46ACDB2 lea rbp,[rsp-100h]
00007FFAC46ACDBA sub rsp,200h
00007FFAC46ACDC1 mov rax,[rel 7FFAC47524E0h]
00007FFAC46ACDC8 xor rax,rsp
00007FFAC46ACDCB mov [rbp+0F0h],rax
00007FFAC46ACDD2 mov r8,[rel 7FFAC474F208h]
00007FFAC46ACDD9 lea rax,[rel 7FFAC46F4A58h]
00007FFAC46ACDE0 xor edi,edi
Moved code:
00007FFAC48ACDA4 mov [rsp+10h],rbx
00007FFAC48ACDA9 mov [rsp+18h],rsi
00007FFAC48ACDAE push rbp
00007FFAC48ACDAF push rdi
00007FFAC48ACDB0 jmp 00007FFAC46ACDB0h
*/
public static void Example() {
2020-01-25 17:12:09 +00:00
Console.WriteLine("Original code:");
Disassemble(exampleCode, exampleCodeRIP);
var codeReader = new ByteArrayCodeReader(exampleCode);
var decoder = Decoder.Create(exampleCodeBitness, codeReader);
decoder.IP = exampleCodeRIP;
2020-01-25 17:12:09 +00:00
// In 64-bit mode, we need 12 bytes to jump to any address:
// mov rax,imm64 // 10
// jmp rax // 2
// We overwrite rax because it's probably not used by the called function.
// In 32-bit mode, a normal JMP is just 5 bytes
const uint requiredBytes = 10 + 2;
uint totalBytes = 0;
var origInstructions = new InstructionList();
while (codeReader.CanReadByte) {
decoder.Decode(out var instr);
origInstructions.Add(instr);
totalBytes += (uint)instr.Length;
if (instr.Code == Code.INVALID)
throw new Exception("Found garbage");
if (totalBytes >= requiredBytes)
break;
switch (instr.FlowControl) {
case FlowControl.Next:
break;
case FlowControl.UnconditionalBranch:
if (instr.Op0Kind == OpKind.NearBranch64) {
var target = instr.NearBranchTarget;
// You could check if it's just jumping forward a few bytes and follow it
// but this is a simple example so we'll fail.
}
goto default;
case FlowControl.IndirectBranch:// eg. jmp reg/mem
case FlowControl.ConditionalBranch:// eg. je, jno, etc
case FlowControl.Return:// eg. ret
case FlowControl.Call:// eg. call method
case FlowControl.IndirectCall:// eg. call reg/mem
case FlowControl.Interrupt:// eg. int n
case FlowControl.XbeginXabortXend:
case FlowControl.Exception:// eg. ud0
default:
throw new Exception("Not supported by this simple example");
}
}
if (totalBytes < requiredBytes)
throw new Exception("Not enough bytes!");
Debug.Assert(origInstructions.Count > 0);
// Create a JMP instruction that branches to the original code, except those instructions
// that we we'll re-encode. We don't need to do it if it already ends in 'ret'
ref readonly var lastInstr = ref origInstructions[origInstructions.Count - 1];
if (lastInstr.FlowControl != FlowControl.Return)
origInstructions.Add(Instruction.CreateBranch(Code.Jmp_rel32_64, lastInstr.NextIP));
// Relocate the code to some new location. It can fix short/near branches and
// convert them to short/near/long forms if needed. This also works even if it's a
// jrcxz/loop/loopcc instruction which only has a short form.
//
// It can currently only fix RIP relative operands if the new location is within 2GB
// of the target data location.
//
// Note that a block is not the same thing as a basic block. A block can contain any
// number of instructions, including any number of branch instructions. One block
// should be enough unless you must relocate different blocks to different locations.
var codeWriter = new CodeWriterImpl();
ulong relocatedBaseAddress = exampleCodeRIP + 0x200000;
2020-01-25 17:12:09 +00:00
var block = new InstructionBlock(codeWriter, origInstructions, relocatedBaseAddress);
// This method can also encode more than one block but that's rarely needed, see above comment.
bool success = BlockEncoder.TryEncode(decoder.Bitness, block, out var errorMessage, out _);
if (!success) {
Console.WriteLine($"ERROR: {errorMessage}");
return;
}
var newCode = codeWriter.ToArray();
2020-01-25 17:12:09 +00:00
// Patch the original code. Pretend that we use some OS API to write to memory...
// We could use the BlockEncoder/Encoder for this but it's easy to do yourself too.
// This is 'mov rax,imm64; jmp rax'
const ulong YOUR_FUNC = 0x123456789ABCDEF0;// Address of your code
exampleCode[0] = 0x48;// \ 'MOV RAX,imm64'
exampleCode[1] = 0xB8;// /
ulong v = YOUR_FUNC;
for (int i = 0; i < 8; i++, v >>= 8)
exampleCode[2 + i] = (byte)v;
exampleCode[10] = 0xFF;// \ JMP RAX
exampleCode[11] = 0xE0;// /
// Disassemble it
Console.WriteLine("Original + patched code:");
var formatter = new NasmFormatter();
var output = new StringOutput();
codeReader = new ByteArrayCodeReader(exampleCode);
decoder = Decoder.Create(exampleCodeBitness, codeReader);
decoder.IP = exampleCodeRIP;
while (codeReader.CanReadByte) {
Instruction instr;
if (decoder.IP == exampleCodeRIP + requiredBytes && lastInstr.NextIP - decoder.IP != 0) {
// The instruction was partially overwritten, so just show it as a 'db x,y,z' instead of garbage
var len = (int)(lastInstr.NextIP - decoder.IP);
var index = (int)(decoder.IP - exampleCodeRIP);
instr = Instruction.CreateDeclareByte(exampleCode, index, len);
instr.NextIP = decoder.IP;
for (int i = 0; i < len; i++)
codeReader.ReadByte();
decoder.IP += (ulong)len;
2019-02-18 19:46:34 +00:00
}
2020-01-25 17:12:09 +00:00
else
instr = decoder.Decode();
formatter.Format(instr, output);
Console.WriteLine($"{instr.IP:X16} {output.ToStringAndReset()}");
2019-02-18 19:46:34 +00:00
}
2020-01-25 17:12:09 +00:00
Console.WriteLine();
2019-02-18 19:46:34 +00:00
2020-01-25 17:12:09 +00:00
// Disassemble the moved code
Console.WriteLine("Moved code:");
Disassemble(newCode, relocatedBaseAddress);
}
static void Disassemble(byte[] data, ulong ip) {
var formatter = new NasmFormatter();
var output = new StringOutput();
2020-01-25 17:12:09 +00:00
var codeReader = new ByteArrayCodeReader(data);
var decoder = Decoder.Create(exampleCodeBitness, codeReader);
decoder.IP = ip;
while (codeReader.CanReadByte) {
decoder.Decode(out var instr);
formatter.Format(instr, output);
Console.WriteLine($"{instr.IP:X16} {output.ToStringAndReset()}");
}
2020-01-25 17:12:09 +00:00
Console.WriteLine();
}
sealed class CodeWriterImpl : CodeWriter {
readonly List<byte> allBytes = new List<byte>();
public override void WriteByte(byte value) => allBytes.Add(value);
public byte[] ToArray() => allBytes.ToArray();
}
const int exampleCodeBitness = 64;
const ulong exampleCodeRIP = 0x00007FFAC46ACDA4;
static readonly byte[] exampleCode = new byte[] {
0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D,
0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05,
0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B,
0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF
};
}
```
## Get instruction info, eg. read/written regs/mem, control flow info, etc
```C#
using System;
using Iced.Intel;
static class HowTo_InstructionInfo {
/*
* This method produces the following output:
2018-09-07 19:09:29 +00:00
00007FFAC46ACDA4 mov [rsp+10h],rbx
2019-08-22 19:44:40 +00:00
OpCode: REX.W 89 /r
2019-09-12 21:06:08 +00:00
Instruction: MOV r/m64, r64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Mov
2019-07-18 19:58:13 +00:00
Code: Mov_rm64_r64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 4, size = 1
2019-02-12 21:02:42 +00:00
Memory size: 8
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: Read
2019-09-01 17:07:36 +00:00
Op0: r64_or_mem
2019-08-22 19:44:40 +00:00
Op1: r64_reg
2020-01-25 17:12:09 +00:00
Used reg: RSP:Read
Used reg: RBX:Read
Used mem: [SS:RSP+0x10;UInt64;Write]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDA9 mov [rsp+18h],rsi
2019-08-22 19:44:40 +00:00
OpCode: REX.W 89 /r
2019-09-12 21:06:08 +00:00
Instruction: MOV r/m64, r64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Mov
2019-07-18 19:58:13 +00:00
Code: Mov_rm64_r64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 4, size = 1
2019-02-12 21:02:42 +00:00
Memory size: 8
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: Read
2019-09-01 17:07:36 +00:00
Op0: r64_or_mem
2019-08-22 19:44:40 +00:00
Op1: r64_reg
2020-01-25 17:12:09 +00:00
Used reg: RSP:Read
Used reg: RSI:Read
Used mem: [SS:RSP+0x18;UInt64;Write]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDAE push rbp
2019-08-22 19:44:40 +00:00
OpCode: 50+ro
2019-09-12 21:06:08 +00:00
Instruction: PUSH r64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Push
2019-07-18 19:58:13 +00:00
Code: Push_r64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
SP Increment: -8
Op0Access: Read
2019-08-22 19:44:40 +00:00
Op0: r64_opcode
2020-01-25 17:12:09 +00:00
Used reg: RBP:Read
Used reg: RSP:ReadWrite
Used mem: [SS:RSP+0xFFFFFFFFFFFFFFF8;UInt64;Write]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDAF push rdi
2019-08-22 19:44:40 +00:00
OpCode: 50+ro
2019-09-12 21:06:08 +00:00
Instruction: PUSH r64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Push
2019-07-18 19:58:13 +00:00
Code: Push_r64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
SP Increment: -8
Op0Access: Read
2019-08-22 19:44:40 +00:00
Op0: r64_opcode
2020-01-25 17:12:09 +00:00
Used reg: RDI:Read
Used reg: RSP:ReadWrite
Used mem: [SS:RSP+0xFFFFFFFFFFFFFFF8;UInt64;Write]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDB0 push r14
2019-08-22 19:44:40 +00:00
OpCode: 50+ro
2019-09-12 21:06:08 +00:00
Instruction: PUSH r64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Push
2019-07-18 19:58:13 +00:00
Code: Push_r64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
SP Increment: -8
Op0Access: Read
2019-08-22 19:44:40 +00:00
Op0: r64_opcode
2020-01-25 17:12:09 +00:00
Used reg: R14:Read
Used reg: RSP:ReadWrite
Used mem: [SS:RSP+0xFFFFFFFFFFFFFFF8;UInt64;Write]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDB2 lea rbp,[rsp-100h]
2019-08-22 19:44:40 +00:00
OpCode: REX.W 8D /r
2019-09-12 21:06:08 +00:00
Instruction: LEA r64, m
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Lea
2019-07-18 19:58:13 +00:00
Code: Lea_r64_m
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 4, size = 4
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: NoMemAccess
2019-08-22 19:44:40 +00:00
Op0: r64_reg
Op1: mem
2020-01-25 17:12:09 +00:00
Used reg: RBP:Write
Used reg: RSP:Read
2018-09-07 19:09:29 +00:00
00007FFAC46ACDBA sub rsp,200h
2019-08-22 19:44:40 +00:00
OpCode: REX.W 81 /5 id
2019-09-12 21:06:08 +00:00
Instruction: SUB r/m64, imm32
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Sub
2019-07-18 19:58:13 +00:00
Code: Sub_rm64_imm32
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Immediate offset = 3, size = 4
2018-09-07 19:09:29 +00:00
RFLAGS Written: OF, SF, ZF, AF, CF, PF
RFLAGS Modified: OF, SF, ZF, AF, CF, PF
Op0Access: ReadWrite
Op1Access: Read
2019-09-01 17:07:36 +00:00
Op0: r64_or_mem
2019-08-22 19:44:40 +00:00
Op1: imm32sex64
2020-01-25 17:12:09 +00:00
Used reg: RSP:ReadWrite
2018-09-07 19:09:29 +00:00
00007FFAC46ACDC1 mov rax,[7FFAC47524E0h]
2019-08-22 19:44:40 +00:00
OpCode: REX.W 8B /r
2019-09-12 21:06:08 +00:00
Instruction: MOV r64, r/m64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Mov
2019-07-18 19:58:13 +00:00
Code: Mov_r64_rm64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 3, size = 4
2019-02-12 21:02:42 +00:00
Memory size: 8
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: Read
2019-08-22 19:44:40 +00:00
Op0: r64_reg
2019-09-01 17:07:36 +00:00
Op1: r64_or_mem
2020-01-25 17:12:09 +00:00
Used reg: RAX:Write
Used mem: [DS:0x7FFAC47524E0;UInt64;Read]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDC8 xor rax,rsp
2019-08-22 19:44:40 +00:00
OpCode: REX.W 33 /r
2019-09-12 21:06:08 +00:00
Instruction: XOR r64, r/m64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Xor
2019-07-18 19:58:13 +00:00
Code: Xor_r64_rm64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
RFLAGS Written: SF, ZF, PF
RFLAGS Cleared: OF, CF
RFLAGS Undefined: AF
RFLAGS Modified: OF, SF, ZF, AF, CF, PF
Op0Access: ReadWrite
Op1Access: Read
2019-08-22 19:44:40 +00:00
Op0: r64_reg
2019-09-01 17:07:36 +00:00
Op1: r64_or_mem
2020-01-25 17:12:09 +00:00
Used reg: RAX:ReadWrite
Used reg: RSP:Read
2018-09-07 19:09:29 +00:00
00007FFAC46ACDCB mov [rbp+0F0h],rax
2019-08-22 19:44:40 +00:00
OpCode: REX.W 89 /r
2019-09-12 21:06:08 +00:00
Instruction: MOV r/m64, r64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Mov
2019-07-18 19:58:13 +00:00
Code: Mov_rm64_r64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 3, size = 4
2019-02-12 21:02:42 +00:00
Memory size: 8
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: Read
2019-09-01 17:07:36 +00:00
Op0: r64_or_mem
2019-08-22 19:44:40 +00:00
Op1: r64_reg
2020-01-25 17:12:09 +00:00
Used reg: RBP:Read
Used reg: RAX:Read
Used mem: [SS:RBP+0xF0;UInt64;Write]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDD2 mov r8,[7FFAC474F208h]
2019-08-22 19:44:40 +00:00
OpCode: REX.W 8B /r
2019-09-12 21:06:08 +00:00
Instruction: MOV r64, r/m64
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Mov
2019-07-18 19:58:13 +00:00
Code: Mov_r64_rm64
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 3, size = 4
2019-02-12 21:02:42 +00:00
Memory size: 8
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: Read
2019-08-22 19:44:40 +00:00
Op0: r64_reg
2019-09-01 17:07:36 +00:00
Op1: r64_or_mem
2020-01-25 17:12:09 +00:00
Used reg: R8:Write
Used mem: [DS:0x7FFAC474F208;UInt64;Read]
2018-09-07 19:09:29 +00:00
00007FFAC46ACDD9 lea rax,[7FFAC46F4A58h]
2019-08-22 19:44:40 +00:00
OpCode: REX.W 8D /r
2019-09-12 21:06:08 +00:00
Instruction: LEA r64, m
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Lea
2019-07-18 19:58:13 +00:00
Code: Lea_r64_m
2019-02-06 18:08:19 +00:00
CpuidFeature: X64
2018-09-07 19:09:29 +00:00
FlowControl: Next
2019-02-03 15:25:26 +00:00
Displacement offset = 3, size = 4
2018-09-07 19:09:29 +00:00
Op0Access: Write
Op1Access: NoMemAccess
2019-08-22 19:44:40 +00:00
Op0: r64_reg
Op1: mem
2020-01-25 17:12:09 +00:00
Used reg: RAX:Write
2018-09-07 19:09:29 +00:00
00007FFAC46ACDE0 xor edi,edi
2019-08-22 19:44:40 +00:00
OpCode: o32 33 /r
2019-09-12 21:06:08 +00:00
Instruction: XOR r32, r/m32
2018-09-07 19:09:29 +00:00
Encoding: Legacy
2019-05-22 16:57:52 +00:00
Mnemonic: Xor
2019-07-18 19:58:13 +00:00
Code: Xor_r32_rm32
2019-02-06 18:08:19 +00:00
CpuidFeature: INTEL386
2018-09-07 19:09:29 +00:00
FlowControl: Next
RFLAGS Cleared: OF, SF, CF
RFLAGS Set: ZF, PF
2018-09-07 19:09:29 +00:00
RFLAGS Undefined: AF
RFLAGS Modified: OF, SF, ZF, AF, CF, PF
Op0Access: Write
Op1Access: None
2019-08-22 19:44:40 +00:00
Op0: r32_reg
2019-09-01 17:07:36 +00:00
Op1: r32_or_mem
2020-01-25 17:12:09 +00:00
Used reg: RDI:Write
*/
public static void Example() {
var codeReader = new ByteArrayCodeReader(exampleCode);
var decoder = Decoder.Create(exampleCodeBitness, codeReader);
decoder.IP = exampleCodeRIP;
// Use a factory to create the instruction info if you need register and
// memory usage. If it's something else, eg. encoding, flags, etc, there
// are properties on Instruction that can be used instead.
var instrInfoFactory = new InstructionInfoFactory();
while (codeReader.CanReadByte) {
decoder.Decode(out var instr);
// Gets offsets in the instruction of the displacement and immediates and their sizes.
// This can be useful if there are relocations in the binary. The encoder has a similar
// method. This method must be called after Decode() and you must pass in the last
// instruction Decode() returned.
var offsets = decoder.GetConstantOffsets(instr);
Console.WriteLine($"{instr.IP:X16} {instr}");
var opCode = instr.OpCode;
var info = instrInfoFactory.GetInfo(instr);
const string tab = " ";
Console.WriteLine($"{tab}OpCode: {opCode.ToOpCodeString()}");
Console.WriteLine($"{tab}Instruction: {opCode.ToInstructionString()}");
Console.WriteLine($"{tab}Encoding: {instr.Encoding}");
Console.WriteLine($"{tab}Mnemonic: {instr.Mnemonic}");
Console.WriteLine($"{tab}Code: {instr.Code}");
Console.WriteLine($"{tab}CpuidFeature: {string.Join(" and ", instr.CpuidFeatures)}");
Console.WriteLine($"{tab}FlowControl: {instr.FlowControl}");
if (offsets.HasDisplacement)
Console.WriteLine($"{tab}Displacement offset = {offsets.DisplacementOffset}, size = {offsets.DisplacementSize}");
if (offsets.HasImmediate)
Console.WriteLine($"{tab}Immediate offset = {offsets.ImmediateOffset}, size = {offsets.ImmediateSize}");
if (offsets.HasImmediate2)
Console.WriteLine($"{tab}Immediate #2 offset = {offsets.ImmediateOffset2}, size = {offsets.ImmediateSize2}");
if (instr.IsStackInstruction)
Console.WriteLine($"{tab}SP Increment: {instr.StackPointerIncrement}");
if (instr.ConditionCode != ConditionCode.None)
Console.WriteLine($"{tab}Condition code: {instr.ConditionCode}");
if (instr.RflagsRead != RflagsBits.None)
Console.WriteLine($"{tab}RFLAGS Read: {instr.RflagsRead}");
if (instr.RflagsWritten != RflagsBits.None)
Console.WriteLine($"{tab}RFLAGS Written: {instr.RflagsWritten}");
if (instr.RflagsCleared != RflagsBits.None)
Console.WriteLine($"{tab}RFLAGS Cleared: {instr.RflagsCleared}");
if (instr.RflagsSet != RflagsBits.None)
Console.WriteLine($"{tab}RFLAGS Set: {instr.RflagsSet}");
if (instr.RflagsUndefined != RflagsBits.None)
Console.WriteLine($"{tab}RFLAGS Undefined: {instr.RflagsUndefined}");
if (instr.RflagsModified != RflagsBits.None)
Console.WriteLine($"{tab}RFLAGS Modified: {instr.RflagsModified}");
for (int i = 0; i < instr.OpCount; i++) {
var opKind = instr.GetOpKind(i);
if (opKind == OpKind.Memory || opKind == OpKind.Memory64) {
int size = instr.MemorySize.GetSize();
if (size != 0)
Console.WriteLine($"{tab}Memory size: {size}");
break;
2019-02-18 19:46:34 +00:00
}
2018-09-07 19:09:29 +00:00
}
for (int i = 0; i < instr.OpCount; i++)
Console.WriteLine($"{tab}Op{i}Access: {info.GetOpAccess(i)}");
for (int i = 0; i < opCode.OpCount; i++)
Console.WriteLine($"{tab}Op{i}: {opCode.GetOpKind(i)}");
// The returned iterator is a struct, nothing is allocated unless you box it
foreach (var regInfo in info.GetUsedRegisters())
2020-01-25 17:12:09 +00:00
Console.WriteLine($"{tab}Used reg: {regInfo.ToString()}");
foreach (var memInfo in info.GetUsedMemory())
2020-01-25 17:12:09 +00:00
Console.WriteLine($"{tab}Used mem: {memInfo.ToString()}");
2018-09-07 19:09:29 +00:00
}
}
const int exampleCodeBitness = 64;
const ulong exampleCodeRIP = 0x00007FFAC46ACDA4;
static readonly byte[] exampleCode = new byte[] {
0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D,
0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05,
0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B,
0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF
};
}
```
2018-09-07 19:33:05 +00:00
# License
2018-09-05 23:29:23 +00:00
MIT
2018-09-21 19:30:08 +00:00
2018-12-26 15:00:56 +00:00
# Icon
2018-09-21 19:30:08 +00:00
Logo `processor` by [Creative Stall](https://thenounproject.com/creativestall/) from the Noun Project