mirror of
https://github.com/AdvDebug/Brovan
synced 2026-08-09 11:57:17 +00:00
Better visuals for disassembling and some fixes
1. Added better visuals for x86_64 disassembling in the 'disasm' command. 2. Added the correct RVA value to ImportLookupRVA, i was testing something and forgot to remove the addition of the ImageBase to RVA. 3. Inside IsCorruptedBinary we should check for MemExecute flag, not just ContainsCode.
This commit is contained in:
@@ -2093,7 +2093,7 @@ namespace Brovan.Core
|
||||
LibraryName = DllName,
|
||||
FunctionName = FunctionName,
|
||||
ImportAddressRVA = ImportAddressRVA,
|
||||
ImportLookupRVA = ((uint)PE.ImageBase + (ThunkRva + (uint)(i * PointerSize))),
|
||||
ImportLookupRVA = ((ThunkRva + (uint)(i * PointerSize))),
|
||||
Offset = IATEntryOffset,
|
||||
IsOrdinal = IsOrdinal,
|
||||
Ordinal = IsOrdinal ? (ushort)(ThunkValue & 0xFFFF) : (ushort)0
|
||||
@@ -2527,7 +2527,7 @@ namespace Brovan.Core
|
||||
if (Section.VirtualAddress < 0 || Section.VirtualSize < 0 || Section.RawSize < 0 || Section.RawOffset < 0 || Section.RawOffset > Data.Length)
|
||||
return BinaryCorruptionStatus.Corrupted;
|
||||
|
||||
if (Section.Characteristics.HasFlag(SectionCharacteristics.ContainsCode))
|
||||
if (Section.Characteristics.HasFlag(SectionCharacteristics.ContainsCode) || Section.Characteristics.HasFlag(SectionCharacteristics.MemExecute))
|
||||
{
|
||||
CodeSection = true;
|
||||
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Brovan.Analysis;
|
||||
|
||||
namespace Brovan.EmulationMenu
|
||||
{
|
||||
internal enum AsmTokenKind
|
||||
{
|
||||
Text,
|
||||
Whitespace,
|
||||
Separator,
|
||||
Mnemonic,
|
||||
Register,
|
||||
Immediate,
|
||||
Label,
|
||||
MemoryKeyword,
|
||||
}
|
||||
|
||||
internal readonly record struct AsmToken(string Text, AsmTokenKind Kind);
|
||||
|
||||
internal sealed class AsmLine
|
||||
{
|
||||
public required ulong Address { get; init; }
|
||||
public required string Mnemonic { get; init; }
|
||||
public required IReadOnlyList<AsmToken> OperandTokens { get; init; }
|
||||
public required bool Current { get; init; }
|
||||
public required bool ShowAddress { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class AsmTheme
|
||||
{
|
||||
public ConsoleColor CurrentPrefixColor { get; init; } = ConsoleColor.Yellow;
|
||||
public ConsoleColor NormalPrefixColor { get; init; } = ConsoleColor.DarkGray;
|
||||
public ConsoleColor CurrentAddressColor { get; init; } = ConsoleColor.White;
|
||||
public ConsoleColor NormalAddressColor { get; init; } = ConsoleColor.DarkGray;
|
||||
public ConsoleColor CurrentMnemonicColor { get; init; } = ConsoleColor.White;
|
||||
public ConsoleColor NormalMnemonicColor { get; init; } = ConsoleColor.DarkCyan;
|
||||
public ConsoleColor DefaultOperandColor { get; init; } = ConsoleColor.Gray;
|
||||
public ConsoleColor CurrentOperandColor { get; init; } = ConsoleColor.White;
|
||||
public ConsoleColor RegisterColor { get; init; } = ConsoleColor.Yellow;
|
||||
public ConsoleColor ImmediateColor { get; init; } = ConsoleColor.Magenta;
|
||||
public ConsoleColor LabelColor { get; init; } = ConsoleColor.Green;
|
||||
public ConsoleColor MemoryKeywordColor { get; init; } = ConsoleColor.DarkCyan;
|
||||
public ConsoleColor SeparatorColor { get; init; } = ConsoleColor.DarkGray;
|
||||
|
||||
public static AsmTheme Default { get; } = new();
|
||||
}
|
||||
|
||||
internal static class AsmConsoleFormatter
|
||||
{
|
||||
private static readonly Regex RegisterTokenRegex = new(
|
||||
@"^(?:r(?:1[0-5]|[0-9])[dwb]?|e?[abcd]x|e?[sd]i|e?[sb]p|[abcd][lh]|[sd]p|[sd]i|rip|eip|ip|cs|ds|es|fs|gs|ss|cr[0-9]+|dr[0-9]+|tr[0-9]+|xmm[0-9]+|ymm[0-9]+|zmm[0-9]+|mm[0-9]+|st[0-7])$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private const string OperandSeparators = ",()[]{}+-*:<>";
|
||||
|
||||
public static AsmLine FormatInstruction(X86Instruction instruction, bool current = false, bool showAddress = true)
|
||||
{
|
||||
return new AsmLine
|
||||
{
|
||||
Address = instruction.Address,
|
||||
Mnemonic = instruction.Mnemonic ?? string.Empty,
|
||||
OperandTokens = TokenizeOperand(instruction.Operand ?? string.Empty),
|
||||
Current = current,
|
||||
ShowAddress = showAddress,
|
||||
};
|
||||
}
|
||||
|
||||
public static string FormatInstructionForDebugger(X86Instruction instruction)
|
||||
{
|
||||
string text = instruction.Mnemonic ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(instruction.Operand))
|
||||
text += " " + instruction.Operand;
|
||||
|
||||
return $"0x{instruction.Address:X}: {text}";
|
||||
}
|
||||
|
||||
private static IReadOnlyList<AsmToken> TokenizeOperand(string operand)
|
||||
{
|
||||
if (string.IsNullOrEmpty(operand))
|
||||
return Array.Empty<AsmToken>();
|
||||
|
||||
List<AsmToken> tokens = new();
|
||||
|
||||
for (int i = 0; i < operand.Length;)
|
||||
{
|
||||
char ch = operand[i];
|
||||
|
||||
if (char.IsWhiteSpace(ch))
|
||||
{
|
||||
int start = i;
|
||||
while (i < operand.Length && char.IsWhiteSpace(operand[i]))
|
||||
i++;
|
||||
|
||||
tokens.Add(new AsmToken(operand[start..i], AsmTokenKind.Whitespace));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OperandSeparators.IndexOf(ch) >= 0)
|
||||
{
|
||||
tokens.Add(new AsmToken(ch.ToString(), AsmTokenKind.Separator));
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int tokenStart = i;
|
||||
while (i < operand.Length && !char.IsWhiteSpace(operand[i]) && OperandSeparators.IndexOf(operand[i]) < 0)
|
||||
i++;
|
||||
|
||||
string token = operand[tokenStart..i];
|
||||
tokens.Add(new AsmToken(token, ClassifyToken(token)));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private static AsmTokenKind ClassifyToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return AsmTokenKind.Text;
|
||||
|
||||
if (IsRegisterToken(token))
|
||||
return AsmTokenKind.Register;
|
||||
|
||||
if (IsNumericToken(token))
|
||||
return AsmTokenKind.Immediate;
|
||||
|
||||
if (IsLabelToken(token))
|
||||
return AsmTokenKind.Label;
|
||||
|
||||
if (IsMemoryKeyword(token))
|
||||
return AsmTokenKind.MemoryKeyword;
|
||||
|
||||
return AsmTokenKind.Text;
|
||||
}
|
||||
|
||||
private static bool IsNumericToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return false;
|
||||
|
||||
if (token.Length > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X'))
|
||||
return token.Skip(2).All(Uri.IsHexDigit);
|
||||
|
||||
if (token.EndsWith("h", StringComparison.OrdinalIgnoreCase) && token.Length > 1)
|
||||
return token[..^1].All(Uri.IsHexDigit);
|
||||
|
||||
return token.All(ch => char.IsDigit(ch) || ch == '-' || ch == '+');
|
||||
}
|
||||
|
||||
private static bool IsRegisterToken(string token)
|
||||
{
|
||||
return !string.IsNullOrEmpty(token) && RegisterTokenRegex.IsMatch(token);
|
||||
}
|
||||
|
||||
private static bool IsLabelToken(string token)
|
||||
{
|
||||
return token.StartsWith("loc_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("sub_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("off_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("byte_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("word_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("dword_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("qword_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("unk_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("arg_", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.StartsWith("var_", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsMemoryKeyword(string token)
|
||||
{
|
||||
return token.Equals("ptr", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("byte", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("word", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("dword", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("qword", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("xmmword", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("ymmword", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("zmmword", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("offset", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("short", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("near", StringComparison.OrdinalIgnoreCase)
|
||||
|| token.Equals("far", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class AsmConsoleRenderer
|
||||
{
|
||||
public static void WriteInstruction(X86Instruction instruction, bool current = false, bool showAddress = true)
|
||||
{
|
||||
WriteLine(AsmConsoleFormatter.FormatInstruction(instruction, current, showAddress), AsmTheme.Default);
|
||||
}
|
||||
|
||||
public static void WriteInstructions(X86Instruction[]? instructions, bool highlightFirst = true, bool showAddress = true)
|
||||
{
|
||||
if (instructions == null || instructions.Length == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < instructions.Length; i++)
|
||||
WriteInstruction(instructions[i], highlightFirst && i == 0, showAddress);
|
||||
}
|
||||
|
||||
public static void WriteLine(AsmLine line, AsmTheme? theme = null)
|
||||
{
|
||||
theme ??= AsmTheme.Default;
|
||||
|
||||
if (Console.IsOutputRedirected)
|
||||
{
|
||||
Console.WriteLine(FormatPlainText(line));
|
||||
return;
|
||||
}
|
||||
|
||||
Console.ForegroundColor = line.Current ? theme.CurrentPrefixColor : theme.NormalPrefixColor;
|
||||
Console.Write(line.Current ? "=> " : " ");
|
||||
Console.ResetColor();
|
||||
|
||||
if (line.ShowAddress)
|
||||
{
|
||||
Console.ForegroundColor = line.Current ? theme.CurrentAddressColor : theme.NormalAddressColor;
|
||||
Console.Write($"0x{line.Address:X16}");
|
||||
Console.ResetColor();
|
||||
|
||||
Console.ForegroundColor = theme.SeparatorColor;
|
||||
Console.Write(": ");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
Console.ForegroundColor = line.Current ? theme.CurrentMnemonicColor : theme.NormalMnemonicColor;
|
||||
Console.Write(line.Mnemonic);
|
||||
Console.ResetColor();
|
||||
|
||||
if (line.OperandTokens.Count > 0)
|
||||
Console.Write(' ');
|
||||
|
||||
ConsoleColor defaultOperandColor = line.Current ? theme.CurrentOperandColor : theme.DefaultOperandColor;
|
||||
foreach (AsmToken token in line.OperandTokens)
|
||||
{
|
||||
Console.ForegroundColor = GetTokenColor(token, theme, defaultOperandColor);
|
||||
Console.Write(token.Text);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private static string FormatPlainText(AsmLine line)
|
||||
{
|
||||
string operand = string.Concat(line.OperandTokens.Select(t => t.Text));
|
||||
string text = line.Mnemonic;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(operand))
|
||||
text += " " + operand;
|
||||
|
||||
if (line.ShowAddress)
|
||||
text = $"0x{line.Address:X16}: {text}";
|
||||
|
||||
return line.Current ? $"=> {text}" : $" {text}";
|
||||
}
|
||||
|
||||
private static ConsoleColor GetTokenColor(AsmToken token, AsmTheme theme, ConsoleColor defaultColor)
|
||||
{
|
||||
return token.Kind switch
|
||||
{
|
||||
AsmTokenKind.Register => theme.RegisterColor,
|
||||
AsmTokenKind.Immediate => theme.ImmediateColor,
|
||||
AsmTokenKind.Label => theme.LabelColor,
|
||||
AsmTokenKind.MemoryKeyword => theme.MemoryKeywordColor,
|
||||
AsmTokenKind.Separator => theme.SeparatorColor,
|
||||
_ => defaultColor,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1516,9 +1516,17 @@ namespace Brovan.EmulationMenu
|
||||
break;
|
||||
}
|
||||
|
||||
string Disassembled = Disassembler.DisassembleToStringEmu(DisasmData, DisasmAddress, Binary, 0, true);
|
||||
X86Instruction[] Instructions = Disassembler.DisassembleBinary(DisasmData, DisasmAddress, Binary, 0, true);
|
||||
if (Instructions.Length == 0)
|
||||
{
|
||||
string Disassembled = Disassembler.DisassembleToStringEmu(DisasmData, DisasmAddress, Binary, 0, true);
|
||||
Console.WriteLine($"\n{Disassembled}\n");
|
||||
break;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n{Disassembled}\n");
|
||||
Console.WriteLine();
|
||||
PrintHighlightedDisassembly(Instructions, true, true);
|
||||
Console.WriteLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Brovan.Core.Emulation.OS.Windows;
|
||||
using Brovan.Core.Emulation.OS.Linux;
|
||||
using Brovan.EmulationMenu;
|
||||
using Brovan.Core.Emulation;
|
||||
using Brovan.Core.Emulation.Guests;
|
||||
using Brovan.Analysis;
|
||||
@@ -13,6 +14,7 @@ using static Brovan.Variables;
|
||||
using static Brovan.Core.Helpers.BinaryHelpers;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using static Brovan.Core.Helpers.Utils;
|
||||
|
||||
namespace Brovan
|
||||
@@ -267,11 +269,17 @@ namespace Brovan
|
||||
|
||||
internal static string FormatInstructionForDebugger(X86Instruction Instruction)
|
||||
{
|
||||
string Text = Instruction.Mnemonic ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(Instruction.Operand))
|
||||
Text += " " + Instruction.Operand;
|
||||
return AsmConsoleFormatter.FormatInstructionForDebugger(Instruction);
|
||||
}
|
||||
|
||||
return $"0x{Instruction.Address:X}: {Text}";
|
||||
internal static void PrintHighlightedInstruction(X86Instruction Instruction, bool Current = false, bool ShowAddress = true)
|
||||
{
|
||||
AsmConsoleRenderer.WriteInstruction(Instruction, Current, ShowAddress);
|
||||
}
|
||||
|
||||
internal static void PrintHighlightedDisassembly(X86Instruction[] Instructions, bool HighlightFirst = true, bool ShowAddress = true)
|
||||
{
|
||||
AsmConsoleRenderer.WriteInstructions(Instructions, HighlightFirst, ShowAddress);
|
||||
}
|
||||
|
||||
internal static void PrintStoppedInstruction(string Prefix, ulong Address)
|
||||
|
||||
Reference in New Issue
Block a user