Skip to content

Commit

Permalink
Merge pull request 9ee1#8 from uxmal/master
Browse files Browse the repository at this point in the history
Implementation of "lazy" IEnumerable / IEnumerator for the Disassembler<,,,,> class
  • Loading branch information
9ee1 committed Aug 11, 2015
2 parents 40a292f + e41ce44 commit f517f67
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
**/bin
**/obj
packages/
/*.suo
7 changes: 7 additions & 0 deletions CapstoneCMD/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,14 @@ internal static void ShowX86() {
//
// ...
var code = new byte[] {0x8d, 0x4c, 0x32, 0x08, 0x01, 0xd8, 0x81, 0xc6, 0x34, 0x12, 0x00, 0x00, 0x05, 0x23, 0x01, 0x00, 0x00, 0x36, 0x8b, 0x84, 0x91, 0x23, 0x01, 0x00, 0x00, 0x41, 0x8d, 0x84, 0x39, 0x89, 0x67, 0x00, 0x00, 0x8d, 0x87, 0x89, 0x67, 0x00, 0x00, 0xb4, 0xc6};
#if DISASSEMBLE_STREAM
//$REVIEW: uxmal: This exercises the lazy stream implementation of the disassembler.
// It isn't greed and tries to disassembly all the instructions at once,
// but only on demand.
var instructions = disassembler.DisassembleStream(code, 0, 0x1000);
#else
var instructions = disassembler.DisassembleAll(code);
#endif

var hexCode = BitConverter.ToString(code).Replace("-", " ");
Console.WriteLine(hexCode);
Expand Down
162 changes: 161 additions & 1 deletion Gee.External.Capstone/CapstoneDisassembler.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Gee.External.Capstone.Arm;
using Gee.External.Capstone.Arm64;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Gee.External.Capstone.X86;
using System.Collections;

namespace Gee.External.Capstone {
/// <summary>
Expand Down Expand Up @@ -201,7 +204,7 @@ public abstract class CapstoneDisassembler<TArchitectureInstruction, TArchitectu
/// <param name="mode">
/// The disassembler's mode.
/// </param>
protected CapstoneDisassembler(DisassembleArchitecture architecture, DisassembleMode mode) : base(architecture, mode) {}
protected CapstoneDisassembler(DisassembleArchitecture architecture, DisassembleMode mode) : base(architecture, mode) { }

/// <summary>
/// Disassemble Binary Code.
Expand Down Expand Up @@ -271,6 +274,22 @@ public Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectur
return instructions;
}

/// <summary>
/// Disassemble Binary Code incrementally.
/// </summary>
/// <param name="code">
/// A collection of bytes representing the binary code to disassemble. Should not be a null reference.
/// </param>
/// <returns>
/// An IEnumerable deferred stream of dissembled instructions.
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// Thrown if the binary code could not be disassembled.
/// </exception>
public IEnumerable<Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail>> DisassembleStream(byte[] code, int offset, long startAddress) {
return new InstructionStream(this, code, offset, startAddress);
}

/// <summary>
/// Disassemble Binary Code.
/// </summary>
Expand Down Expand Up @@ -298,5 +317,146 @@ public Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectur
/// A dissembled instruction.
/// </returns>
protected abstract Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> CreateInstruction(NativeInstruction nativeInstruction);

/// <summary>
/// Provides an IEnumerable interface so that Capstone.NET can be used with
/// other .NET components, like LinQ, without having to eagerly disassemble
/// large areas of memory.
/// </summary>
private class InstructionStream :
IEnumerable<Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail>> {
private CapstoneDisassembler<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> dasm;
private byte[] code;
private int offset;
private long address;

public InstructionStream(
CapstoneDisassembler<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> dasm,
byte[] code,
int offset,
long address)
{
this.dasm = dasm;
this.code = code;
this.offset = offset;
this.address = address;
}

public IEnumerator<Instruction<TArchitectureInstruction,TArchitectureRegister,TArchitectureGroup,TArchitectureDetail>> GetEnumerator()
{
return new InstructionEnumerator(dasm, code, offset, address);
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

/// <summary>
/// Enumerator that lazily disassembles a stream of instructions.
/// </summary>
private class InstructionEnumerator :
IEnumerator<Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail>> {
private CapstoneDisassembler<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> dasm;
private GCHandle pinnedCode; // Pinned array of bytes containing the area we wish to disassemble.
private int offset; // "cursor": the position within the code we're currently at.
private int codeSize; // the size of the code.
private Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> current;
private ulong address;

public InstructionEnumerator(
CapstoneDisassembler<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> dasm,
byte[] code,
int offset,
long startAddress) {
this.dasm = dasm;
// Avoid copying the code to be disassembled, by pinning it instead.
this.pinnedCode = GCHandle.Alloc(code, GCHandleType.Pinned);
this.offset = offset;
this.codeSize = code.Length;
this.address = (ulong)startAddress;
}

/// <summary>
/// Finalize the enumerator.
/// </summary>
/// <remarks>
/// The finalizer will be called at some non-specified time
/// if you forget to Dispose() this object. Avoid this by being
/// diligent with your resource management.
/// </remarks>
~InstructionEnumerator() {
Dispose(false);
}

/// <summary>
/// Gets the current instruction in the instruction stream.
/// </summary>
/// <exception cref="System.InvalidOperationException">
/// Thrown if the caller attempts to read this property before
/// calling the MoveNext() method.
/// </exception>
public Instruction<TArchitectureInstruction, TArchitectureRegister, TArchitectureGroup, TArchitectureDetail> Current {
get {
if (current == null)
throw new InvalidOperationException();
return current;
}
}

object IEnumerator.Current {
get { return Current; }
}

/// <summary>
/// Dispose() method is called automatically by foreach
/// </summary>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Disposes of any unmanaged/unsafe resources.
/// </summary>
/// <param name="disposing"></param>
private void Dispose(bool disposing) {
if (pinnedCode.IsAllocated)
pinnedCode.Free();
}

/// <summary>
/// Retrieve the next instruction using the native Capstone disassembler.
/// </summary>
/// <returns></returns>
public bool MoveNext() {
var pCount = (IntPtr)1;
var pCode = this.pinnedCode.AddrOfPinnedObject() + offset;
var pInstructions = IntPtr.Zero;
var pSize = (IntPtr)codeSize - offset;
var uStartingAddress = (ulong)address;

var pResultCode = CapstoneImport.Disassemble(dasm.Handle.DangerousGetHandle(), pCode, pSize, uStartingAddress, pCode, ref pInstructions);

var iResultCode = (int)pResultCode;
var instructions = MarshalExtension.PtrToStructure<NativeInstruction>(pInstructions, iResultCode);
if (instructions == null || instructions.Length == 0)
return false;

// Update the state of the IEnumerator.
this.current = dasm.CreateInstruction(instructions[0]);
this.address += (ulong) current.Bytes.Length;
this.offset += current.Bytes.Length;
return true;
}

/// <summary>
/// The Reset method is not supported; it is rarely used in practice.
/// </summary>
public void Reset() {
throw new NotSupportedException();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
<Name>Gee.External.Capstone</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down

0 comments on commit f517f67

Please sign in to comment.