Last active
March 19, 2016 16:42
-
-
Save Const-me/c69cce18dd6bf1c81f89 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Gee.External.Capstone; | |
using Gee.External.Capstone.X86; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace InstructionsCheck | |
{ | |
/// <summary>This class implements a way to disassemble real-life modules with Capstone.</summary> | |
/// <remarks>Real-life x86 and amd64 code contains inline data. | |
/// And real-life data can't be disassembled, it causes Capstone to stop disassembling and fail. | |
/// This static class implements some heuristics to resume disassembly after that happens.</remarks> | |
static class DisasmEx | |
{ | |
/// <summary>return a portion of the byte array.</summary> | |
static IEnumerable<byte> take( this byte[] arr, int offset, int c ) | |
{ | |
int end = Math.Min( arr.Length, offset + c ); | |
for( int i = offset; i < end; i++ ) | |
yield return arr[ i ]; | |
} | |
/// <summary>True if the code from the offset is equal to the marker.</summary> | |
static bool isMarkerAt( this byte[] code, byte[] marker, int offset ) | |
{ | |
return code.take( offset, marker.Length ).SequenceEqual( marker ); | |
} | |
/// <summary>Create byte array from hex string</summary> | |
public static byte[] parseBytes( string hex ) | |
{ | |
hex = hex.Trim(); | |
// http://stackoverflow.com/a/321404/126995 | |
return Enumerable.Range( 0, hex.Length ) | |
.Where( x => x % 2 == 0 ) | |
.Select( x => Convert.ToByte( hex.Substring( x, 2 ), 16 ) ) | |
.ToArray(); | |
} | |
/// <summary>Return possible x86 NOP commands, and also int3 command.</summary> | |
static IEnumerable<string> allNOPs() | |
{ | |
yield return "CC"; // int 3: not nop, but we still skip it. | |
// http://stackoverflow.com/q/25545470/126995 | |
yield return "90 "; | |
yield return "6690 "; | |
yield return "0f1f00 "; | |
yield return "0f1f4000 "; | |
// AMD | |
yield return "0f1f440000 "; | |
yield return "660f1f440000 "; | |
yield return "0f1f8000000000 "; | |
yield return "0f1f840000000000 "; | |
yield return "660f1f840000000000 "; | |
yield return "66660f1f840000000000 "; | |
yield return "6666660f1f840000000000 "; | |
// Intel | |
yield return "0f1f440000 "; | |
yield return "660f1f440000 "; | |
yield return "0f1f8000000000 "; | |
yield return "0f1f840000000000 "; | |
yield return "660f1f840000000000 "; | |
yield return "0f1f440000 "; | |
yield return "660f1f440000 "; | |
yield return "0f1f8000000000 "; | |
yield return "0f1f840000000000 "; | |
yield return "660f1f840000000000 "; | |
yield return "662e0f1f840000000000 "; | |
yield return "0f1f440000660f1f440000 "; | |
yield return "660f1f440000660f1f440000 "; | |
yield return "660f1f4400000f1f8000000000 "; | |
yield return "0f1f80000000000f1f8000000000 "; | |
yield return "0f1f80000000000f1f840000000000 "; | |
yield return "66662e0f1f840000000000 "; | |
yield return "6666662e0f1f840000000000 "; | |
yield return "666666662e0f1f840000000000 "; | |
yield return "66666666662e0f1f840000000000 "; | |
yield return "6666666666662e0f1f840000000000 "; | |
} | |
const int minStopLength = 5; | |
static IEnumerable<byte[]> createStopMarkers() | |
{ | |
byte[] int3 = Enumerable.Repeat( (byte)0xCC, minStopLength ).ToArray(); | |
return allNOPs() | |
.Distinct() | |
.Select( parseBytes ) | |
.Where( n => n.Length >= minStopLength ) | |
.Concat( Enumerable.Repeat( int3, 1 ) ); | |
} | |
static IEnumerable<byte[]> createNOPs() | |
{ | |
return allNOPs().Distinct() | |
.Select( parseBytes ) | |
.OrderByDescending( an => an.Length ); | |
} | |
static readonly byte[][] stopMarkers = createStopMarkers().ToArray(); | |
static readonly byte[][] nops = createNOPs().ToArray(); | |
static int skipNOPs( byte[] code, int offset ) | |
{ | |
while( true ) | |
{ | |
bool found = false; | |
foreach( byte[] nop in nops ) | |
{ | |
if( code.isMarkerAt( nop, offset ) ) | |
{ | |
offset += nop.Length; | |
found = true; | |
break; | |
} | |
} | |
if( !found ) | |
return offset; | |
} | |
} | |
static int searchNextCode( DisassembleMode mode, byte[] code, int offset ) | |
{ | |
while( offset < code.Length ) | |
{ | |
foreach( byte[] marker in stopMarkers ) | |
{ | |
if( code.isMarkerAt( marker, offset ) ) | |
{ | |
offset += marker.Length; | |
return skipNOPs( code, offset ); | |
} | |
} | |
offset++; | |
} | |
return -1; | |
} | |
/// <summary>Same as CapstoneDisassembler.DisassembleStream but resumes after invalid instructions.</summary> | |
public static IEnumerable<Instruction<X86Instruction, X86Register, X86InstructionGroup, X86InstructionDetail>> | |
disassembleStreamEx( this CapstoneDisassembler<X86Instruction, X86Register, X86InstructionGroup, X86InstructionDetail> disassembler, | |
byte[] code, int offset, long startingAddress ) | |
{ | |
while( true ) | |
{ | |
foreach( var i in disassembler.DisassembleStream( code, offset, startingAddress ) ) | |
{ | |
yield return i; | |
offset += i.Bytes.Length; | |
} | |
int o1 = offset; | |
offset = searchNextCode( disassembler.Mode, code, offset ); | |
if( offset < 0 ) | |
yield break; | |
if( offset < o1 ) | |
throw new ApplicationException(); | |
Console.WriteLine( "; Skipped addresses {0:X}-{1:X}", startingAddress + o1, startingAddress + offset ); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment