mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-07-03 05:10:27 +03:00
Compare commits
232 Commits
master
...
x86-disass
Author | SHA1 | Date | |
---|---|---|---|
e137deff7e | |||
c044db1b96 | |||
1c7054781c | |||
de2e4312fb | |||
0ddbfd2951 | |||
883f3a2659 | |||
c7fd962d90 | |||
7eead316cd | |||
23fb497e0a | |||
54a0a3e9c0 | |||
8c15143933 | |||
d089fc9b28 | |||
8567cf1d6d | |||
1536ce4385 | |||
7bb14523e5 | |||
d25e7e8133 | |||
3cdd1fb2e6 | |||
adb37fe84f | |||
fea700596c | |||
167b0e2c48 | |||
57d9a35ec5 | |||
6ea208d8bf | |||
a4de35cf41 | |||
cfef24f72d | |||
4cb20cf741 | |||
e9c221ac14 | |||
e967c0e0c0 | |||
18ecf31c46 | |||
2a8cf9534e | |||
84d5652a62 | |||
66f9e838ad | |||
e6e3e886c8 | |||
d216c29315 | |||
ec56576116 | |||
5916d13995 | |||
963248dca0 | |||
df453b930f | |||
4d2db05a07 | |||
33dc0b0fa2 | |||
a62812f71c | |||
a9d4c39717 | |||
124493cd94 | |||
7c0e6d7f3a | |||
dd97a00c2b | |||
3fc0ebf1d5 | |||
8c9b34ef09 | |||
fa1a7f582c | |||
089fe4dfd4 | |||
b210764caa | |||
e8955b1ebd | |||
9096267f73 | |||
eac8e9ea69 | |||
226ec25549 | |||
9da33e12c4 | |||
800915b534 | |||
f654f64c71 | |||
be2dfc3dc5 | |||
72ad1c0d90 | |||
d2279f4720 | |||
f702e9da84 | |||
41a4e5884d | |||
58b739d922 | |||
a474c4b7e4 | |||
09786b781b | |||
e5b63270b6 | |||
154e811d2d | |||
bc6d32a725 | |||
db96af74ff | |||
193f9cd2d8 | |||
a91d6af8fc | |||
9445fb225f | |||
9ddaa02471 | |||
deb98183b1 | |||
6719cff2af | |||
d4eb920e2f | |||
e06ea2beb3 | |||
46592d4877 | |||
4327464b98 | |||
0dac4481f6 | |||
6882f0bd86 | |||
61e92a50a5 | |||
0a2d551cb4 | |||
904f0eed47 | |||
6169d68967 | |||
d6903f2e5b | |||
2fde1f2ae3 | |||
2123ed2c5d | |||
abe4d38d4b | |||
49f1d7d221 | |||
3ea327064a | |||
d351f41808 | |||
9117830ff1 | |||
685eeda03d | |||
c516e063e7 | |||
157171fa90 | |||
c9e854a663 | |||
99b93523a4 | |||
f54dc10596 | |||
4567465570 | |||
d3d2c4c63f | |||
b7c6092b7f | |||
dae52fc3ec | |||
5b09d6f9b8 | |||
689195c6e5 | |||
e134452eda | |||
53696a9f1c | |||
243789892d | |||
4b549f4b1b | |||
c9901aa9b8 | |||
2d0f701dd1 | |||
996be18172 | |||
38770de005 | |||
5daab494e1 | |||
fac1339fec | |||
4e837f5c63 | |||
b531db77d5 | |||
0ff20494e1 | |||
46a4696481 | |||
7ab388f26d | |||
ec1aa4a124 | |||
ec70b31058 | |||
8d1522b6cb | |||
6827cb735e | |||
00547ed273 | |||
0ea3294c61 | |||
f19f2254fe | |||
cf1e1acf71 | |||
11a2cfada4 | |||
59df064ca4 | |||
89b2b32cd6 | |||
b0ade45f1b | |||
30676b36a1 | |||
56c12b552c | |||
e91a0223f7 | |||
a04a16af7d | |||
8cf26060f2 | |||
032030169e | |||
b11b39ac4e | |||
d1d52af511 | |||
3f4b9a8547 | |||
2cdd9f1e83 | |||
565158d9bd | |||
2c85192d13 | |||
44c73321ea | |||
e8a16e7ecd | |||
af94b88868 | |||
b215908d76 | |||
611dce32e5 | |||
b2929c38e9 | |||
0d271abdcb | |||
b718745d7a | |||
17ef78a7a7 | |||
e12f5b5bdf | |||
33b151d856 | |||
465056dd9a | |||
618ee641a8 | |||
d46d03ce65 | |||
d0667950f8 | |||
79bf419c07 | |||
efd9141b39 | |||
bdd691a021 | |||
410211fcc6 | |||
9dfa559045 | |||
c14a92bf04 | |||
bfaeba0d5f | |||
b4a85d2839 | |||
52841237c1 | |||
28ba47bfab | |||
c701fdb435 | |||
9cad5ff95c | |||
8123ced2d6 | |||
03aa51d13c | |||
b8a37e626c | |||
402cdc68fb | |||
7d23af32fa | |||
266fdfeee5 | |||
393aac5bf6 | |||
439b6576b7 | |||
70f2acd3d1 | |||
79bb19df6b | |||
94a61a17a1 | |||
3ffaaf0057 | |||
7063a4a5a8 | |||
016e1ee54f | |||
f658f4384c | |||
3cc6d27e33 | |||
82653f96f2 | |||
bb695cf3bb | |||
acccf5169a | |||
c027adc113 | |||
0cc03c2479 | |||
3ea408d088 | |||
d5bcd56774 | |||
759d28f9a7 | |||
5ede2bd3c6 | |||
a0e40c8a52 | |||
f107b8e763 | |||
794b56c6b5 | |||
a6b6cc1149 | |||
6ed6a7bd00 | |||
fe0b04f5a1 | |||
bf5fcdd2ff | |||
bd251b6c06 | |||
0925bb7fef | |||
87e0c152e2 | |||
dbc9b42007 | |||
1442fd7060 | |||
e4b8645da0 | |||
58a148ebd8 | |||
82ffd51a3e | |||
0fb3fd7311 | |||
f3aa862a57 | |||
cedd7a931e | |||
ae1c4730d0 | |||
dffc405c10 | |||
2e6e133159 | |||
1a76bb4e77 | |||
3823121bea | |||
60f63c2c06 | |||
d73cccd3c5 | |||
9b5ec7e0d6 | |||
6a69b0b91b | |||
53de948376 | |||
cf2d61915c | |||
79773b08aa | |||
49a0a9e3a3 | |||
e4adb45ed2 | |||
f1a2fca4f3 | |||
61a86f6681 | |||
666a592217 | |||
bc572f5d33 | |||
8dfc0b1a7b |
23
.windsurfrules
Normal file
23
.windsurfrules
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
when creating or edditing code, adjust namespace declaration style to oneliner, e.g. "namespace MyNamespace;".
|
||||||
|
|
||||||
|
always separate usings, namespaces, type declarations, methods and properties with empty line.
|
||||||
|
|
||||||
|
always add comments to the code, when the code is not trivial.
|
||||||
|
|
||||||
|
always put classes into separate files.
|
||||||
|
|
||||||
|
always try to build the project you've edited.
|
||||||
|
|
||||||
|
always summarize the changes you've made.
|
||||||
|
|
||||||
|
always add changes to git with descriptive comment, but be concise.
|
||||||
|
|
||||||
|
never use terminal commands to edit code. In case of a failure, write it to user and stop execution.
|
||||||
|
|
||||||
|
never address compiler warnings yourself. If you see a warning, suggest to address it.
|
||||||
|
|
||||||
|
when working with RVA variables, always add that to variable name, e.g. "nameRVA".
|
||||||
|
|
||||||
|
always build only affected project, not full solution.
|
||||||
|
|
||||||
|
never introduce special cases in general solutions.
|
14
LandscapeExplorer/LandscapeExplorer.csproj
Normal file
14
LandscapeExplorer/LandscapeExplorer.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\NResLib\NResLib.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
332
LandscapeExplorer/Program.cs
Normal file
332
LandscapeExplorer/Program.cs
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using NResLib;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LandscapeExplorer;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
private const string MapsDirectory = @"C:\Program Files (x86)\Nikita\Iron Strategy\DATA\MAPS\SC_3";
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
|
Console.WriteLine("Parkan 1 Landscape Explorer\n");
|
||||||
|
|
||||||
|
// Get all .map and .msh files in the directory
|
||||||
|
var mapFiles = Directory.GetFiles(MapsDirectory, "*.map");
|
||||||
|
var mshFiles = Directory.GetFiles(MapsDirectory, "*.msh");
|
||||||
|
|
||||||
|
Console.WriteLine($"Found {mapFiles.Length} .map files and {mshFiles.Length} .msh files in {MapsDirectory}\n");
|
||||||
|
|
||||||
|
// Process .map files
|
||||||
|
Console.WriteLine("=== MAP Files Analysis ===\n");
|
||||||
|
foreach (var mapFile in mapFiles)
|
||||||
|
{
|
||||||
|
AnalyzeNResFile(mapFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process .msh files
|
||||||
|
Console.WriteLine("\n=== MSH Files Analysis ===\n");
|
||||||
|
foreach (var mshFile in mshFiles)
|
||||||
|
{
|
||||||
|
AnalyzeNResFile(mshFile);
|
||||||
|
|
||||||
|
// Perform detailed landscape analysis on MSH files
|
||||||
|
AnalyzeLandscapeMeshFile(mshFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine("\nAnalysis complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analyzes an NRes file and displays its structure
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">Path to the NRes file</param>
|
||||||
|
private static void AnalyzeNResFile(string filePath)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Analyzing file: {Path.GetFileName(filePath)}");
|
||||||
|
|
||||||
|
var parseResult = NResParser.ReadFile(filePath);
|
||||||
|
|
||||||
|
if (parseResult.Error != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Error: {parseResult.Error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var archive = parseResult.Archive!;
|
||||||
|
|
||||||
|
Console.WriteLine($" Header: {archive.Header.NRes}, Version: {archive.Header.Version:X}, Files: {archive.Header.FileCount}, Size: {archive.Header.TotalFileLengthBytes} bytes");
|
||||||
|
|
||||||
|
// Group files by type for better analysis
|
||||||
|
var filesByType = archive.Files.GroupBy(f => f.FileType);
|
||||||
|
|
||||||
|
foreach (var group in filesByType)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" File Type: {group.Key}, Count: {group.Count()}");
|
||||||
|
|
||||||
|
// Display details of the first file of each type as an example
|
||||||
|
var example = group.First();
|
||||||
|
Console.WriteLine($" Example: {example.FileName}");
|
||||||
|
Console.WriteLine($" Elements: {example.ElementCount}, Element Size: {example.ElementSize} bytes");
|
||||||
|
Console.WriteLine($" File Length: {example.FileLength} bytes, Offset: {example.OffsetInFile}");
|
||||||
|
|
||||||
|
// If this is a landscape-related file, provide more detailed analysis
|
||||||
|
if (IsLandscapeRelatedType(group.Key))
|
||||||
|
{
|
||||||
|
AnalyzeLandscapeData(example, filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if a file type is related to landscape data
|
||||||
|
/// </summary>
|
||||||
|
private static bool IsLandscapeRelatedType(string fileType)
|
||||||
|
{
|
||||||
|
// Based on the Landscape constructor analysis, these types might be related to landscape
|
||||||
|
return fileType == "LAND" || fileType == "TERR" || fileType == "MSH0" ||
|
||||||
|
fileType == "MESH" || fileType == "MATR" || fileType == "TEXT";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analyzes landscape-specific data in a file
|
||||||
|
/// </summary>
|
||||||
|
private static void AnalyzeLandscapeData(ListMetadataItem item, string filePath)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" [Landscape Data Analysis]:");
|
||||||
|
|
||||||
|
// Read the file data for this specific item
|
||||||
|
using var fs = new FileStream(filePath, FileMode.Open);
|
||||||
|
fs.Seek(item.OffsetInFile, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var buffer = new byte[Math.Min(item.FileLength, 256)]; // Read at most 256 bytes for analysis
|
||||||
|
fs.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
// Display some basic statistics based on the file type
|
||||||
|
if (item.FileType == "LAND" || item.FileType == "TERR")
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Terrain data with {item.ElementCount} elements");
|
||||||
|
// If element size is known, we can calculate grid dimensions
|
||||||
|
if (item.ElementCount > 0 && item.ElementSize > 0)
|
||||||
|
{
|
||||||
|
// Assuming square terrain, which is common in games from this era
|
||||||
|
var gridSize = Math.Sqrt(item.ElementCount);
|
||||||
|
if (Math.Abs(gridSize - Math.Round(gridSize)) < 0.001) // If it's close to a whole number
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Terrain grid size: {Math.Round(gridSize)} x {Math.Round(gridSize)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (item.FileType == "MSH0" || item.FileType == "MESH")
|
||||||
|
{
|
||||||
|
// For mesh data, try to estimate vertex/face counts
|
||||||
|
Console.WriteLine($" Mesh data, possibly with vertices and faces");
|
||||||
|
|
||||||
|
// Common sizes: vertices are often 12 bytes (3 floats), faces are often 12 bytes (3 indices)
|
||||||
|
if (item.ElementSize == 12)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Possibly {item.ElementCount} vertices or faces");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display first few bytes as hex for debugging
|
||||||
|
var hexPreview = BitConverter.ToString(
|
||||||
|
buffer.Take(32)
|
||||||
|
.ToArray()
|
||||||
|
)
|
||||||
|
.Replace("-", " ");
|
||||||
|
Console.WriteLine($" Data preview (hex): {hexPreview}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a detailed analysis of a landscape mesh file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">Path to the MSH file</param>
|
||||||
|
private static void AnalyzeLandscapeMeshFile(string filePath)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\nDetailed Landscape Analysis for: {Path.GetFileName(filePath)}\n");
|
||||||
|
|
||||||
|
var parseResult = NResParser.ReadFile(filePath);
|
||||||
|
if (parseResult.Error != null || parseResult.Archive == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Error analyzing file: {parseResult.Error}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var archive = parseResult.Archive;
|
||||||
|
|
||||||
|
// Based on the Landscape constructor and the file analysis, we can identify specific sections
|
||||||
|
// File types in MSH files appear to be numeric values (01, 02, 03, etc.)
|
||||||
|
|
||||||
|
// First, let's extract all the different data sections
|
||||||
|
var sections = new Dictionary<string, (ListMetadataItem Meta, byte[] Data)>();
|
||||||
|
|
||||||
|
foreach (var item in archive.Files)
|
||||||
|
{
|
||||||
|
using var fs = new FileStream(filePath, FileMode.Open);
|
||||||
|
fs.Seek(item.OffsetInFile, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var buffer = new byte[item.FileLength];
|
||||||
|
fs.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
sections[item.FileType] = (item, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now analyze each section based on what we know from the Landscape constructor
|
||||||
|
Console.WriteLine(" Landscape Structure Analysis:");
|
||||||
|
|
||||||
|
// Type 01 appears to be basic landscape information (possibly header/metadata)
|
||||||
|
if (sections.TryGetValue("01 00 00 00", out var section01))
|
||||||
|
{
|
||||||
|
Console.WriteLine($" Section 01: Basic Landscape Info");
|
||||||
|
Console.WriteLine($" Elements: {section01.Meta.ElementCount}, Element Size: {section01.Meta.ElementSize} bytes");
|
||||||
|
Console.WriteLine($" Total Size: {section01.Meta.FileLength} bytes");
|
||||||
|
|
||||||
|
// Try to extract some basic info if the format is as expected
|
||||||
|
if (section01.Meta.ElementSize == 38 && section01.Data.Length >= 38)
|
||||||
|
{
|
||||||
|
// This is speculative based on common terrain formats
|
||||||
|
var width = BitConverter.ToInt32(section01.Data, 0);
|
||||||
|
var height = BitConverter.ToInt32(section01.Data, 4);
|
||||||
|
Console.WriteLine($" Possible Dimensions: {width} x {height}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 03 appears to be vertex data (based on element size of 12 bytes which is typical for 3D vertices)
|
||||||
|
if (sections.TryGetValue("03 00 00 00", out var section03))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\n Section 03: Vertex Data");
|
||||||
|
Console.WriteLine($" Vertex Count: {section03.Meta.ElementCount}");
|
||||||
|
Console.WriteLine($" Vertex Size: {section03.Meta.ElementSize} bytes");
|
||||||
|
|
||||||
|
// If we have vertex data in expected format (3 floats per vertex)
|
||||||
|
if (section03.Meta.ElementSize == 12 && section03.Data.Length >= 36)
|
||||||
|
{
|
||||||
|
// Display first 3 vertices as example
|
||||||
|
Console.WriteLine(" Sample Vertices:");
|
||||||
|
for (int i = 0; i < Math.Min(3, section03.Meta.ElementCount); i++)
|
||||||
|
{
|
||||||
|
var offset = i * 12;
|
||||||
|
var x = BitConverter.ToSingle(section03.Data, offset);
|
||||||
|
var y = BitConverter.ToSingle(section03.Data, offset + 4);
|
||||||
|
var z = BitConverter.ToSingle(section03.Data, offset + 8);
|
||||||
|
Console.WriteLine($" Vertex {i}: ({x}, {y}, {z})");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate terrain bounds
|
||||||
|
var minX = float.MaxValue;
|
||||||
|
var minY = float.MaxValue;
|
||||||
|
var minZ = float.MaxValue;
|
||||||
|
var maxX = float.MinValue;
|
||||||
|
var maxY = float.MinValue;
|
||||||
|
var maxZ = float.MinValue;
|
||||||
|
|
||||||
|
for (int i = 0; i < section03.Meta.ElementCount; i++)
|
||||||
|
{
|
||||||
|
var offset = i * 12;
|
||||||
|
if (offset + 12 <= section03.Data.Length)
|
||||||
|
{
|
||||||
|
var x = BitConverter.ToSingle(section03.Data, offset);
|
||||||
|
var y = BitConverter.ToSingle(section03.Data, offset + 4);
|
||||||
|
var z = BitConverter.ToSingle(section03.Data, offset + 8);
|
||||||
|
|
||||||
|
minX = Math.Min(minX, x);
|
||||||
|
minY = Math.Min(minY, y);
|
||||||
|
minZ = Math.Min(minZ, z);
|
||||||
|
maxX = Math.Max(maxX, x);
|
||||||
|
maxY = Math.Max(maxY, y);
|
||||||
|
maxZ = Math.Max(maxZ, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine(" Terrain Bounds:");
|
||||||
|
Console.WriteLine($" Min: ({minX}, {minY}, {minZ})");
|
||||||
|
Console.WriteLine($" Max: ({maxX}, {maxY}, {maxZ})");
|
||||||
|
Console.WriteLine($" Dimensions: {maxX - minX} x {maxY - minY} x {maxZ - minZ}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 02 might be face/index data for the mesh
|
||||||
|
if (sections.TryGetValue("02 00 00 00", out var section02))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\n Section 02: Possible Face/Index Data");
|
||||||
|
Console.WriteLine($" Elements: {section02.Meta.ElementCount}");
|
||||||
|
Console.WriteLine($" Element Size: {section02.Meta.ElementSize} bytes");
|
||||||
|
|
||||||
|
// If element size is divisible by 4 (common for index data)
|
||||||
|
if (section02.Meta.ElementSize % 4 == 0 && section02.Data.Length >= 12)
|
||||||
|
{
|
||||||
|
// Display first triangle as example (assuming 3 indices per triangle)
|
||||||
|
Console.WriteLine(" Sample Indices (if this is index data):");
|
||||||
|
var indicesPerElement = section02.Meta.ElementSize / 4;
|
||||||
|
for (int i = 0; i < Math.Min(1, section02.Meta.ElementCount); i++)
|
||||||
|
{
|
||||||
|
Console.Write($" Element {i}: ");
|
||||||
|
for (int j = 0; j < indicesPerElement; j++)
|
||||||
|
{
|
||||||
|
var offset = i * section02.Meta.ElementSize + j * 4;
|
||||||
|
if (offset + 4 <= section02.Data.Length)
|
||||||
|
{
|
||||||
|
var index = BitConverter.ToInt32(section02.Data, offset);
|
||||||
|
Console.Write($"{index} ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types 04, 05, 12, 0E, 0B might be texture coordinates, normals, colors, etc.
|
||||||
|
var otherSections = new[] {"04 00 00 00", "05 00 00 00", "12 00 00 00", "0E 00 00 00", "0B 00 00 00"};
|
||||||
|
foreach (var sectionType in otherSections)
|
||||||
|
{
|
||||||
|
if (sections.TryGetValue(sectionType, out var section))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\n Section {sectionType.Substring(0, 2)}: Additional Mesh Data");
|
||||||
|
Console.WriteLine($" Elements: {section.Meta.ElementCount}");
|
||||||
|
Console.WriteLine($" Element Size: {section.Meta.ElementSize} bytes");
|
||||||
|
|
||||||
|
// If element size is 4 bytes, it could be color data, texture indices, etc.
|
||||||
|
if (section.Meta.ElementSize == 4 && section.Data.Length >= 12)
|
||||||
|
{
|
||||||
|
Console.WriteLine(" Sample Data (as integers):");
|
||||||
|
for (int i = 0; i < Math.Min(3, section.Meta.ElementCount); i++)
|
||||||
|
{
|
||||||
|
var offset = i * 4;
|
||||||
|
var value = BitConverter.ToInt32(section.Data, offset);
|
||||||
|
Console.WriteLine($" Element {i}: {value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type 15 might be material or special data (Msh_15 in the decompiled code)
|
||||||
|
if (sections.TryGetValue("15 00 00 00", out var section15) && sections.TryGetValue("03 00 00 00", out var vertexSection))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"\n Section 15: Special Data (Msh_15 type in decompiled code)");
|
||||||
|
Console.WriteLine($" Elements: {section15.Meta.ElementCount}");
|
||||||
|
Console.WriteLine($" Element Size: {section15.Meta.ElementSize} bytes");
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (var i = 0; i < section15.Data.Length; i += 28)
|
||||||
|
{
|
||||||
|
var first = BinaryPrimitives.ReadUInt32LittleEndian(section15.Data.AsSpan(i));
|
||||||
|
|
||||||
|
if ((first & 0x20000) != 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Found {first}/0x{first:X8} 0x20000 at index {i / 28}. &0x20000={first&0x20000}/0x{first&0x20000:X8} offset: {i:X8}");
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"Total found: {count}");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
151
NResLib/NResPacker.cs
Normal file
151
NResLib/NResPacker.cs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NResLib;
|
||||||
|
|
||||||
|
public class NResPacker
|
||||||
|
{
|
||||||
|
public static string Pack(NResArchive archive, string srcNresPath, string contentDirectoryPath, string targetFileDirectoryPath)
|
||||||
|
{
|
||||||
|
var diskFiles = Directory.GetFiles(contentDirectoryPath)
|
||||||
|
.Select(Path.GetFileName)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var fileOffset = 16; // 16 по умолчанию, т.к. есть заголовок в 16 байт.
|
||||||
|
|
||||||
|
var metadataItems = new List<ListMetadataItem>();
|
||||||
|
|
||||||
|
foreach (var archiveFile in archive.Files)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(archiveFile.FileName);
|
||||||
|
var fileName = Path.GetFileNameWithoutExtension(archiveFile.FileName);
|
||||||
|
|
||||||
|
if (extension == "")
|
||||||
|
{
|
||||||
|
extension = ".bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetFileName = $"{archiveFile.Index}_{archiveFile.FileType}_{fileName}{extension}";
|
||||||
|
|
||||||
|
if (diskFiles.All(x => x != targetFileName))
|
||||||
|
{
|
||||||
|
return $"Не найдён файл {targetFileName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
var filePath = Path.Combine(contentDirectoryPath, targetFileName);
|
||||||
|
|
||||||
|
var fileInfo = new FileInfo(filePath);
|
||||||
|
|
||||||
|
if (!fileInfo.Exists)
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
var newFileLength = (int)fileInfo.Length;
|
||||||
|
|
||||||
|
var listItem = new ListMetadataItem(
|
||||||
|
archiveFile.FileType,
|
||||||
|
archiveFile.ElementCount,
|
||||||
|
archiveFile.Magic1,
|
||||||
|
newFileLength,
|
||||||
|
archiveFile.ElementSize,
|
||||||
|
archiveFile.FileName,
|
||||||
|
archiveFile.Magic3,
|
||||||
|
archiveFile.Magic4,
|
||||||
|
archiveFile.Magic5,
|
||||||
|
archiveFile.Magic6,
|
||||||
|
fileOffset,
|
||||||
|
archiveFile.Index
|
||||||
|
);
|
||||||
|
|
||||||
|
fileOffset += newFileLength;
|
||||||
|
|
||||||
|
metadataItems.Add(listItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalFileLength =
|
||||||
|
16 + // заголовок
|
||||||
|
metadataItems.Sum(x => x.FileLength) + // сумма длин всех файлов
|
||||||
|
metadataItems.Count * 64; // длина всех метаданных
|
||||||
|
|
||||||
|
var header = new NResArchiveHeader(archive.Header.NRes, archive.Header.Version, archive.Header.FileCount, totalFileLength);
|
||||||
|
|
||||||
|
var targetArchive = new NResArchive(header, metadataItems);
|
||||||
|
|
||||||
|
// имя архива = имени папки в которую архив распаковывали
|
||||||
|
string targetArchiveFileName = Path.GetFileName(srcNresPath)!;
|
||||||
|
|
||||||
|
var targetArchivePath = Path.Combine(targetFileDirectoryPath, targetArchiveFileName);
|
||||||
|
|
||||||
|
using var fs = new FileStream(targetArchivePath, FileMode.CreateNew);
|
||||||
|
|
||||||
|
Span<byte> span = stackalloc byte[4];
|
||||||
|
|
||||||
|
span.Clear();
|
||||||
|
Encoding.ASCII.GetBytes(header.NRes, span);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, header.Version);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, header.FileCount);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, header.TotalFileLengthBytes);
|
||||||
|
fs.Write(span);
|
||||||
|
|
||||||
|
foreach (var archiveFile in targetArchive.Files)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(archiveFile.FileName);
|
||||||
|
var fileName = Path.GetFileNameWithoutExtension(archiveFile.FileName);
|
||||||
|
|
||||||
|
if (extension == "")
|
||||||
|
{
|
||||||
|
extension = ".bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetFileName = $"{archiveFile.Index}_{archiveFile.FileType}_{fileName}{extension}";
|
||||||
|
|
||||||
|
var filePath = Path.Combine(contentDirectoryPath, targetFileName);
|
||||||
|
using var srcFs = new FileStream(filePath, FileMode.Open);
|
||||||
|
|
||||||
|
srcFs.CopyTo(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> fileNameSpan = stackalloc byte[20];
|
||||||
|
|
||||||
|
foreach (var archiveFile in targetArchive.Files)
|
||||||
|
{
|
||||||
|
span.Clear();
|
||||||
|
Encoding.ASCII.GetBytes(archiveFile.FileType, span);
|
||||||
|
fs.Write(span);
|
||||||
|
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(span, archiveFile.ElementCount);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.Magic1);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.FileLength);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.ElementSize);
|
||||||
|
fs.Write(span);
|
||||||
|
|
||||||
|
fileNameSpan.Clear();
|
||||||
|
Encoding.ASCII.GetBytes(archiveFile.FileName, fileNameSpan);
|
||||||
|
fs.Write(fileNameSpan);
|
||||||
|
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.Magic3);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.Magic4);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.Magic5);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.Magic6);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.OffsetInFile);
|
||||||
|
fs.Write(span);
|
||||||
|
BinaryPrimitives.WriteInt32LittleEndian(span, archiveFile.Index);
|
||||||
|
fs.Write(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Flush();
|
||||||
|
|
||||||
|
return "Запакован архив";
|
||||||
|
}
|
||||||
|
}
|
@ -138,6 +138,34 @@ namespace NResUI.ImGuiUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nResExplorerViewModel.HasFile)
|
||||||
|
{
|
||||||
|
if (ImGui.MenuItem("Запаковать NRes"))
|
||||||
|
{
|
||||||
|
messageBox.Show("Выберите папку с контентом NRES");
|
||||||
|
var contentDirectoryPicker = Dialog.FolderPicker();
|
||||||
|
|
||||||
|
if (contentDirectoryPicker.IsOk)
|
||||||
|
{
|
||||||
|
var contentDirectoryPath = contentDirectoryPicker.Path;
|
||||||
|
|
||||||
|
var targetFileDirectoryPicker = Dialog.FolderPicker();
|
||||||
|
|
||||||
|
if (targetFileDirectoryPicker.IsOk)
|
||||||
|
{
|
||||||
|
var targetFileDirectory = targetFileDirectoryPicker.Path;
|
||||||
|
|
||||||
|
var packResult = NResPacker.Pack(
|
||||||
|
nResExplorerViewModel.Archive!,
|
||||||
|
nResExplorerViewModel.Path!,
|
||||||
|
contentDirectoryPath, targetFileDirectory);
|
||||||
|
|
||||||
|
messageBox.Show(packResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.EndMenu();
|
ImGui.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +82,8 @@ public class NResExplorerPanel : IImGuiPanel
|
|||||||
);
|
);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.Text(
|
ImGui.Text(
|
||||||
_viewModel.Archive.Files[i]
|
"0x" + _viewModel.Archive.Files[i]
|
||||||
.ElementSize.ToString()
|
.ElementSize.ToString("X2")
|
||||||
);
|
);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.Text(_viewModel.Archive.Files[i].FileName);
|
ImGui.Text(_viewModel.Archive.Files[i].FileName);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -30,6 +30,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Visualisator", "Visualisato
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X86Disassembler", "X86Disassembler\X86Disassembler.csproj", "{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X86Disassembler", "X86Disassembler\X86Disassembler.csproj", "{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "X86DisassemblerTests", "X86DisassemblerTests\X86DisassemblerTests.csproj", "{D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LandscapeExplorer", "LandscapeExplorer\LandscapeExplorer.csproj", "{2700BD3F-DC67-4B58-8F73-F790AA68E4FE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -88,5 +92,13 @@ Global
|
|||||||
{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B5C2E94A-0F63-4E09-BC04-F2518E2CC1F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D6A1F5A9-0C7A-4F8F-B8C5-83E9D3F3A1D5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{2700BD3F-DC67-4B58-8F73-F790AA68E4FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{2700BD3F-DC67-4B58-8F73-F790AA68E4FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{2700BD3F-DC67-4B58-8F73-F790AA68E4FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{2700BD3F-DC67-4B58-8F73-F790AA68E4FE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -1,11 +1,31 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssemblyCodeArray_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003Fa1_003Fbc9d4e81_003FAssemblyCodeArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssemblyCodeArray_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003Fa1_003Fbc9d4e81_003FAssemblyCodeArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssemblyCodeMemory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F6e_003F09b667c6_003FAssemblyCodeMemory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAssemblyCodeMemory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F6e_003F09b667c6_003FAssemblyCodeMemory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACollectionAsserts_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F435f965090c5b89f0c5efb49ac3c5a72367d90599314191af25a832d0942f_003FCollectionAsserts_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACsvReader_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Ff7b87edd534764eebf2388a77d49e5cd9c6d49eb6788dca9b1c07d4545412715_003FCsvReader_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADefaultTypeConverter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F64b864a5d465bc24fc4b55e1026aba213beb1733ef631abeca5a9f25357eda_003FDefaultTypeConverter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADisassembler_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003Fd4_003Fad0818f9_003FDisassembler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADisassembler_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003Fd4_003Fad0818f9_003FDisassembler_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEncoding_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F1675dc7b710feeeb3e0bc8728be8a947537155c199480fb23b776e81d459_003FEncoding_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFailAsserts_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F807c15a7d8383b1548dff1ae33270e637836659d9caecd676ea6f2c59f1c71a_003FFailAsserts_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGL_002Egen_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F54e6df16dd99323ba9b0682ce5d5dac3648ccd10aafd29d5f3fad52b62bf3f75_003FGL_002Egen_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AGL_002Egen_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F54e6df16dd99323ba9b0682ce5d5dac3648ccd10aafd29d5f3fad52b62bf3f75_003FGL_002Egen_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAssemblyCode_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F8c_003F9fe9bac2_003FIAssemblyCode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIAssemblyCode_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F8c_003F9fe9bac2_003FIAssemblyCode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMatrix4x4_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fed6aa59cd75423c5b655901d6ec4fb4be48ab669fa6fb01b3a7a7f31be95_003FMatrix4x4_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMatrix4x4_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fed6aa59cd75423c5b655901d6ec4fb4be48ab669fa6fb01b3a7a7f31be95_003FMatrix4x4_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FLocal_003FSymbols_003Fsrc_003Fdotnet_003Fruntime_003F5535e31a712343a63f5d7d796cd874e563e5ac14_003Fsrc_003Flibraries_003FSystem_002EPrivate_002ECoreLib_003Fsrc_003FSystem_003FMemory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMemory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FLocal_003FSymbols_003Fsrc_003Fdotnet_003Fruntime_003F5535e31a712343a63f5d7d796cd874e563e5ac14_003Fsrc_003Flibraries_003FSystem_002EPrivate_002ECoreLib_003Fsrc_003FSystem_003FMemory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa03380083db34a2faee436e29e06a72ae8e910_003Fb6_003F67cd826c_003FSafeFileHandle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASingle_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc99a63bcf3d2a18c20ee19e58ac875ab1edf2a147c8b92ffeed185ab8a44b4_003FSingle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASingle_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc99a63bcf3d2a18c20ee19e58ac875ab1edf2a147c8b92ffeed185ab8a44b4_003FSingle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStringAsserts_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F999ae9cc4ab7b7cfbc5080803e994426e97fd9d87c5b1f44544a799bc114_003FStringAsserts_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Aud_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F15_003F87bd9007_003Fud_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Aud_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F15_003F87bd9007_003Fud_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Audis86_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F95_003F953bbb0f_003Fudis86_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Audis86_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FUsers_003FAdmin_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fa494e0aa381c41ff9484df33e5edb42535e00_003F95_003F953bbb0f_003Fudis86_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
|
||||||
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=47ebaefe_002Da806_002D4565_002Dabe7_002D4f14ac675135/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;X86DisassemblerTests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
|
<Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="&lt;X86DisassemblerTests&gt;" />
|
||||||
|
</SessionState></s:String>
|
||||||
|
|
||||||
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=87a33e46_002D2816_002D434f_002D972a_002D703eb7a78476/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Session" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
|
<And>
|
||||||
|
<Namespace>X86DisassemblerTests</Namespace>
|
||||||
|
<Project Location="C:\Projects\CSharp\ParkanPlayground\X86DisassemblerTests" Presentation="&lt;X86DisassemblerTests&gt;" />
|
||||||
|
</And>
|
||||||
|
</SessionState></s:String>
|
||||||
|
|
||||||
|
</wpf:ResourceDictionary>
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
44
TestDisassembler.cs
Normal file
44
TestDisassembler.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using X86Disassembler.X86;
|
||||||
|
|
||||||
|
namespace TestDisassembler;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
// Test the specific byte sequence that's causing issues
|
||||||
|
byte[] codeBytes = HexStringToByteArray("816B1078563412");
|
||||||
|
|
||||||
|
// Create a disassembler with the code
|
||||||
|
Disassembler disassembler = new Disassembler(codeBytes, 0x1000);
|
||||||
|
|
||||||
|
// Disassemble the code
|
||||||
|
var instructions = disassembler.Disassemble();
|
||||||
|
|
||||||
|
// Print the number of instructions
|
||||||
|
Console.WriteLine($"Number of instructions: {instructions.Count}");
|
||||||
|
|
||||||
|
// Print each instruction
|
||||||
|
for (int i = 0; i < instructions.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Instruction {i+1}: {instructions[i].Mnemonic} {instructions[i].Operands}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] HexStringToByteArray(string hex)
|
||||||
|
{
|
||||||
|
// Remove any non-hex characters
|
||||||
|
hex = hex.Replace(" ", "").Replace("-", "");
|
||||||
|
|
||||||
|
// Create a byte array
|
||||||
|
byte[] bytes = new byte[hex.Length / 2];
|
||||||
|
|
||||||
|
// Convert each pair of hex characters to a byte
|
||||||
|
for (int i = 0; i < hex.Length; i += 2)
|
||||||
|
{
|
||||||
|
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
44
TestDisassembler/Program.cs
Normal file
44
TestDisassembler/Program.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using X86Disassembler.X86;
|
||||||
|
|
||||||
|
namespace TestDisassembler;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
// Test the specific byte sequence with segment override prefix that's causing issues
|
||||||
|
byte[] codeBytes = HexStringToByteArray("26FF7510");
|
||||||
|
|
||||||
|
// Create a disassembler with the code
|
||||||
|
Disassembler disassembler = new Disassembler(codeBytes, 0x1000);
|
||||||
|
|
||||||
|
// Disassemble the code
|
||||||
|
var instructions = disassembler.Disassemble();
|
||||||
|
|
||||||
|
// Print the number of instructions
|
||||||
|
Console.WriteLine($"Number of instructions: {instructions.Count}");
|
||||||
|
|
||||||
|
// Print each instruction
|
||||||
|
for (int i = 0; i < instructions.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Instruction {i+1}: {instructions[i].Mnemonic} {instructions[i].Operands}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] HexStringToByteArray(string hex)
|
||||||
|
{
|
||||||
|
// Remove any non-hex characters
|
||||||
|
hex = hex.Replace(" ", "").Replace("-", "");
|
||||||
|
|
||||||
|
// Create a byte array
|
||||||
|
byte[] bytes = new byte[hex.Length / 2];
|
||||||
|
|
||||||
|
// Convert each pair of hex characters to a byte
|
||||||
|
for (int i = 0; i < hex.Length; i += 2)
|
||||||
|
{
|
||||||
|
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
14
TestDisassembler/TestDisassembler.csproj
Normal file
14
TestDisassembler/TestDisassembler.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\X86Disassembler\X86Disassembler.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
22
X86Disassembler/Analysers/AsmFunction.cs
Normal file
22
X86Disassembler/Analysers/AsmFunction.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
namespace X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a disassembled function with its control flow graph
|
||||||
|
/// </summary>
|
||||||
|
public class AsmFunction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The starting address of the function
|
||||||
|
/// </summary>
|
||||||
|
public ulong Address { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of basic blocks that make up the function
|
||||||
|
/// </summary>
|
||||||
|
public List<InstructionBlock> Blocks { get; set; } = [];
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Address:X8}\n{string.Join("\n", Blocks)}";
|
||||||
|
}
|
||||||
|
}
|
554
X86Disassembler/Analysers/BlockDisassembler.cs
Normal file
554
X86Disassembler/Analysers/BlockDisassembler.cs
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
using X86Disassembler.X86;
|
||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disassembles code into basic blocks by following control flow instructions.
|
||||||
|
/// A basic block is a sequence of instructions with a single entry point (the first instruction)
|
||||||
|
/// and a single exit point (the last instruction, typically a jump or return).
|
||||||
|
/// </summary>
|
||||||
|
public class BlockDisassembler
|
||||||
|
{
|
||||||
|
// The buffer containing the code to disassemble
|
||||||
|
private readonly byte[] _codeBuffer;
|
||||||
|
|
||||||
|
// The length of the buffer
|
||||||
|
private readonly int _length;
|
||||||
|
|
||||||
|
// The base address of the code
|
||||||
|
private readonly ulong _baseAddress;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BlockDisassembler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeBuffer">The raw code bytes to be disassembled</param>
|
||||||
|
/// <param name="baseAddress">The base RVA (Relative Virtual Address) of the code section</param>
|
||||||
|
public BlockDisassembler(byte[] codeBuffer, ulong baseAddress)
|
||||||
|
{
|
||||||
|
_codeBuffer = codeBuffer;
|
||||||
|
_length = codeBuffer.Length;
|
||||||
|
|
||||||
|
_baseAddress = baseAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disassembles code starting from the specified RVA address by following control flow.
|
||||||
|
/// Creates blocks of instructions separated by jumps, branches, and returns.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rvaAddress">The RVA (Relative Virtual Address) to start disassembly from</param>
|
||||||
|
/// <returns>A list of instruction blocks representing the control flow of the code</returns>
|
||||||
|
public AsmFunction DisassembleFromAddress(uint rvaAddress)
|
||||||
|
{
|
||||||
|
// Create instruction decoder for parsing the code buffer
|
||||||
|
InstructionDecoder decoder = new InstructionDecoder(_codeBuffer, _length);
|
||||||
|
|
||||||
|
// Track visited addresses to prevent infinite loops
|
||||||
|
HashSet<ulong> visitedAddresses = [];
|
||||||
|
|
||||||
|
// Queue of addresses to process (breadth-first approach)
|
||||||
|
Queue<ulong> addressQueue = [];
|
||||||
|
|
||||||
|
// Calculate the file offset from the RVA by subtracting the base address
|
||||||
|
// Store the file offset for processing, but we'll convert back to RVA when creating blocks
|
||||||
|
ulong fileOffset = rvaAddress - _baseAddress;
|
||||||
|
addressQueue.Enqueue(fileOffset);
|
||||||
|
|
||||||
|
// Keep track of the original entry point RVA for the function
|
||||||
|
ulong entryPointRVA = rvaAddress;
|
||||||
|
|
||||||
|
// List to store discovered basic blocks
|
||||||
|
List<InstructionBlock> blocks = [];
|
||||||
|
|
||||||
|
// Dictionary to track blocks by address for quick lookup
|
||||||
|
Dictionary<ulong, InstructionBlock> blocksByAddress = new Dictionary<ulong, InstructionBlock>();
|
||||||
|
|
||||||
|
while (addressQueue.Count > 0)
|
||||||
|
{
|
||||||
|
// Get the next address to process
|
||||||
|
var address = addressQueue.Dequeue();
|
||||||
|
|
||||||
|
// Skip if we've already visited this address
|
||||||
|
if (!visitedAddresses.Add(address))
|
||||||
|
{
|
||||||
|
// Skip addresses we've already processed
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position the decoder at the current address
|
||||||
|
decoder.SetPosition((int) address);
|
||||||
|
|
||||||
|
// Collect instructions for this block
|
||||||
|
List<Instruction> instructions = [];
|
||||||
|
|
||||||
|
// Get the current block if it exists (for tracking predecessors)
|
||||||
|
InstructionBlock? currentBlock = null;
|
||||||
|
if (blocksByAddress.TryGetValue(address, out var existingBlock))
|
||||||
|
{
|
||||||
|
currentBlock = existingBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process instructions until we hit a control flow change
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Get the current position
|
||||||
|
ulong currentPosition = (ulong)decoder.GetPosition();
|
||||||
|
|
||||||
|
// If we've stepped onto an existing block, create a new block up to this point
|
||||||
|
// and stop processing this path (to avoid duplicating instructions)
|
||||||
|
if (blocksByAddress.TryGetValue(currentPosition, out var targetBlock) && currentPosition != address)
|
||||||
|
{
|
||||||
|
// We've stepped onto an existing block, create a new one up to this point
|
||||||
|
|
||||||
|
// Register this block and establish the relationship with the target block
|
||||||
|
var newBlock = RegisterBlock(blocks, address, instructions, null, false, false);
|
||||||
|
blocksByAddress[address] = newBlock;
|
||||||
|
|
||||||
|
// Add the target block as a successor to the new block
|
||||||
|
newBlock.Successors.Add(targetBlock);
|
||||||
|
|
||||||
|
// Add the new block as a predecessor to the target block
|
||||||
|
targetBlock.Predecessors.Add(newBlock);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the next instruction
|
||||||
|
var instruction = decoder.DecodeInstruction();
|
||||||
|
|
||||||
|
// Handle decoding failures
|
||||||
|
if (instruction is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Unexpectedly failed to decode instruction at {address}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the instruction to the current block
|
||||||
|
instructions.Add(instruction);
|
||||||
|
|
||||||
|
// Check for conditional jump (e.g., JZ, JNZ, JLE)
|
||||||
|
// For conditional jumps, we need to follow both the jump target and the fall-through path
|
||||||
|
if (instruction.Type.IsConditionalJump())
|
||||||
|
{
|
||||||
|
// Get the jump target address
|
||||||
|
uint jumpTargetAddress = instruction.StructuredOperands[0].GetValue();
|
||||||
|
|
||||||
|
// Get the fall-through address (next instruction after this jump)
|
||||||
|
uint fallThroughAddress = (uint)decoder.GetPosition();
|
||||||
|
|
||||||
|
// Register this block (it ends with a conditional jump)
|
||||||
|
var newBlock = RegisterBlock(blocks, address, instructions, currentBlock, false, false);
|
||||||
|
blocksByAddress[address] = newBlock;
|
||||||
|
|
||||||
|
// Register the target block if it doesn't exist yet
|
||||||
|
InstructionBlock? jumpTargetBlock = null;
|
||||||
|
if (blocksByAddress.TryGetValue(jumpTargetAddress, out var existingTargetBlock))
|
||||||
|
{
|
||||||
|
jumpTargetBlock = existingTargetBlock;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We'll create this block later when we process the queue
|
||||||
|
// For now, just queue it for processing
|
||||||
|
addressQueue.Enqueue(jumpTargetAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the fall-through block if it doesn't exist yet
|
||||||
|
InstructionBlock? fallThroughBlock = null;
|
||||||
|
if (blocksByAddress.TryGetValue(fallThroughAddress, out var existingFallThroughBlock))
|
||||||
|
{
|
||||||
|
fallThroughBlock = existingFallThroughBlock;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We'll create this block later when we process the queue
|
||||||
|
// For now, just queue it for processing
|
||||||
|
addressQueue.Enqueue(fallThroughAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the jump target block exists, add it as a successor to the current block
|
||||||
|
if (jumpTargetBlock != null)
|
||||||
|
{
|
||||||
|
newBlock.Successors.Add(jumpTargetBlock);
|
||||||
|
jumpTargetBlock.Predecessors.Add(newBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the fall-through block exists, add it as a successor to the current block
|
||||||
|
if (fallThroughBlock != null)
|
||||||
|
{
|
||||||
|
newBlock.Successors.Add(fallThroughBlock);
|
||||||
|
fallThroughBlock.Predecessors.Add(newBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unconditional jump (e.g., JMP)
|
||||||
|
// For unconditional jumps, we only follow the jump target
|
||||||
|
if (instruction.Type.IsRegularJump())
|
||||||
|
{
|
||||||
|
// Get the jump target address
|
||||||
|
uint jumpTargetAddress = instruction.StructuredOperands[0].GetValue();
|
||||||
|
|
||||||
|
// Register this block (it ends with an unconditional jump)
|
||||||
|
var newBlock = RegisterBlock(blocks, address, instructions, currentBlock, false, false);
|
||||||
|
blocksByAddress[address] = newBlock;
|
||||||
|
|
||||||
|
// Register the target block if it doesn't exist yet
|
||||||
|
InstructionBlock? jumpTargetBlock = null;
|
||||||
|
if (blocksByAddress.TryGetValue(jumpTargetAddress, out var existingTargetBlock))
|
||||||
|
{
|
||||||
|
jumpTargetBlock = existingTargetBlock;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We'll create this block later when we process the queue
|
||||||
|
// For now, just queue it for processing
|
||||||
|
addressQueue.Enqueue(jumpTargetAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the jump target block exists, add it as a successor to the current block
|
||||||
|
if (jumpTargetBlock != null)
|
||||||
|
{
|
||||||
|
newBlock.Successors.Add(jumpTargetBlock);
|
||||||
|
jumpTargetBlock.Predecessors.Add(newBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for return instruction (e.g., RET, RETF)
|
||||||
|
// Returns end a block without any successors
|
||||||
|
if (instruction.Type.IsRet())
|
||||||
|
{
|
||||||
|
// Register this block (it ends with a return)
|
||||||
|
var newBlock = RegisterBlock(blocks, address, instructions, currentBlock, false, false);
|
||||||
|
blocksByAddress[address] = newBlock;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since blocks aren't necessarily ordered (ASM can jump anywhere it likes)
|
||||||
|
// we need to sort the blocks ourselves
|
||||||
|
blocks.Sort((b1, b2) => b1.Address.CompareTo(b2.Address));
|
||||||
|
|
||||||
|
// First, establish the successor and predecessor relationships based on file offsets
|
||||||
|
// This is done by analyzing the last instruction of each block
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
if (block.Instructions.Count == 0) continue;
|
||||||
|
|
||||||
|
var lastInstruction = block.Instructions[^1];
|
||||||
|
|
||||||
|
// Check if the last instruction is a conditional jump
|
||||||
|
if (lastInstruction.Type.IsConditionalJump())
|
||||||
|
{
|
||||||
|
// Get the jump target address (file offset)
|
||||||
|
ulong targetAddress = 0;
|
||||||
|
if (lastInstruction.StructuredOperands.Count > 0 && lastInstruction.StructuredOperands[0] is RelativeOffsetOperand relOp)
|
||||||
|
{
|
||||||
|
targetAddress = relOp.TargetAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the target block
|
||||||
|
var targetBlock = blocks.FirstOrDefault(b => b.Address == targetAddress);
|
||||||
|
if (targetBlock != null)
|
||||||
|
{
|
||||||
|
// Add the target block as a successor to this block
|
||||||
|
if (!block.Successors.Contains(targetBlock))
|
||||||
|
{
|
||||||
|
block.Successors.Add(targetBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this block as a predecessor to the target block
|
||||||
|
if (!targetBlock.Predecessors.Contains(block))
|
||||||
|
{
|
||||||
|
targetBlock.Predecessors.Add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For conditional jumps, also add the fall-through block as a successor
|
||||||
|
// The fall-through block is the one that immediately follows this block in memory
|
||||||
|
// Find the next block in address order
|
||||||
|
var nextBlock = blocks.OrderBy(b => b.Address).FirstOrDefault(b => b.Address > block.Address);
|
||||||
|
if (nextBlock != null)
|
||||||
|
{
|
||||||
|
// The fall-through block is the one that immediately follows this block in memory
|
||||||
|
var fallThroughBlock = nextBlock;
|
||||||
|
|
||||||
|
// Add the fall-through block as a successor to this block
|
||||||
|
if (!block.Successors.Contains(fallThroughBlock))
|
||||||
|
{
|
||||||
|
block.Successors.Add(fallThroughBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this block as a predecessor to the fall-through block
|
||||||
|
if (!fallThroughBlock.Predecessors.Contains(block))
|
||||||
|
{
|
||||||
|
fallThroughBlock.Predecessors.Add(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the last instruction is an unconditional jump
|
||||||
|
else if (lastInstruction.Type == InstructionType.Jmp)
|
||||||
|
{
|
||||||
|
// Get the jump target address (file offset)
|
||||||
|
ulong targetAddress = 0;
|
||||||
|
if (lastInstruction.StructuredOperands.Count > 0 && lastInstruction.StructuredOperands[0] is RelativeOffsetOperand relOp)
|
||||||
|
{
|
||||||
|
targetAddress = relOp.TargetAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the target block
|
||||||
|
var targetBlock = blocks.FirstOrDefault(b => b.Address == targetAddress);
|
||||||
|
if (targetBlock != null)
|
||||||
|
{
|
||||||
|
// Add the target block as a successor to this block
|
||||||
|
if (!block.Successors.Contains(targetBlock))
|
||||||
|
{
|
||||||
|
block.Successors.Add(targetBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this block as a predecessor to the target block
|
||||||
|
if (!targetBlock.Predecessors.Contains(block))
|
||||||
|
{
|
||||||
|
targetBlock.Predecessors.Add(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For non-jump instructions that don't end the function (like Ret), add the fall-through block
|
||||||
|
else if (!lastInstruction.Type.IsRet())
|
||||||
|
{
|
||||||
|
// The fall-through block is the one that immediately follows this block in memory
|
||||||
|
// Find the next block in address order
|
||||||
|
var nextBlock = blocks.OrderBy(b => b.Address).FirstOrDefault(b => b.Address > block.Address);
|
||||||
|
if (nextBlock != null)
|
||||||
|
{
|
||||||
|
// The fall-through block is the one that immediately follows this block in memory
|
||||||
|
var fallThroughBlock = nextBlock;
|
||||||
|
|
||||||
|
// Add the fall-through block as a successor to this block
|
||||||
|
if (!block.Successors.Contains(fallThroughBlock))
|
||||||
|
{
|
||||||
|
block.Successors.Add(fallThroughBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this block as a predecessor to the fall-through block
|
||||||
|
if (!fallThroughBlock.Predecessors.Contains(block))
|
||||||
|
{
|
||||||
|
fallThroughBlock.Predecessors.Add(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the original file offset for each block in a dictionary
|
||||||
|
Dictionary<InstructionBlock, ulong> blockToFileOffset = new Dictionary<InstructionBlock, ulong>();
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
blockToFileOffset[block] = block.Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert all block addresses from file offsets to RVA
|
||||||
|
// and update the block dictionary for quick lookup
|
||||||
|
Dictionary<ulong, InstructionBlock> rvaBlocksByAddress = new Dictionary<ulong, InstructionBlock>();
|
||||||
|
Dictionary<ulong, ulong> fileOffsetToRvaMap = new Dictionary<ulong, ulong>();
|
||||||
|
|
||||||
|
// First pass: create a mapping from file offset to RVA for each block
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
// Get the original file offset address
|
||||||
|
ulong blockFileOffset = block.Address;
|
||||||
|
|
||||||
|
// Calculate the RVA address
|
||||||
|
ulong blockRvaAddress = blockFileOffset + _baseAddress;
|
||||||
|
|
||||||
|
// Store the mapping
|
||||||
|
fileOffsetToRvaMap[blockFileOffset] = blockRvaAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass: update all blocks to use RVA addresses
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
// Get the original file offset address
|
||||||
|
ulong blockFileOffset = block.Address;
|
||||||
|
|
||||||
|
// Update the block's address to RVA
|
||||||
|
ulong blockRvaAddress = fileOffsetToRvaMap[blockFileOffset];
|
||||||
|
block.Address = blockRvaAddress;
|
||||||
|
|
||||||
|
// Add to the dictionary for quick lookup
|
||||||
|
rvaBlocksByAddress[blockRvaAddress] = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now update all successors and predecessors to use the correct RVA addresses
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
// Create new lists for successors and predecessors with the correct RVA addresses
|
||||||
|
List<InstructionBlock> updatedSuccessors = new List<InstructionBlock>();
|
||||||
|
List<InstructionBlock> updatedPredecessors = new List<InstructionBlock>();
|
||||||
|
|
||||||
|
// Update successors
|
||||||
|
foreach (var successor in block.Successors)
|
||||||
|
{
|
||||||
|
// Get the original file offset of the successor
|
||||||
|
if (blockToFileOffset.TryGetValue(successor, out ulong successorFileOffset))
|
||||||
|
{
|
||||||
|
// Look up the RVA address in our mapping
|
||||||
|
if (fileOffsetToRvaMap.TryGetValue(successorFileOffset, out ulong successorRvaAddress))
|
||||||
|
{
|
||||||
|
// Find the block with this RVA address
|
||||||
|
if (rvaBlocksByAddress.TryGetValue(successorRvaAddress, out var rvaSuccessor))
|
||||||
|
{
|
||||||
|
updatedSuccessors.Add(rvaSuccessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update predecessors
|
||||||
|
foreach (var predecessor in block.Predecessors)
|
||||||
|
{
|
||||||
|
// Get the original file offset of the predecessor
|
||||||
|
if (blockToFileOffset.TryGetValue(predecessor, out ulong predecessorFileOffset))
|
||||||
|
{
|
||||||
|
// Look up the RVA address in our mapping
|
||||||
|
if (fileOffsetToRvaMap.TryGetValue(predecessorFileOffset, out ulong predecessorRvaAddress))
|
||||||
|
{
|
||||||
|
// Find the block with this RVA address
|
||||||
|
if (rvaBlocksByAddress.TryGetValue(predecessorRvaAddress, out var rvaPredecessor))
|
||||||
|
{
|
||||||
|
updatedPredecessors.Add(rvaPredecessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the old lists with the updated ones
|
||||||
|
block.Successors = updatedSuccessors;
|
||||||
|
block.Predecessors = updatedPredecessors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new AsmFunction with the RVA address
|
||||||
|
var asmFunction = new AsmFunction()
|
||||||
|
{
|
||||||
|
Address = entryPointRVA,
|
||||||
|
Blocks = blocks,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify that the entry block exists (no need to log this information)
|
||||||
|
|
||||||
|
return asmFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and registers a new instruction block in the blocks collection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="blocks">The list of blocks to add to</param>
|
||||||
|
/// <param name="address">The starting address of the block</param>
|
||||||
|
/// <param name="instructions">The instructions contained in the block</param>
|
||||||
|
/// <param name="currentBlock">The current block being processed (null if this is the first block)</param>
|
||||||
|
/// <param name="isJumpTarget">Whether this block is a jump target</param>
|
||||||
|
/// <param name="isFallThrough">Whether this block is a fall-through from another block</param>
|
||||||
|
/// <returns>The newly created block</returns>
|
||||||
|
public InstructionBlock RegisterBlock(
|
||||||
|
List<InstructionBlock> blocks,
|
||||||
|
ulong address,
|
||||||
|
List<Instruction> instructions,
|
||||||
|
InstructionBlock? currentBlock = null,
|
||||||
|
bool isJumpTarget = false,
|
||||||
|
bool isFallThrough = false)
|
||||||
|
{
|
||||||
|
// Check if a block already exists at this address
|
||||||
|
var existingBlock = blocks.FirstOrDefault(b => b.Address == address);
|
||||||
|
|
||||||
|
if (existingBlock != null)
|
||||||
|
{
|
||||||
|
// If the current block is not null, update the relationships
|
||||||
|
if (currentBlock != null)
|
||||||
|
{
|
||||||
|
// Add the existing block as a successor to the current block if not already present
|
||||||
|
if (!currentBlock.Successors.Contains(existingBlock))
|
||||||
|
{
|
||||||
|
currentBlock.Successors.Add(existingBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current block as a predecessor to the existing block if not already present
|
||||||
|
if (!existingBlock.Predecessors.Contains(currentBlock))
|
||||||
|
{
|
||||||
|
existingBlock.Predecessors.Add(currentBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new block with the provided address and instructions
|
||||||
|
var block = new InstructionBlock()
|
||||||
|
{
|
||||||
|
Address = address,
|
||||||
|
Instructions = new List<Instruction>(instructions) // Create a copy of the instructions list
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the block to the collection
|
||||||
|
blocks.Add(block);
|
||||||
|
|
||||||
|
// If the current block is not null, update the relationships
|
||||||
|
if (currentBlock != null)
|
||||||
|
{
|
||||||
|
// Add the new block as a successor to the current block
|
||||||
|
currentBlock.Successors.Add(block);
|
||||||
|
|
||||||
|
// Add the current block as a predecessor to the new block
|
||||||
|
block.Predecessors.Add(currentBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a basic block of instructions with a single entry and exit point
|
||||||
|
/// </summary>
|
||||||
|
public class InstructionBlock
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The starting address of the block
|
||||||
|
/// </summary>
|
||||||
|
public ulong Address { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of instructions contained in this block
|
||||||
|
/// </summary>
|
||||||
|
public List<Instruction> Instructions { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The blocks that can transfer control to this block
|
||||||
|
/// </summary>
|
||||||
|
public List<InstructionBlock> Predecessors { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The blocks that this block can transfer control to
|
||||||
|
/// </summary>
|
||||||
|
public List<InstructionBlock> Successors { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a string representation of the block, including its address, instructions, and control flow information
|
||||||
|
/// </summary>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
// Create a string for predecessors
|
||||||
|
string predecessorsStr = Predecessors.Count > 0
|
||||||
|
? $"Predecessors: {string.Join(", ", Predecessors.Select(p => $"0x{p.Address:X8}"))}"
|
||||||
|
: "No predecessors";
|
||||||
|
|
||||||
|
// Create a string for successors
|
||||||
|
string successorsStr = Successors.Count > 0
|
||||||
|
? $"Successors: {string.Join(", ", Successors.Select(s => $"0x{s.Address:X8}"))}"
|
||||||
|
: "No successors";
|
||||||
|
|
||||||
|
// Return the complete string representation
|
||||||
|
return $"Address: 0x{Address:X8}\n{predecessorsStr}\n{successorsStr}\n{string.Join("\n", Instructions)}";
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/Analysers/FileAbsoluteAddress.cs
Normal file
56
X86Disassembler/Analysers/FileAbsoluteAddress.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
namespace X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
public abstract class Address(ulong value, ulong imageBase)
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The actual value of the address, not specifically typed.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly ulong Value = value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PE.ImageBase from which this address is constructed
|
||||||
|
/// </summary>
|
||||||
|
protected readonly ulong ImageBase = imageBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Absolute address in the PE file
|
||||||
|
/// </summary>
|
||||||
|
public class FileAbsoluteAddress(ulong value, ulong imageBase) : Address(value, imageBase)
|
||||||
|
{
|
||||||
|
public ulong GetValue()
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual VirtualAddress AsImageBaseAddress()
|
||||||
|
{
|
||||||
|
return new VirtualAddress(Value + ImageBase, ImageBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual FileAbsoluteAddress AsFileAbsolute()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Address from PE.ImageBase
|
||||||
|
/// </summary>
|
||||||
|
public class VirtualAddress : FileAbsoluteAddress
|
||||||
|
{
|
||||||
|
public VirtualAddress(ulong value, ulong imageBase) : base(value, imageBase)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override VirtualAddress AsImageBaseAddress()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override FileAbsoluteAddress AsFileAbsolute()
|
||||||
|
{
|
||||||
|
return new FileAbsoluteAddress(Value - ImageBase, ImageBase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
40
X86Disassembler/Analysers/InstructionTypeExtensions.cs
Normal file
40
X86Disassembler/Analysers/InstructionTypeExtensions.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using X86Disassembler.X86;
|
||||||
|
|
||||||
|
namespace X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
public static class InstructionTypeExtensions
|
||||||
|
{
|
||||||
|
public static bool IsConditionalJump(this InstructionType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
InstructionType.Jg => true,
|
||||||
|
InstructionType.Jge => true,
|
||||||
|
InstructionType.Jl => true,
|
||||||
|
InstructionType.Jle => true,
|
||||||
|
InstructionType.Ja => true,
|
||||||
|
InstructionType.Jae => true,
|
||||||
|
InstructionType.Jb => true,
|
||||||
|
InstructionType.Jbe => true,
|
||||||
|
InstructionType.Jz => true,
|
||||||
|
InstructionType.Jnz => true,
|
||||||
|
InstructionType.Jo => true,
|
||||||
|
InstructionType.Jno => true,
|
||||||
|
InstructionType.Js => true,
|
||||||
|
InstructionType.Jns => true,
|
||||||
|
InstructionType.Jp => true,
|
||||||
|
InstructionType.Jnp => true,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsRegularJump(this InstructionType type)
|
||||||
|
{
|
||||||
|
return type == InstructionType.Jmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsRet(this InstructionType type)
|
||||||
|
{
|
||||||
|
return type is InstructionType.Ret or InstructionType.Retf;
|
||||||
|
}
|
||||||
|
}
|
16
X86Disassembler/Analysers/OperandExtensions.cs
Normal file
16
X86Disassembler/Analysers/OperandExtensions.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using X86Disassembler.X86;
|
||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
public static class OperandExtensions
|
||||||
|
{
|
||||||
|
public static uint GetValue(this Operand operand)
|
||||||
|
{
|
||||||
|
return operand switch
|
||||||
|
{
|
||||||
|
RelativeOffsetOperand roo => roo.TargetAddress,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
60
X86Disassembler/PE/PEUtility.cs
Normal file
60
X86Disassembler/PE/PEUtility.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class for PE format operations
|
||||||
|
/// </summary>
|
||||||
|
public class PEUtility
|
||||||
|
{
|
||||||
|
private readonly List<SectionHeader> _sectionHeaders;
|
||||||
|
private readonly uint _sizeOfHeaders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a new instance of the PEUtility class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sectionHeaders">The section headers</param>
|
||||||
|
/// <param name="sizeOfHeaders">The size of the headers</param>
|
||||||
|
public PEUtility(List<SectionHeader> sectionHeaders, uint sizeOfHeaders)
|
||||||
|
{
|
||||||
|
_sectionHeaders = sectionHeaders;
|
||||||
|
_sizeOfHeaders = sizeOfHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a Relative Virtual Address (RVA) to a file offset
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rva">The RVA to convert</param>
|
||||||
|
/// <returns>The corresponding file offset</returns>
|
||||||
|
public uint RvaToOffset(uint rva)
|
||||||
|
{
|
||||||
|
if (rva == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var section in _sectionHeaders)
|
||||||
|
{
|
||||||
|
// Check if the RVA is within this section
|
||||||
|
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
|
||||||
|
{
|
||||||
|
// Calculate the offset within the section
|
||||||
|
uint offsetInSection = rva - section.VirtualAddress;
|
||||||
|
|
||||||
|
// Make sure we don't exceed the raw data size
|
||||||
|
if (offsetInSection < section.SizeOfRawData)
|
||||||
|
{
|
||||||
|
return section.PointerToRawData + offsetInSection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the RVA is not within any section, it might be in the headers
|
||||||
|
if (rva < _sizeOfHeaders)
|
||||||
|
{
|
||||||
|
return rva;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException($"RVA {rva:X8} is not within any section");
|
||||||
|
}
|
||||||
|
}
|
56
X86Disassembler/PE/Parsers/DOSHeaderParser.cs
Normal file
56
X86Disassembler/PE/Parsers/DOSHeaderParser.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for the DOS header of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class DOSHeaderParser : IParser<DOSHeader>
|
||||||
|
{
|
||||||
|
// DOS Header constants
|
||||||
|
private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ'
|
||||||
|
|
||||||
|
public DOSHeader Parse(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var header = new DOSHeader();
|
||||||
|
|
||||||
|
header.e_magic = reader.ReadUInt16();
|
||||||
|
if (header.e_magic != DOS_SIGNATURE)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Invalid DOS signature (MZ)");
|
||||||
|
}
|
||||||
|
|
||||||
|
header.e_cblp = reader.ReadUInt16();
|
||||||
|
header.e_cp = reader.ReadUInt16();
|
||||||
|
header.e_crlc = reader.ReadUInt16();
|
||||||
|
header.e_cparhdr = reader.ReadUInt16();
|
||||||
|
header.e_minalloc = reader.ReadUInt16();
|
||||||
|
header.e_maxalloc = reader.ReadUInt16();
|
||||||
|
header.e_ss = reader.ReadUInt16();
|
||||||
|
header.e_sp = reader.ReadUInt16();
|
||||||
|
header.e_csum = reader.ReadUInt16();
|
||||||
|
header.e_ip = reader.ReadUInt16();
|
||||||
|
header.e_cs = reader.ReadUInt16();
|
||||||
|
header.e_lfarlc = reader.ReadUInt16();
|
||||||
|
header.e_ovno = reader.ReadUInt16();
|
||||||
|
|
||||||
|
header.e_res = new ushort[4];
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
header.e_res[i] = reader.ReadUInt16();
|
||||||
|
}
|
||||||
|
|
||||||
|
header.e_oemid = reader.ReadUInt16();
|
||||||
|
header.e_oeminfo = reader.ReadUInt16();
|
||||||
|
|
||||||
|
header.e_res2 = new ushort[10];
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
header.e_res2[i] = reader.ReadUInt16();
|
||||||
|
}
|
||||||
|
|
||||||
|
header.e_lfanew = reader.ReadUInt32();
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
161
X86Disassembler/PE/Parsers/ExportDirectoryParser.cs
Normal file
161
X86Disassembler/PE/Parsers/ExportDirectoryParser.cs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
using System.Text;
|
||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for the Export Directory of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class ExportDirectoryParser
|
||||||
|
{
|
||||||
|
private readonly PEUtility _utility;
|
||||||
|
|
||||||
|
public ExportDirectoryParser(PEUtility utility)
|
||||||
|
{
|
||||||
|
_utility = utility;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the Export Directory from the binary reader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader</param>
|
||||||
|
/// <param name="rva">The RVA of the Export Directory</param>
|
||||||
|
/// <returns>The parsed Export Directory</returns>
|
||||||
|
public ExportDirectory Parse(BinaryReader reader, uint rva)
|
||||||
|
{
|
||||||
|
ExportDirectory directory = new ExportDirectory();
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(_utility.RvaToOffset(rva), SeekOrigin.Begin);
|
||||||
|
|
||||||
|
directory.Characteristics = reader.ReadUInt32();
|
||||||
|
directory.TimeDateStamp = reader.ReadUInt32();
|
||||||
|
directory.MajorVersion = reader.ReadUInt16();
|
||||||
|
directory.MinorVersion = reader.ReadUInt16();
|
||||||
|
directory.DllNameRva = reader.ReadUInt32();
|
||||||
|
directory.Base = reader.ReadUInt32();
|
||||||
|
directory.NumberOfFunctions = reader.ReadUInt32();
|
||||||
|
directory.NumberOfNames = reader.ReadUInt32();
|
||||||
|
directory.AddressOfFunctions = reader.ReadUInt32();
|
||||||
|
directory.AddressOfNames = reader.ReadUInt32();
|
||||||
|
directory.AddressOfNameOrdinals = reader.ReadUInt32();
|
||||||
|
|
||||||
|
uint dllNameOffset = _utility.RvaToOffset(directory.DllNameRva);
|
||||||
|
reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Read the null-terminated ASCII string
|
||||||
|
var nameBuilder = new StringBuilder();
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
while ((b = reader.ReadByte()) != 0)
|
||||||
|
{
|
||||||
|
nameBuilder.Append((char) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
directory.DllName = nameBuilder.ToString();
|
||||||
|
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the exported functions using the export directory information
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader</param>
|
||||||
|
/// <param name="directory">The Export Directory</param>
|
||||||
|
/// <param name="exportDirRva">The RVA of the Export Directory</param>
|
||||||
|
/// <param name="exportDirSize">The size of the Export Directory</param>
|
||||||
|
/// <returns>List of exported functions</returns>
|
||||||
|
public List<ExportedFunction> ParseExportedFunctions(BinaryReader reader, ExportDirectory directory, uint exportDirRva, uint exportDirSize)
|
||||||
|
{
|
||||||
|
List<ExportedFunction> exportedFunctions = new List<ExportedFunction>();
|
||||||
|
|
||||||
|
// Read the array of function addresses (RVAs)
|
||||||
|
uint[] functionRVAs = new uint[directory.NumberOfFunctions];
|
||||||
|
reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfFunctions), SeekOrigin.Begin);
|
||||||
|
for (int i = 0; i < directory.NumberOfFunctions; i++)
|
||||||
|
{
|
||||||
|
functionRVAs[i] = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the array of name RVAs
|
||||||
|
uint[] nameRVAs = new uint[directory.NumberOfNames];
|
||||||
|
reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNames), SeekOrigin.Begin);
|
||||||
|
for (int i = 0; i < directory.NumberOfNames; i++)
|
||||||
|
{
|
||||||
|
nameRVAs[i] = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the array of name ordinals
|
||||||
|
ushort[] nameOrdinals = new ushort[directory.NumberOfNames];
|
||||||
|
reader.BaseStream.Seek(_utility.RvaToOffset(directory.AddressOfNameOrdinals), SeekOrigin.Begin);
|
||||||
|
for (int i = 0; i < directory.NumberOfNames; i++)
|
||||||
|
{
|
||||||
|
nameOrdinals[i] = reader.ReadUInt16();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a dictionary to map ordinals to names
|
||||||
|
Dictionary<ushort, string> ordinalToName = new Dictionary<ushort, string>();
|
||||||
|
for (int i = 0; i < directory.NumberOfNames; i++)
|
||||||
|
{
|
||||||
|
// Read the function name
|
||||||
|
reader.BaseStream.Seek(_utility.RvaToOffset(nameRVAs[i]), SeekOrigin.Begin);
|
||||||
|
var nameBuilder = new StringBuilder();
|
||||||
|
byte b;
|
||||||
|
while ((b = reader.ReadByte()) != 0)
|
||||||
|
{
|
||||||
|
nameBuilder.Append((char) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = nameBuilder.ToString();
|
||||||
|
|
||||||
|
// Map the ordinal to the name
|
||||||
|
ordinalToName[nameOrdinals[i]] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the exported functions
|
||||||
|
for (ushort i = 0; i < directory.NumberOfFunctions; i++)
|
||||||
|
{
|
||||||
|
uint functionRVA = functionRVAs[i];
|
||||||
|
if (functionRVA == 0)
|
||||||
|
{
|
||||||
|
continue; // Skip empty entries
|
||||||
|
}
|
||||||
|
|
||||||
|
ExportedFunction function = new ExportedFunction();
|
||||||
|
function.Ordinal = (ushort) (i + directory.Base);
|
||||||
|
function.AddressRva = functionRVA;
|
||||||
|
|
||||||
|
// Check if this function has a name
|
||||||
|
if (ordinalToName.TryGetValue(i, out string? name))
|
||||||
|
{
|
||||||
|
function.Name = name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
function.Name = $"Ordinal_{function.Ordinal}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a forwarder
|
||||||
|
uint exportDirEnd = exportDirRva + exportDirSize;
|
||||||
|
|
||||||
|
if (functionRVA >= exportDirRva && functionRVA < exportDirEnd)
|
||||||
|
{
|
||||||
|
function.IsForwarder = true;
|
||||||
|
|
||||||
|
// Read the forwarder string
|
||||||
|
reader.BaseStream.Seek(_utility.RvaToOffset(functionRVA), SeekOrigin.Begin);
|
||||||
|
var forwarderBuilder = new StringBuilder();
|
||||||
|
byte b;
|
||||||
|
while ((b = reader.ReadByte()) != 0)
|
||||||
|
{
|
||||||
|
forwarderBuilder.Append((char) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function.ForwarderName = forwarderBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
exportedFunctions.Add(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exportedFunctions;
|
||||||
|
}
|
||||||
|
}
|
29
X86Disassembler/PE/Parsers/FileHeaderParser.cs
Normal file
29
X86Disassembler/PE/Parsers/FileHeaderParser.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for the File header of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class FileHeaderParser : IParser<FileHeader>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the File header from the binary reader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader positioned at the start of the File header</param>
|
||||||
|
/// <returns>The parsed File header</returns>
|
||||||
|
public FileHeader Parse(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var header = new FileHeader();
|
||||||
|
|
||||||
|
header.Machine = reader.ReadUInt16();
|
||||||
|
header.NumberOfSections = reader.ReadUInt16();
|
||||||
|
header.TimeDateStamp = reader.ReadUInt32();
|
||||||
|
header.PointerToSymbolTable = reader.ReadUInt32();
|
||||||
|
header.NumberOfSymbols = reader.ReadUInt32();
|
||||||
|
header.SizeOfOptionalHeader = reader.ReadUInt16();
|
||||||
|
header.Characteristics = reader.ReadUInt16();
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
15
X86Disassembler/PE/Parsers/IParser.cs
Normal file
15
X86Disassembler/PE/Parsers/IParser.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for PE format component parsers
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of component to parse</typeparam>
|
||||||
|
public interface IParser<out T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a component from the binary reader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader positioned at the start of the component</param>
|
||||||
|
/// <returns>The parsed component</returns>
|
||||||
|
T Parse(BinaryReader reader);
|
||||||
|
}
|
162
X86Disassembler/PE/Parsers/ImportDescriptorParser.cs
Normal file
162
X86Disassembler/PE/Parsers/ImportDescriptorParser.cs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
using System.Text;
|
||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for Import Descriptors in a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class ImportDescriptorParser
|
||||||
|
{
|
||||||
|
private readonly PEUtility _utility;
|
||||||
|
|
||||||
|
public ImportDescriptorParser(PEUtility utility)
|
||||||
|
{
|
||||||
|
_utility = utility;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the Import Descriptors from the binary reader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader</param>
|
||||||
|
/// <param name="rva">The RVA of the Import Directory</param>
|
||||||
|
/// <returns>List of Import Descriptors</returns>
|
||||||
|
public List<ImportDescriptor> Parse(BinaryReader reader, uint rva)
|
||||||
|
{
|
||||||
|
var descriptors = new List<ImportDescriptor>();
|
||||||
|
|
||||||
|
uint importTableOffset = _utility.RvaToOffset(rva);
|
||||||
|
reader.BaseStream.Seek(importTableOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
int descriptorCount = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
descriptorCount++;
|
||||||
|
|
||||||
|
// Read the import descriptor
|
||||||
|
uint originalFirstThunk = reader.ReadUInt32();
|
||||||
|
uint timeDateStamp = reader.ReadUInt32();
|
||||||
|
uint forwarderChain = reader.ReadUInt32();
|
||||||
|
uint nameRva = reader.ReadUInt32();
|
||||||
|
uint firstThunk = reader.ReadUInt32();
|
||||||
|
|
||||||
|
// Check if we've reached the end of the import descriptors
|
||||||
|
if (originalFirstThunk == 0 && nameRva == 0 && firstThunk == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportDescriptor descriptor = new ImportDescriptor
|
||||||
|
{
|
||||||
|
OriginalFirstThunkRva = originalFirstThunk,
|
||||||
|
TimeDateStamp = timeDateStamp,
|
||||||
|
ForwarderChain = forwarderChain,
|
||||||
|
DllNameRva = nameRva,
|
||||||
|
FirstThunkRva = firstThunk,
|
||||||
|
DllName = "Unknown"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (nameRva != 0)
|
||||||
|
{
|
||||||
|
uint nameOffset = _utility.RvaToOffset(nameRva);
|
||||||
|
reader.BaseStream.Seek(nameOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Read the null-terminated ASCII string
|
||||||
|
StringBuilder nameBuilder = new StringBuilder();
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
while ((b = reader.ReadByte()) != 0)
|
||||||
|
{
|
||||||
|
nameBuilder.Append((char) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.DllName = nameBuilder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the imported functions
|
||||||
|
ParseImportedFunctions(reader, descriptor);
|
||||||
|
|
||||||
|
descriptors.Add(descriptor);
|
||||||
|
|
||||||
|
// Return to the import table to read the next descriptor
|
||||||
|
reader.BaseStream.Seek(importTableOffset + (descriptorCount * 20), SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the imported functions for a given import descriptor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader</param>
|
||||||
|
/// <param name="descriptor">The Import Descriptor</param>
|
||||||
|
private void ParseImportedFunctions(BinaryReader reader, ImportDescriptor descriptor)
|
||||||
|
{
|
||||||
|
// Use OriginalFirstThunk if available, otherwise use FirstThunk
|
||||||
|
uint thunkRva = descriptor.OriginalFirstThunkRva != 0
|
||||||
|
? descriptor.OriginalFirstThunkRva
|
||||||
|
: descriptor.FirstThunkRva;
|
||||||
|
|
||||||
|
if (thunkRva == 0)
|
||||||
|
{
|
||||||
|
return; // No functions to parse
|
||||||
|
}
|
||||||
|
|
||||||
|
uint thunkOffset = _utility.RvaToOffset(thunkRva);
|
||||||
|
int functionCount = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Seek(thunkOffset + (functionCount * 4), SeekOrigin.Begin);
|
||||||
|
uint thunkData = reader.ReadUInt32();
|
||||||
|
|
||||||
|
if (thunkData == 0)
|
||||||
|
{
|
||||||
|
break; // End of the function list
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportedFunction function = new ImportedFunction
|
||||||
|
{
|
||||||
|
ThunkRva = thunkRva + (uint) (functionCount * 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if imported by ordinal (high bit set)
|
||||||
|
if ((thunkData & 0x80000000) != 0)
|
||||||
|
{
|
||||||
|
function.IsOrdinal = true;
|
||||||
|
function.Ordinal = (ushort) (thunkData & 0xFFFF);
|
||||||
|
function.Name = $"Ordinal {function.Ordinal}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Imported by name - the thunkData is an RVA to a hint/name structure
|
||||||
|
|
||||||
|
uint hintNameOffset = _utility.RvaToOffset(thunkData);
|
||||||
|
reader.BaseStream.Seek(hintNameOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Read the hint (2 bytes)
|
||||||
|
function.Hint = reader.ReadUInt16();
|
||||||
|
|
||||||
|
// Read the function name (null-terminated ASCII string)
|
||||||
|
StringBuilder nameBuilder = new StringBuilder();
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
while ((b = reader.ReadByte()) != 0)
|
||||||
|
{
|
||||||
|
nameBuilder.Append((char) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function.Name = nameBuilder.ToString();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(function.Name))
|
||||||
|
{
|
||||||
|
function.Name = $"Function_at_{thunkData:X8}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.Functions.Add(function);
|
||||||
|
functionCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
X86Disassembler/PE/Parsers/OptionalHeaderParser.cs
Normal file
95
X86Disassembler/PE/Parsers/OptionalHeaderParser.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for the Optional header of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class OptionalHeaderParser : IParser<OptionalHeader>
|
||||||
|
{
|
||||||
|
// Optional Header Magic values
|
||||||
|
private const ushort PE32_MAGIC = 0x10B; // 32-bit executable
|
||||||
|
private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse the Optional header from the binary reader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader positioned at the start of the Optional header</param>
|
||||||
|
/// <returns>The parsed Optional header</returns>
|
||||||
|
public OptionalHeader Parse(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var header = new OptionalHeader();
|
||||||
|
|
||||||
|
// Standard fields
|
||||||
|
header.Magic = reader.ReadUInt16();
|
||||||
|
|
||||||
|
// Determine if this is a PE32 or PE32+ file
|
||||||
|
var is64Bit = header.Magic == PE32PLUS_MAGIC;
|
||||||
|
|
||||||
|
header.MajorLinkerVersion = reader.ReadByte();
|
||||||
|
header.MinorLinkerVersion = reader.ReadByte();
|
||||||
|
header.SizeOfCode = reader.ReadUInt32();
|
||||||
|
header.SizeOfInitializedData = reader.ReadUInt32();
|
||||||
|
header.SizeOfUninitializedData = reader.ReadUInt32();
|
||||||
|
header.AddressOfEntryPoint = reader.ReadUInt32();
|
||||||
|
header.BaseOfCode = reader.ReadUInt32();
|
||||||
|
|
||||||
|
// PE32 has BaseOfData, PE32+ doesn't
|
||||||
|
if (!is64Bit)
|
||||||
|
{
|
||||||
|
header.BaseOfData = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows-specific fields
|
||||||
|
header.ImageBase = is64Bit
|
||||||
|
? reader.ReadUInt64()
|
||||||
|
: reader.ReadUInt32();
|
||||||
|
|
||||||
|
header.SectionAlignment = reader.ReadUInt32();
|
||||||
|
header.FileAlignment = reader.ReadUInt32();
|
||||||
|
header.MajorOperatingSystemVersion = reader.ReadUInt16();
|
||||||
|
header.MinorOperatingSystemVersion = reader.ReadUInt16();
|
||||||
|
header.MajorImageVersion = reader.ReadUInt16();
|
||||||
|
header.MinorImageVersion = reader.ReadUInt16();
|
||||||
|
header.MajorSubsystemVersion = reader.ReadUInt16();
|
||||||
|
header.MinorSubsystemVersion = reader.ReadUInt16();
|
||||||
|
header.Win32VersionValue = reader.ReadUInt32();
|
||||||
|
header.SizeOfImage = reader.ReadUInt32();
|
||||||
|
header.SizeOfHeaders = reader.ReadUInt32();
|
||||||
|
header.CheckSum = reader.ReadUInt32();
|
||||||
|
header.Subsystem = reader.ReadUInt16();
|
||||||
|
header.DllCharacteristics = reader.ReadUInt16();
|
||||||
|
|
||||||
|
// Size fields differ between PE32 and PE32+
|
||||||
|
if (is64Bit)
|
||||||
|
{
|
||||||
|
header.SizeOfStackReserve = reader.ReadUInt64();
|
||||||
|
header.SizeOfStackCommit = reader.ReadUInt64();
|
||||||
|
header.SizeOfHeapReserve = reader.ReadUInt64();
|
||||||
|
header.SizeOfHeapCommit = reader.ReadUInt64();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
header.SizeOfStackReserve = reader.ReadUInt32();
|
||||||
|
header.SizeOfStackCommit = reader.ReadUInt32();
|
||||||
|
header.SizeOfHeapReserve = reader.ReadUInt32();
|
||||||
|
header.SizeOfHeapCommit = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
header.LoaderFlags = reader.ReadUInt32();
|
||||||
|
header.NumberOfRvaAndSizes = reader.ReadUInt32();
|
||||||
|
|
||||||
|
// Data directories
|
||||||
|
header.DataDirectories = new DataDirectory[header.NumberOfRvaAndSizes];
|
||||||
|
|
||||||
|
for (int i = 0; i < header.NumberOfRvaAndSizes; i++)
|
||||||
|
{
|
||||||
|
var dir = new DataDirectory();
|
||||||
|
dir.VirtualAddress = reader.ReadUInt32();
|
||||||
|
dir.Size = reader.ReadUInt32();
|
||||||
|
header.DataDirectories[i] = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
38
X86Disassembler/PE/Parsers/SectionHeaderParser.cs
Normal file
38
X86Disassembler/PE/Parsers/SectionHeaderParser.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Text;
|
||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE.Parsers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parser for section headers in a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class SectionHeaderParser : IParser<SectionHeader>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a section header from the binary reader
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The binary reader positioned at the start of the section header</param>
|
||||||
|
/// <returns>The parsed section header</returns>
|
||||||
|
public SectionHeader Parse(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var header = new SectionHeader();
|
||||||
|
|
||||||
|
// Read section name (8 bytes)
|
||||||
|
var nameBytes = reader.ReadBytes(8);
|
||||||
|
// Convert to string, removing any null characters
|
||||||
|
header.Name = Encoding.ASCII.GetString(nameBytes)
|
||||||
|
.TrimEnd('\0');
|
||||||
|
|
||||||
|
header.VirtualSize = reader.ReadUInt32();
|
||||||
|
header.VirtualAddress = reader.ReadUInt32();
|
||||||
|
header.SizeOfRawData = reader.ReadUInt32();
|
||||||
|
header.PointerToRawData = reader.ReadUInt32();
|
||||||
|
header.PointerToRelocations = reader.ReadUInt32();
|
||||||
|
header.PointerToLinenumbers = reader.ReadUInt32();
|
||||||
|
header.NumberOfRelocations = reader.ReadUInt16();
|
||||||
|
header.NumberOfLinenumbers = reader.ReadUInt16();
|
||||||
|
header.Characteristics = reader.ReadUInt32();
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
}
|
158
X86Disassembler/PE/PeFile.cs
Normal file
158
X86Disassembler/PE/PeFile.cs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
using X86Disassembler.PE.Parsers;
|
||||||
|
using X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
namespace X86Disassembler.PE;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a Portable Executable (PE) file format parser
|
||||||
|
/// </summary>
|
||||||
|
public class PeFile
|
||||||
|
{
|
||||||
|
// DOS Header constants
|
||||||
|
private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ'
|
||||||
|
private const uint PE_SIGNATURE = 0x00004550; // 'PE\0\0'
|
||||||
|
|
||||||
|
// Optional Header Magic values
|
||||||
|
private const ushort PE32_MAGIC = 0x10B; // 32-bit executable
|
||||||
|
private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable
|
||||||
|
|
||||||
|
// Section characteristics flags
|
||||||
|
private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code
|
||||||
|
private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable
|
||||||
|
private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable
|
||||||
|
private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable
|
||||||
|
|
||||||
|
// Data directories
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0; // Export Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1; // Import Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; // Resource Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; // Exception Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_SECURITY = 4; // Security Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; // Base Relocation Table
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_DEBUG = 6; // Debug Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7; // Architecture Specific Data
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; // RVA of GP
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_TLS = 9; // TLS Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; // Load Configuration Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; // Bound Import Directory
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_IAT = 12; // Import Address Table
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13; // Delay Load Import Descriptors
|
||||||
|
private const int IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14; // COM Runtime descriptor
|
||||||
|
|
||||||
|
// PE file data
|
||||||
|
private byte[] _fileData;
|
||||||
|
|
||||||
|
// Parser instances
|
||||||
|
private readonly DOSHeaderParser _dosHeaderParser;
|
||||||
|
private readonly FileHeaderParser _fileHeaderParser;
|
||||||
|
private readonly OptionalHeaderParser _optionalHeaderParser;
|
||||||
|
private readonly SectionHeaderParser _sectionHeaderParser;
|
||||||
|
private PEUtility _peUtility;
|
||||||
|
private ExportDirectoryParser _exportDirectoryParser;
|
||||||
|
private ImportDescriptorParser _importDescriptorParser;
|
||||||
|
|
||||||
|
// Parsed headers
|
||||||
|
public DOSHeader DosHeader { get; private set; }
|
||||||
|
public FileHeader FileHeader { get; private set; }
|
||||||
|
public OptionalHeader OptionalHeader { get; private set; }
|
||||||
|
public List<SectionHeader> SectionHeaders { get; private set; }
|
||||||
|
public bool Is64Bit { get; private set; }
|
||||||
|
|
||||||
|
// Export and Import information
|
||||||
|
public ExportDirectory ExportDirectory { get; private set; }
|
||||||
|
public List<ExportedFunction> ExportedFunctions { get; private set; }
|
||||||
|
public List<ImportDescriptor> ImportDescriptors { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the PEFormat class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileData">The raw file data</param>
|
||||||
|
public PeFile(byte[] fileData)
|
||||||
|
{
|
||||||
|
_fileData = fileData;
|
||||||
|
SectionHeaders = [];
|
||||||
|
ExportedFunctions = [];
|
||||||
|
ImportDescriptors = [];
|
||||||
|
|
||||||
|
// Initialize parsers
|
||||||
|
_dosHeaderParser = new DOSHeaderParser();
|
||||||
|
_fileHeaderParser = new FileHeaderParser();
|
||||||
|
_optionalHeaderParser = new OptionalHeaderParser();
|
||||||
|
_sectionHeaderParser = new SectionHeaderParser();
|
||||||
|
|
||||||
|
// Initialize properties to avoid nullability warnings
|
||||||
|
DosHeader = new DOSHeader();
|
||||||
|
FileHeader = new FileHeader();
|
||||||
|
OptionalHeader = new OptionalHeader();
|
||||||
|
ExportDirectory = new ExportDirectory();
|
||||||
|
|
||||||
|
// These will be initialized during Parse()
|
||||||
|
_peUtility = null!;
|
||||||
|
_exportDirectoryParser = null!;
|
||||||
|
_importDescriptorParser = null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the PE file structure
|
||||||
|
/// </summary>
|
||||||
|
public void Parse()
|
||||||
|
{
|
||||||
|
using var stream = new MemoryStream(_fileData);
|
||||||
|
using var reader = new BinaryReader(stream);
|
||||||
|
|
||||||
|
// Parse DOS header
|
||||||
|
DosHeader = _dosHeaderParser.Parse(reader);
|
||||||
|
|
||||||
|
// Move to PE header
|
||||||
|
reader.BaseStream.Seek(DosHeader.e_lfanew, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Verify PE signature
|
||||||
|
uint peSignature = reader.ReadUInt32();
|
||||||
|
if (peSignature != PE_SIGNATURE)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Invalid PE signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse File Header
|
||||||
|
FileHeader = _fileHeaderParser.Parse(reader);
|
||||||
|
|
||||||
|
// Parse Optional Header
|
||||||
|
OptionalHeader = _optionalHeaderParser.Parse(reader);
|
||||||
|
Is64Bit = OptionalHeader.Is64Bit();
|
||||||
|
|
||||||
|
// Parse Section Headers
|
||||||
|
for (int i = 0; i < FileHeader.NumberOfSections; i++)
|
||||||
|
{
|
||||||
|
SectionHeaders.Add(_sectionHeaderParser.Parse(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize utility after section headers are parsed
|
||||||
|
_peUtility = new PEUtility(SectionHeaders, OptionalHeader.SizeOfHeaders);
|
||||||
|
_exportDirectoryParser = new ExportDirectoryParser(_peUtility);
|
||||||
|
_importDescriptorParser = new ImportDescriptorParser(_peUtility);
|
||||||
|
|
||||||
|
// Parse Export Directory
|
||||||
|
if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_EXPORT &&
|
||||||
|
OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress != 0)
|
||||||
|
{
|
||||||
|
uint exportDirRva = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||||
|
uint exportDirSize = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
|
||||||
|
|
||||||
|
ExportDirectory = _exportDirectoryParser.Parse(reader, exportDirRva);
|
||||||
|
ExportedFunctions = _exportDirectoryParser.ParseExportedFunctions(
|
||||||
|
reader,
|
||||||
|
ExportDirectory,
|
||||||
|
exportDirRva,
|
||||||
|
exportDirSize
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse Import Descriptors
|
||||||
|
if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_IMPORT &&
|
||||||
|
OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0)
|
||||||
|
{
|
||||||
|
uint importDirRva = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||||
|
ImportDescriptors = _importDescriptorParser.Parse(reader, importDirRva);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
X86Disassembler/PE/Types/DOSHeader.cs
Normal file
27
X86Disassembler/PE/Types/DOSHeader.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the DOS header of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class DOSHeader
|
||||||
|
{
|
||||||
|
public ushort e_magic; // Magic number (MZ)
|
||||||
|
public ushort e_cblp; // Bytes on last page of file
|
||||||
|
public ushort e_cp; // Pages in file
|
||||||
|
public ushort e_crlc; // Relocations
|
||||||
|
public ushort e_cparhdr; // Size of header in paragraphs
|
||||||
|
public ushort e_minalloc; // Minimum extra paragraphs needed
|
||||||
|
public ushort e_maxalloc; // Maximum extra paragraphs needed
|
||||||
|
public ushort e_ss; // Initial (relative) SS value
|
||||||
|
public ushort e_sp; // Initial SP value
|
||||||
|
public ushort e_csum; // Checksum
|
||||||
|
public ushort e_ip; // Initial IP value
|
||||||
|
public ushort e_cs; // Initial (relative) CS value
|
||||||
|
public ushort e_lfarlc; // File address of relocation table
|
||||||
|
public ushort e_ovno; // Overlay number
|
||||||
|
public ushort[] e_res = new ushort[4]; // Reserved words
|
||||||
|
public ushort e_oemid; // OEM identifier (for e_oeminfo)
|
||||||
|
public ushort e_oeminfo; // OEM information; e_oemid specific
|
||||||
|
public ushort[] e_res2 = new ushort[10]; // Reserved words
|
||||||
|
public uint e_lfanew; // File address of new exe header
|
||||||
|
}
|
10
X86Disassembler/PE/Types/DataDirectory.cs
Normal file
10
X86Disassembler/PE/Types/DataDirectory.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a data directory in the optional header
|
||||||
|
/// </summary>
|
||||||
|
public class DataDirectory
|
||||||
|
{
|
||||||
|
public uint VirtualAddress; // RVA of the table
|
||||||
|
public uint Size; // Size of the table
|
||||||
|
}
|
20
X86Disassembler/PE/Types/ExportDirectory.cs
Normal file
20
X86Disassembler/PE/Types/ExportDirectory.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the Export Directory of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class ExportDirectory
|
||||||
|
{
|
||||||
|
public uint Characteristics; // Reserved, must be 0
|
||||||
|
public uint TimeDateStamp; // Time and date stamp
|
||||||
|
public ushort MajorVersion; // Major version
|
||||||
|
public ushort MinorVersion; // Minor version
|
||||||
|
public uint DllNameRva; // RVA of the name of the DLL
|
||||||
|
public string DllName = ""; // The actual name of the DLL
|
||||||
|
public uint Base; // Ordinal base
|
||||||
|
public uint NumberOfFunctions; // Number of functions
|
||||||
|
public uint NumberOfNames; // Number of names
|
||||||
|
public uint AddressOfFunctions; // RVA of the export address table
|
||||||
|
public uint AddressOfNames; // RVA of the export names table
|
||||||
|
public uint AddressOfNameOrdinals; // RVA of the ordinal table
|
||||||
|
}
|
13
X86Disassembler/PE/Types/ExportedFunction.cs
Normal file
13
X86Disassembler/PE/Types/ExportedFunction.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an exported function in a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class ExportedFunction
|
||||||
|
{
|
||||||
|
public string Name = ""; // Function name
|
||||||
|
public ushort Ordinal; // Function ordinal
|
||||||
|
public uint AddressRva; // Function RVA
|
||||||
|
public bool IsForwarder; // True if this is a forwarder
|
||||||
|
public string ForwarderName = ""; // Name of the forwarded function
|
||||||
|
}
|
15
X86Disassembler/PE/Types/FileHeader.cs
Normal file
15
X86Disassembler/PE/Types/FileHeader.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the File header of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class FileHeader
|
||||||
|
{
|
||||||
|
public ushort Machine; // Target machine type
|
||||||
|
public ushort NumberOfSections; // Number of sections
|
||||||
|
public uint TimeDateStamp; // Time and date stamp
|
||||||
|
public uint PointerToSymbolTable; // File pointer to COFF symbol table
|
||||||
|
public uint NumberOfSymbols; // Number of symbols
|
||||||
|
public ushort SizeOfOptionalHeader; // Size of optional header
|
||||||
|
public ushort Characteristics; // Characteristics
|
||||||
|
}
|
16
X86Disassembler/PE/Types/ImportDescriptor.cs
Normal file
16
X86Disassembler/PE/Types/ImportDescriptor.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an Import Descriptor in a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class ImportDescriptor
|
||||||
|
{
|
||||||
|
public uint OriginalFirstThunkRva; // RVA to original first thunk
|
||||||
|
public uint TimeDateStamp; // Time and date stamp
|
||||||
|
public uint ForwarderChain; // Forwarder chain
|
||||||
|
public uint DllNameRva; // RVA to the name of the DLL
|
||||||
|
public string DllName = ""; // The actual name of the DLL
|
||||||
|
public uint FirstThunkRva; // RVA to first thunk
|
||||||
|
|
||||||
|
public List<ImportedFunction> Functions = [];
|
||||||
|
}
|
13
X86Disassembler/PE/Types/ImportedFunction.cs
Normal file
13
X86Disassembler/PE/Types/ImportedFunction.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an imported function in a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class ImportedFunction
|
||||||
|
{
|
||||||
|
public string Name = ""; // Function name
|
||||||
|
public ushort Hint; // Hint value
|
||||||
|
public bool IsOrdinal; // True if imported by ordinal
|
||||||
|
public ushort Ordinal; // Ordinal value (if imported by ordinal)
|
||||||
|
public uint ThunkRva; // RVA of the thunk for this function
|
||||||
|
}
|
56
X86Disassembler/PE/Types/OptionalHeader.cs
Normal file
56
X86Disassembler/PE/Types/OptionalHeader.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the Optional header of a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class OptionalHeader
|
||||||
|
{
|
||||||
|
// Optional Header Magic values
|
||||||
|
private const ushort PE32_MAGIC = 0x10B; // 32-bit executable
|
||||||
|
private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable
|
||||||
|
|
||||||
|
// Standard fields
|
||||||
|
public ushort Magic; // Magic number (PE32 or PE32+)
|
||||||
|
public byte MajorLinkerVersion; // Major linker version
|
||||||
|
public byte MinorLinkerVersion; // Minor linker version
|
||||||
|
public uint SizeOfCode; // Size of code section
|
||||||
|
public uint SizeOfInitializedData; // Size of initialized data section
|
||||||
|
public uint SizeOfUninitializedData; // Size of uninitialized data section
|
||||||
|
public uint AddressOfEntryPoint; // Address of entry point
|
||||||
|
public uint BaseOfCode; // Base of code section
|
||||||
|
public uint BaseOfData; // Base of data section (PE32 only)
|
||||||
|
|
||||||
|
// Windows-specific fields
|
||||||
|
public ulong ImageBase; // Image base address (uint for PE32, ulong for PE32+)
|
||||||
|
public uint SectionAlignment; // Section alignment
|
||||||
|
public uint FileAlignment; // File alignment
|
||||||
|
public ushort MajorOperatingSystemVersion; // Major OS version
|
||||||
|
public ushort MinorOperatingSystemVersion; // Minor OS version
|
||||||
|
public ushort MajorImageVersion; // Major image version
|
||||||
|
public ushort MinorImageVersion; // Minor image version
|
||||||
|
public ushort MajorSubsystemVersion; // Major subsystem version
|
||||||
|
public ushort MinorSubsystemVersion; // Minor subsystem version
|
||||||
|
public uint Win32VersionValue; // Win32 version value
|
||||||
|
public uint SizeOfImage; // Size of image
|
||||||
|
public uint SizeOfHeaders; // Size of headers
|
||||||
|
public uint CheckSum; // Checksum
|
||||||
|
public ushort Subsystem; // Subsystem
|
||||||
|
public ushort DllCharacteristics; // DLL characteristics
|
||||||
|
public ulong SizeOfStackReserve; // Size of stack reserve (uint for PE32, ulong for PE32+)
|
||||||
|
public ulong SizeOfStackCommit; // Size of stack commit (uint for PE32, ulong for PE32+)
|
||||||
|
public ulong SizeOfHeapReserve; // Size of heap reserve (uint for PE32, ulong for PE32+)
|
||||||
|
public ulong SizeOfHeapCommit; // Size of heap commit (uint for PE32, ulong for PE32+)
|
||||||
|
public uint LoaderFlags; // Loader flags
|
||||||
|
public uint NumberOfRvaAndSizes; // Number of RVA and sizes
|
||||||
|
|
||||||
|
public DataDirectory[] DataDirectories = []; // Data directories
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the PE file is 64-bit based on the Magic value
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the PE file is 64-bit, false otherwise</returns>
|
||||||
|
public bool Is64Bit()
|
||||||
|
{
|
||||||
|
return Magic == PE32PLUS_MAGIC;
|
||||||
|
}
|
||||||
|
}
|
45
X86Disassembler/PE/Types/SectionHeader.cs
Normal file
45
X86Disassembler/PE/Types/SectionHeader.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
namespace X86Disassembler.PE.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a section header in a PE file
|
||||||
|
/// </summary>
|
||||||
|
public class SectionHeader
|
||||||
|
{
|
||||||
|
// Section characteristics flags
|
||||||
|
private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code
|
||||||
|
private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable
|
||||||
|
private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable
|
||||||
|
private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable
|
||||||
|
|
||||||
|
public string Name = ""; // Section name
|
||||||
|
public uint VirtualSize; // Virtual size
|
||||||
|
public uint VirtualAddress; // Virtual address
|
||||||
|
public uint SizeOfRawData; // Size of raw data
|
||||||
|
public uint PointerToRawData; // Pointer to raw data
|
||||||
|
public uint PointerToRelocations; // Pointer to relocations
|
||||||
|
public uint PointerToLinenumbers; // Pointer to line numbers
|
||||||
|
public ushort NumberOfRelocations; // Number of relocations
|
||||||
|
public ushort NumberOfLinenumbers; // Number of line numbers
|
||||||
|
public uint Characteristics; // Characteristics
|
||||||
|
|
||||||
|
public bool ContainsCode()
|
||||||
|
{
|
||||||
|
return (Characteristics & IMAGE_SCN_CNT_CODE) != 0 ||
|
||||||
|
(Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReadable()
|
||||||
|
{
|
||||||
|
return (Characteristics & IMAGE_SCN_MEM_READ) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsWritable()
|
||||||
|
{
|
||||||
|
return (Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsExecutable()
|
||||||
|
{
|
||||||
|
return (Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,834 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace X86Disassembler
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a Portable Executable (PE) file format parser
|
|
||||||
/// </summary>
|
|
||||||
public class PEFormat
|
|
||||||
{
|
|
||||||
// DOS Header constants
|
|
||||||
private const ushort DOS_SIGNATURE = 0x5A4D; // 'MZ'
|
|
||||||
private const uint PE_SIGNATURE = 0x00004550; // 'PE\0\0'
|
|
||||||
|
|
||||||
// Optional Header Magic values
|
|
||||||
private const ushort PE32_MAGIC = 0x10B; // 32-bit executable
|
|
||||||
private const ushort PE32PLUS_MAGIC = 0x20B; // 64-bit executable
|
|
||||||
|
|
||||||
// Section characteristics flags
|
|
||||||
private const uint IMAGE_SCN_CNT_CODE = 0x00000020; // Section contains code
|
|
||||||
private const uint IMAGE_SCN_MEM_EXECUTE = 0x20000000; // Section is executable
|
|
||||||
private const uint IMAGE_SCN_MEM_READ = 0x40000000; // Section is readable
|
|
||||||
private const uint IMAGE_SCN_MEM_WRITE = 0x80000000; // Section is writable
|
|
||||||
|
|
||||||
// Data directories
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0; // Export Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_IMPORT = 1; // Import Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_RESOURCE = 2; // Resource Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3; // Exception Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_SECURITY = 4; // Security Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_BASERELOC = 5; // Base Relocation Table
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_DEBUG = 6; // Debug Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7; // Architecture Specific Data
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8; // RVA of GP
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_TLS = 9; // TLS Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10; // Load Configuration Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11; // Bound Import Directory
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_IAT = 12; // Import Address Table
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13; // Delay Load Import Descriptors
|
|
||||||
private const int IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14; // COM Runtime descriptor
|
|
||||||
|
|
||||||
// PE file data
|
|
||||||
private byte[] _fileData;
|
|
||||||
|
|
||||||
// Parsed headers
|
|
||||||
public DOSHeader DosHeader { get; private set; }
|
|
||||||
public FileHeader FileHeader { get; private set; }
|
|
||||||
public OptionalHeader OptionalHeader { get; private set; }
|
|
||||||
public List<SectionHeader> SectionHeaders { get; private set; }
|
|
||||||
public bool Is64Bit { get; private set; }
|
|
||||||
|
|
||||||
// Export and Import information
|
|
||||||
public ExportDirectory ExportDirectory { get; private set; }
|
|
||||||
public List<ExportedFunction> ExportedFunctions { get; private set; }
|
|
||||||
public List<ImportDescriptor> ImportDescriptors { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses a PE file from the given byte array
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileData">The raw file data</param>
|
|
||||||
public PEFormat(byte[] fileData)
|
|
||||||
{
|
|
||||||
_fileData = fileData;
|
|
||||||
SectionHeaders = new List<SectionHeader>();
|
|
||||||
ExportedFunctions = new List<ExportedFunction>();
|
|
||||||
ImportDescriptors = new List<ImportDescriptor>();
|
|
||||||
Parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the PE file structure
|
|
||||||
/// </summary>
|
|
||||||
private void Parse()
|
|
||||||
{
|
|
||||||
using (MemoryStream stream = new MemoryStream(_fileData))
|
|
||||||
using (BinaryReader reader = new BinaryReader(stream))
|
|
||||||
{
|
|
||||||
// Parse DOS header
|
|
||||||
DosHeader = ParseDOSHeader(reader);
|
|
||||||
|
|
||||||
// Move to PE header
|
|
||||||
reader.BaseStream.Seek(DosHeader.e_lfanew, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
// Verify PE signature
|
|
||||||
uint peSignature = reader.ReadUInt32();
|
|
||||||
if (peSignature != PE_SIGNATURE)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Invalid PE signature");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse File Header
|
|
||||||
FileHeader = ParseFileHeader(reader);
|
|
||||||
|
|
||||||
// Parse Optional Header
|
|
||||||
OptionalHeader = ParseOptionalHeader(reader);
|
|
||||||
|
|
||||||
// Parse Section Headers
|
|
||||||
for (int i = 0; i < FileHeader.NumberOfSections; i++)
|
|
||||||
{
|
|
||||||
SectionHeaders.Add(ParseSectionHeader(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse Export Directory
|
|
||||||
if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_EXPORT &&
|
|
||||||
OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress != 0)
|
|
||||||
{
|
|
||||||
ExportDirectory = ParseExportDirectory(reader, OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
|
||||||
ParseExportedFunctions(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse Import Descriptors
|
|
||||||
if (OptionalHeader.DataDirectories.Length > IMAGE_DIRECTORY_ENTRY_IMPORT &&
|
|
||||||
OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0)
|
|
||||||
{
|
|
||||||
ImportDescriptors = ParseImportDescriptors(reader, OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the DOS header
|
|
||||||
/// </summary>
|
|
||||||
private DOSHeader ParseDOSHeader(BinaryReader reader)
|
|
||||||
{
|
|
||||||
DOSHeader header = new DOSHeader();
|
|
||||||
|
|
||||||
header.e_magic = reader.ReadUInt16();
|
|
||||||
if (header.e_magic != DOS_SIGNATURE)
|
|
||||||
{
|
|
||||||
throw new InvalidDataException("Invalid DOS signature (MZ)");
|
|
||||||
}
|
|
||||||
|
|
||||||
header.e_cblp = reader.ReadUInt16();
|
|
||||||
header.e_cp = reader.ReadUInt16();
|
|
||||||
header.e_crlc = reader.ReadUInt16();
|
|
||||||
header.e_cparhdr = reader.ReadUInt16();
|
|
||||||
header.e_minalloc = reader.ReadUInt16();
|
|
||||||
header.e_maxalloc = reader.ReadUInt16();
|
|
||||||
header.e_ss = reader.ReadUInt16();
|
|
||||||
header.e_sp = reader.ReadUInt16();
|
|
||||||
header.e_csum = reader.ReadUInt16();
|
|
||||||
header.e_ip = reader.ReadUInt16();
|
|
||||||
header.e_cs = reader.ReadUInt16();
|
|
||||||
header.e_lfarlc = reader.ReadUInt16();
|
|
||||||
header.e_ovno = reader.ReadUInt16();
|
|
||||||
|
|
||||||
header.e_res = new ushort[4];
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
header.e_res[i] = reader.ReadUInt16();
|
|
||||||
}
|
|
||||||
|
|
||||||
header.e_oemid = reader.ReadUInt16();
|
|
||||||
header.e_oeminfo = reader.ReadUInt16();
|
|
||||||
|
|
||||||
header.e_res2 = new ushort[10];
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
header.e_res2[i] = reader.ReadUInt16();
|
|
||||||
}
|
|
||||||
|
|
||||||
header.e_lfanew = reader.ReadUInt32();
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the File header
|
|
||||||
/// </summary>
|
|
||||||
private FileHeader ParseFileHeader(BinaryReader reader)
|
|
||||||
{
|
|
||||||
FileHeader header = new FileHeader();
|
|
||||||
|
|
||||||
header.Machine = reader.ReadUInt16();
|
|
||||||
header.NumberOfSections = reader.ReadUInt16();
|
|
||||||
header.TimeDateStamp = reader.ReadUInt32();
|
|
||||||
header.PointerToSymbolTable = reader.ReadUInt32();
|
|
||||||
header.NumberOfSymbols = reader.ReadUInt32();
|
|
||||||
header.SizeOfOptionalHeader = reader.ReadUInt16();
|
|
||||||
header.Characteristics = reader.ReadUInt16();
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the Optional header
|
|
||||||
/// </summary>
|
|
||||||
private OptionalHeader ParseOptionalHeader(BinaryReader reader)
|
|
||||||
{
|
|
||||||
OptionalHeader header = new OptionalHeader();
|
|
||||||
|
|
||||||
// Standard fields
|
|
||||||
header.Magic = reader.ReadUInt16();
|
|
||||||
|
|
||||||
// Determine if this is a PE32 or PE32+ file
|
|
||||||
Is64Bit = header.Magic == PE32PLUS_MAGIC;
|
|
||||||
|
|
||||||
header.MajorLinkerVersion = reader.ReadByte();
|
|
||||||
header.MinorLinkerVersion = reader.ReadByte();
|
|
||||||
header.SizeOfCode = reader.ReadUInt32();
|
|
||||||
header.SizeOfInitializedData = reader.ReadUInt32();
|
|
||||||
header.SizeOfUninitializedData = reader.ReadUInt32();
|
|
||||||
header.AddressOfEntryPoint = reader.ReadUInt32();
|
|
||||||
header.BaseOfCode = reader.ReadUInt32();
|
|
||||||
|
|
||||||
// PE32 has BaseOfData, PE32+ doesn't
|
|
||||||
if (!Is64Bit)
|
|
||||||
{
|
|
||||||
header.BaseOfData = reader.ReadUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows-specific fields
|
|
||||||
if (Is64Bit)
|
|
||||||
{
|
|
||||||
header.ImageBase = reader.ReadUInt64();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
header.ImageBase = reader.ReadUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
header.SectionAlignment = reader.ReadUInt32();
|
|
||||||
header.FileAlignment = reader.ReadUInt32();
|
|
||||||
header.MajorOperatingSystemVersion = reader.ReadUInt16();
|
|
||||||
header.MinorOperatingSystemVersion = reader.ReadUInt16();
|
|
||||||
header.MajorImageVersion = reader.ReadUInt16();
|
|
||||||
header.MinorImageVersion = reader.ReadUInt16();
|
|
||||||
header.MajorSubsystemVersion = reader.ReadUInt16();
|
|
||||||
header.MinorSubsystemVersion = reader.ReadUInt16();
|
|
||||||
header.Win32VersionValue = reader.ReadUInt32();
|
|
||||||
header.SizeOfImage = reader.ReadUInt32();
|
|
||||||
header.SizeOfHeaders = reader.ReadUInt32();
|
|
||||||
header.CheckSum = reader.ReadUInt32();
|
|
||||||
header.Subsystem = reader.ReadUInt16();
|
|
||||||
header.DllCharacteristics = reader.ReadUInt16();
|
|
||||||
|
|
||||||
// Size fields differ between PE32 and PE32+
|
|
||||||
if (Is64Bit)
|
|
||||||
{
|
|
||||||
header.SizeOfStackReserve = reader.ReadUInt64();
|
|
||||||
header.SizeOfStackCommit = reader.ReadUInt64();
|
|
||||||
header.SizeOfHeapReserve = reader.ReadUInt64();
|
|
||||||
header.SizeOfHeapCommit = reader.ReadUInt64();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
header.SizeOfStackReserve = reader.ReadUInt32();
|
|
||||||
header.SizeOfStackCommit = reader.ReadUInt32();
|
|
||||||
header.SizeOfHeapReserve = reader.ReadUInt32();
|
|
||||||
header.SizeOfHeapCommit = reader.ReadUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
header.LoaderFlags = reader.ReadUInt32();
|
|
||||||
header.NumberOfRvaAndSizes = reader.ReadUInt32();
|
|
||||||
|
|
||||||
// Data directories
|
|
||||||
int numDirectories = (int)Math.Min(header.NumberOfRvaAndSizes, 16); // Maximum of 16 directories
|
|
||||||
header.DataDirectories = new DataDirectory[numDirectories];
|
|
||||||
|
|
||||||
for (int i = 0; i < numDirectories; i++)
|
|
||||||
{
|
|
||||||
DataDirectory dir = new DataDirectory();
|
|
||||||
dir.VirtualAddress = reader.ReadUInt32();
|
|
||||||
dir.Size = reader.ReadUInt32();
|
|
||||||
header.DataDirectories[i] = dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses a section header
|
|
||||||
/// </summary>
|
|
||||||
private SectionHeader ParseSectionHeader(BinaryReader reader)
|
|
||||||
{
|
|
||||||
SectionHeader header = new SectionHeader();
|
|
||||||
|
|
||||||
// Read section name (8 bytes)
|
|
||||||
byte[] nameBytes = reader.ReadBytes(8);
|
|
||||||
// Convert to string, removing any null characters
|
|
||||||
header.Name = Encoding.ASCII.GetString(nameBytes).TrimEnd('\0');
|
|
||||||
|
|
||||||
header.VirtualSize = reader.ReadUInt32();
|
|
||||||
header.VirtualAddress = reader.ReadUInt32();
|
|
||||||
header.SizeOfRawData = reader.ReadUInt32();
|
|
||||||
header.PointerToRawData = reader.ReadUInt32();
|
|
||||||
header.PointerToRelocations = reader.ReadUInt32();
|
|
||||||
header.PointerToLinenumbers = reader.ReadUInt32();
|
|
||||||
header.NumberOfRelocations = reader.ReadUInt16();
|
|
||||||
header.NumberOfLinenumbers = reader.ReadUInt16();
|
|
||||||
header.Characteristics = reader.ReadUInt32();
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the Export Directory
|
|
||||||
/// </summary>
|
|
||||||
private ExportDirectory ParseExportDirectory(BinaryReader reader, uint rva)
|
|
||||||
{
|
|
||||||
ExportDirectory directory = new ExportDirectory();
|
|
||||||
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(rva), SeekOrigin.Begin);
|
|
||||||
|
|
||||||
directory.Characteristics = reader.ReadUInt32();
|
|
||||||
directory.TimeDateStamp = reader.ReadUInt32();
|
|
||||||
directory.MajorVersion = reader.ReadUInt16();
|
|
||||||
directory.MinorVersion = reader.ReadUInt16();
|
|
||||||
directory.Name = reader.ReadUInt32();
|
|
||||||
directory.Base = reader.ReadUInt32();
|
|
||||||
directory.NumberOfFunctions = reader.ReadUInt32();
|
|
||||||
directory.NumberOfNames = reader.ReadUInt32();
|
|
||||||
directory.AddressOfFunctions = reader.ReadUInt32();
|
|
||||||
directory.AddressOfNames = reader.ReadUInt32();
|
|
||||||
directory.AddressOfNameOrdinals = reader.ReadUInt32();
|
|
||||||
|
|
||||||
// Read the DLL name
|
|
||||||
uint dllNameRVA = directory.Name;
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(dllNameRVA), SeekOrigin.Begin);
|
|
||||||
byte[] dllNameBytes = reader.ReadBytes(256);
|
|
||||||
directory.DllName = Encoding.ASCII.GetString(dllNameBytes).TrimEnd('\0');
|
|
||||||
|
|
||||||
return directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the Import Descriptors
|
|
||||||
/// </summary>
|
|
||||||
private List<ImportDescriptor> ParseImportDescriptors(BinaryReader reader, uint rva)
|
|
||||||
{
|
|
||||||
List<ImportDescriptor> descriptors = new List<ImportDescriptor>();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(rva), SeekOrigin.Begin);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ImportDescriptor descriptor = new ImportDescriptor();
|
|
||||||
|
|
||||||
descriptor.OriginalFirstThunk = reader.ReadUInt32();
|
|
||||||
descriptor.TimeDateStamp = reader.ReadUInt32();
|
|
||||||
descriptor.ForwarderChain = reader.ReadUInt32();
|
|
||||||
descriptor.Name = reader.ReadUInt32();
|
|
||||||
descriptor.FirstThunk = reader.ReadUInt32();
|
|
||||||
|
|
||||||
// Check if we've reached the end of the import descriptors
|
|
||||||
if (descriptor.OriginalFirstThunk == 0 && descriptor.Name == 0 && descriptor.FirstThunk == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Read the DLL name
|
|
||||||
uint dllNameOffset = RvaToOffset(descriptor.Name);
|
|
||||||
reader.BaseStream.Seek(dllNameOffset, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
List<byte> nameBytes = new List<byte>();
|
|
||||||
byte b;
|
|
||||||
while ((b = reader.ReadByte()) != 0)
|
|
||||||
{
|
|
||||||
nameBytes.Add(b);
|
|
||||||
}
|
|
||||||
descriptor.DllName = Encoding.ASCII.GetString(nameBytes.ToArray());
|
|
||||||
|
|
||||||
// Read the imported functions (use FirstThunk if OriginalFirstThunk is 0)
|
|
||||||
uint thunkRVA = descriptor.OriginalFirstThunk != 0 ? descriptor.OriginalFirstThunk : descriptor.FirstThunk;
|
|
||||||
|
|
||||||
if (thunkRVA != 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint thunkOffset = RvaToOffset(thunkRVA);
|
|
||||||
uint currentThunkOffset = thunkOffset;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
reader.BaseStream.Seek(currentThunkOffset, SeekOrigin.Begin);
|
|
||||||
uint thunk = reader.ReadUInt32();
|
|
||||||
|
|
||||||
if (thunk == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImportedFunction function = new ImportedFunction();
|
|
||||||
function.ThunkRVA = thunkRVA + (currentThunkOffset - thunkOffset);
|
|
||||||
|
|
||||||
// Check if the function is imported by ordinal
|
|
||||||
if ((thunk & 0x80000000) != 0)
|
|
||||||
{
|
|
||||||
function.IsOrdinal = true;
|
|
||||||
function.Ordinal = (ushort)(thunk & 0xFFFF);
|
|
||||||
function.Name = $"Ordinal_{function.Ordinal}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Read the function name and hint
|
|
||||||
try
|
|
||||||
{
|
|
||||||
uint nameOffset = RvaToOffset(thunk);
|
|
||||||
reader.BaseStream.Seek(nameOffset, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
function.Hint = reader.ReadUInt16();
|
|
||||||
|
|
||||||
List<byte> funcNameBytes = new List<byte>();
|
|
||||||
byte c;
|
|
||||||
while ((c = reader.ReadByte()) != 0)
|
|
||||||
{
|
|
||||||
funcNameBytes.Add(c);
|
|
||||||
}
|
|
||||||
function.Name = Encoding.ASCII.GetString(funcNameBytes.ToArray());
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
function.Name = $"Function_at_{thunk:X8}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.Functions.Add(function);
|
|
||||||
|
|
||||||
currentThunkOffset += 4; // Move to the next thunk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// Skip this thunk table if there's an error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// If we can't read the DLL name, use a placeholder
|
|
||||||
descriptor.DllName = $"DLL_at_{descriptor.Name:X8}";
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptors.Add(descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// Return whatever descriptors we've managed to parse
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the exported functions using the export directory information
|
|
||||||
/// </summary>
|
|
||||||
private void ParseExportedFunctions(BinaryReader reader)
|
|
||||||
{
|
|
||||||
if (ExportDirectory == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the array of function addresses (RVAs)
|
|
||||||
uint[] functionRVAs = new uint[ExportDirectory.NumberOfFunctions];
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(ExportDirectory.AddressOfFunctions), SeekOrigin.Begin);
|
|
||||||
for (int i = 0; i < ExportDirectory.NumberOfFunctions; i++)
|
|
||||||
{
|
|
||||||
functionRVAs[i] = reader.ReadUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the array of name RVAs
|
|
||||||
uint[] nameRVAs = new uint[ExportDirectory.NumberOfNames];
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(ExportDirectory.AddressOfNames), SeekOrigin.Begin);
|
|
||||||
for (int i = 0; i < ExportDirectory.NumberOfNames; i++)
|
|
||||||
{
|
|
||||||
nameRVAs[i] = reader.ReadUInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the array of name ordinals
|
|
||||||
ushort[] nameOrdinals = new ushort[ExportDirectory.NumberOfNames];
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(ExportDirectory.AddressOfNameOrdinals), SeekOrigin.Begin);
|
|
||||||
for (int i = 0; i < ExportDirectory.NumberOfNames; i++)
|
|
||||||
{
|
|
||||||
nameOrdinals[i] = reader.ReadUInt16();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a dictionary to map ordinals to names
|
|
||||||
Dictionary<ushort, string> ordinalToName = new Dictionary<ushort, string>();
|
|
||||||
for (int i = 0; i < ExportDirectory.NumberOfNames; i++)
|
|
||||||
{
|
|
||||||
// Read the function name
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(nameRVAs[i]), SeekOrigin.Begin);
|
|
||||||
List<byte> nameBytes = new List<byte>();
|
|
||||||
byte b;
|
|
||||||
while ((b = reader.ReadByte()) != 0)
|
|
||||||
{
|
|
||||||
nameBytes.Add(b);
|
|
||||||
}
|
|
||||||
string name = Encoding.ASCII.GetString(nameBytes.ToArray());
|
|
||||||
|
|
||||||
// Map the ordinal to the name
|
|
||||||
ordinalToName[nameOrdinals[i]] = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the exported functions
|
|
||||||
for (ushort i = 0; i < ExportDirectory.NumberOfFunctions; i++)
|
|
||||||
{
|
|
||||||
uint functionRVA = functionRVAs[i];
|
|
||||||
if (functionRVA == 0)
|
|
||||||
{
|
|
||||||
continue; // Skip empty entries
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportedFunction function = new ExportedFunction();
|
|
||||||
function.Ordinal = (ushort)(i + ExportDirectory.Base);
|
|
||||||
function.Address = functionRVA;
|
|
||||||
|
|
||||||
// Check if this function has a name
|
|
||||||
if (ordinalToName.TryGetValue(i, out string name))
|
|
||||||
{
|
|
||||||
function.Name = name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
function.Name = $"Ordinal_{function.Ordinal}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a forwarder
|
|
||||||
uint exportDirStart = OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
|
||||||
uint exportDirEnd = exportDirStart + OptionalHeader.DataDirectories[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
|
|
||||||
|
|
||||||
if (functionRVA >= exportDirStart && functionRVA < exportDirEnd)
|
|
||||||
{
|
|
||||||
function.IsForwarder = true;
|
|
||||||
|
|
||||||
// Read the forwarder string
|
|
||||||
reader.BaseStream.Seek(RvaToOffset(functionRVA), SeekOrigin.Begin);
|
|
||||||
List<byte> forwarderBytes = new List<byte>();
|
|
||||||
byte b;
|
|
||||||
while ((b = reader.ReadByte()) != 0)
|
|
||||||
{
|
|
||||||
forwarderBytes.Add(b);
|
|
||||||
}
|
|
||||||
function.ForwarderName = Encoding.ASCII.GetString(forwarderBytes.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
ExportedFunctions.Add(function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the raw data for a specific section
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sectionIndex">Index of the section</param>
|
|
||||||
/// <returns>Byte array containing the section data</returns>
|
|
||||||
public byte[] GetSectionData(int sectionIndex)
|
|
||||||
{
|
|
||||||
if (sectionIndex < 0 || sectionIndex >= SectionHeaders.Count)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(sectionIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
SectionHeader section = SectionHeaders[sectionIndex];
|
|
||||||
byte[] sectionData = new byte[section.SizeOfRawData];
|
|
||||||
|
|
||||||
Array.Copy(_fileData, section.PointerToRawData, sectionData, 0, section.SizeOfRawData);
|
|
||||||
|
|
||||||
return sectionData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the raw data for a section by name
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sectionName">Name of the section</param>
|
|
||||||
/// <returns>Byte array containing the section data</returns>
|
|
||||||
public byte[] GetSectionData(string sectionName)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < SectionHeaders.Count; i++)
|
|
||||||
{
|
|
||||||
if (SectionHeaders[i].Name == sectionName)
|
|
||||||
{
|
|
||||||
return GetSectionData(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"Section '{sectionName}' not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a section contains code
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="section">The section to check</param>
|
|
||||||
/// <returns>True if the section contains code, false otherwise</returns>
|
|
||||||
public bool IsSectionContainsCode(SectionHeader section)
|
|
||||||
{
|
|
||||||
return (section.Characteristics & IMAGE_SCN_CNT_CODE) != 0 ||
|
|
||||||
(section.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all code sections
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>List of section indices that contain code</returns>
|
|
||||||
public List<int> GetCodeSections()
|
|
||||||
{
|
|
||||||
List<int> codeSections = new List<int>();
|
|
||||||
|
|
||||||
for (int i = 0; i < SectionHeaders.Count; i++)
|
|
||||||
{
|
|
||||||
if (IsSectionContainsCode(SectionHeaders[i]))
|
|
||||||
{
|
|
||||||
codeSections.Add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return codeSections;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts a Relative Virtual Address (RVA) to a file offset
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="rva">The RVA to convert</param>
|
|
||||||
/// <returns>The corresponding file offset</returns>
|
|
||||||
public uint RvaToOffset(uint rva)
|
|
||||||
{
|
|
||||||
if (rva == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var section in SectionHeaders)
|
|
||||||
{
|
|
||||||
// Check if the RVA is within this section
|
|
||||||
if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
|
|
||||||
{
|
|
||||||
// Calculate the offset within the section
|
|
||||||
uint offsetInSection = rva - section.VirtualAddress;
|
|
||||||
|
|
||||||
// Make sure we don't exceed the raw data size
|
|
||||||
if (offsetInSection < section.SizeOfRawData)
|
|
||||||
{
|
|
||||||
return section.PointerToRawData + offsetInSection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the RVA is not within any section, it might be in the headers
|
|
||||||
if (rva < OptionalHeader.SizeOfHeaders)
|
|
||||||
{
|
|
||||||
return rva;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"RVA {rva:X8} is not within any section");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region PE Format Structures
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DOS Header structure
|
|
||||||
/// </summary>
|
|
||||||
public class DOSHeader
|
|
||||||
{
|
|
||||||
public ushort e_magic; // Magic number ("MZ")
|
|
||||||
public ushort e_cblp; // Bytes on last page of file
|
|
||||||
public ushort e_cp; // Pages in file
|
|
||||||
public ushort e_crlc; // Relocations
|
|
||||||
public ushort e_cparhdr; // Size of header in paragraphs
|
|
||||||
public ushort e_minalloc; // Minimum extra paragraphs needed
|
|
||||||
public ushort e_maxalloc; // Maximum extra paragraphs needed
|
|
||||||
public ushort e_ss; // Initial (relative) SS value
|
|
||||||
public ushort e_sp; // Initial SP value
|
|
||||||
public ushort e_csum; // Checksum
|
|
||||||
public ushort e_ip; // Initial IP value
|
|
||||||
public ushort e_cs; // Initial (relative) CS value
|
|
||||||
public ushort e_lfarlc; // File address of relocation table
|
|
||||||
public ushort e_ovno; // Overlay number
|
|
||||||
public ushort[] e_res; // Reserved words
|
|
||||||
public ushort e_oemid; // OEM identifier
|
|
||||||
public ushort e_oeminfo; // OEM information
|
|
||||||
public ushort[] e_res2; // Reserved words
|
|
||||||
public uint e_lfanew; // File address of new exe header
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// File Header structure
|
|
||||||
/// </summary>
|
|
||||||
public class FileHeader
|
|
||||||
{
|
|
||||||
public ushort Machine; // Target machine type
|
|
||||||
public ushort NumberOfSections; // Number of sections
|
|
||||||
public uint TimeDateStamp; // Time stamp
|
|
||||||
public uint PointerToSymbolTable; // File offset of symbol table
|
|
||||||
public uint NumberOfSymbols; // Number of symbols
|
|
||||||
public ushort SizeOfOptionalHeader; // Size of optional header
|
|
||||||
public ushort Characteristics; // Characteristics
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Optional Header structure
|
|
||||||
/// </summary>
|
|
||||||
public class OptionalHeader
|
|
||||||
{
|
|
||||||
// Standard fields
|
|
||||||
public ushort Magic; // Magic number (PE32 or PE32+)
|
|
||||||
public byte MajorLinkerVersion; // Major linker version
|
|
||||||
public byte MinorLinkerVersion; // Minor linker version
|
|
||||||
public uint SizeOfCode; // Size of code section
|
|
||||||
public uint SizeOfInitializedData; // Size of initialized data
|
|
||||||
public uint SizeOfUninitializedData; // Size of uninitialized data
|
|
||||||
public uint AddressOfEntryPoint; // Entry point RVA
|
|
||||||
public uint BaseOfCode; // Base of code section
|
|
||||||
public uint BaseOfData; // Base of data section (PE32 only)
|
|
||||||
|
|
||||||
// Windows-specific fields
|
|
||||||
public dynamic ImageBase; // Preferred image base (uint for PE32, ulong for PE32+)
|
|
||||||
public uint SectionAlignment; // Section alignment
|
|
||||||
public uint FileAlignment; // File alignment
|
|
||||||
public ushort MajorOperatingSystemVersion; // Major OS version
|
|
||||||
public ushort MinorOperatingSystemVersion; // Minor OS version
|
|
||||||
public ushort MajorImageVersion; // Major image version
|
|
||||||
public ushort MinorImageVersion; // Minor image version
|
|
||||||
public ushort MajorSubsystemVersion; // Major subsystem version
|
|
||||||
public ushort MinorSubsystemVersion; // Minor subsystem version
|
|
||||||
public uint Win32VersionValue; // Win32 version value
|
|
||||||
public uint SizeOfImage; // Size of image
|
|
||||||
public uint SizeOfHeaders; // Size of headers
|
|
||||||
public uint CheckSum; // Checksum
|
|
||||||
public ushort Subsystem; // Subsystem
|
|
||||||
public ushort DllCharacteristics; // DLL characteristics
|
|
||||||
public dynamic SizeOfStackReserve; // Size of stack reserve (uint for PE32, ulong for PE32+)
|
|
||||||
public dynamic SizeOfStackCommit; // Size of stack commit (uint for PE32, ulong for PE32+)
|
|
||||||
public dynamic SizeOfHeapReserve; // Size of heap reserve (uint for PE32, ulong for PE32+)
|
|
||||||
public dynamic SizeOfHeapCommit; // Size of heap commit (uint for PE32, ulong for PE32+)
|
|
||||||
public uint LoaderFlags; // Loader flags
|
|
||||||
public uint NumberOfRvaAndSizes; // Number of data directories
|
|
||||||
|
|
||||||
// Data directories
|
|
||||||
public DataDirectory[] DataDirectories; // Data directories
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data Directory structure
|
|
||||||
/// </summary>
|
|
||||||
public class DataDirectory
|
|
||||||
{
|
|
||||||
public uint VirtualAddress; // RVA of the directory
|
|
||||||
public uint Size; // Size of the directory
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Section Header structure
|
|
||||||
/// </summary>
|
|
||||||
public class SectionHeader
|
|
||||||
{
|
|
||||||
public string Name; // Section name
|
|
||||||
public uint VirtualSize; // Virtual size
|
|
||||||
public uint VirtualAddress; // Virtual address (RVA)
|
|
||||||
public uint SizeOfRawData; // Size of raw data
|
|
||||||
public uint PointerToRawData; // File pointer to raw data
|
|
||||||
public uint PointerToRelocations; // File pointer to relocations
|
|
||||||
public uint PointerToLinenumbers; // File pointer to line numbers
|
|
||||||
public ushort NumberOfRelocations; // Number of relocations
|
|
||||||
public ushort NumberOfLinenumbers; // Number of line numbers
|
|
||||||
public uint Characteristics; // Characteristics
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Export and Import Structures
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Export Directory structure
|
|
||||||
/// </summary>
|
|
||||||
public class ExportDirectory
|
|
||||||
{
|
|
||||||
public uint Characteristics;
|
|
||||||
public uint TimeDateStamp;
|
|
||||||
public ushort MajorVersion;
|
|
||||||
public ushort MinorVersion;
|
|
||||||
public uint Name; // RVA to the DLL name
|
|
||||||
public string DllName; // Actual DLL name
|
|
||||||
public uint Base; // Ordinal base
|
|
||||||
public uint NumberOfFunctions; // Number of exported functions
|
|
||||||
public uint NumberOfNames; // Number of exported names
|
|
||||||
public uint AddressOfFunctions; // RVA to function addresses
|
|
||||||
public uint AddressOfNames; // RVA to function names
|
|
||||||
public uint AddressOfNameOrdinals; // RVA to ordinals
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an exported function
|
|
||||||
/// </summary>
|
|
||||||
public class ExportedFunction
|
|
||||||
{
|
|
||||||
public string Name; // Function name
|
|
||||||
public uint Address; // Function RVA
|
|
||||||
public ushort Ordinal; // Function ordinal
|
|
||||||
public bool IsForwarder; // True if this is a forwarder
|
|
||||||
public string ForwarderName; // Name of the forwarded function (if IsForwarder is true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Import Descriptor structure
|
|
||||||
/// </summary>
|
|
||||||
public class ImportDescriptor
|
|
||||||
{
|
|
||||||
public uint OriginalFirstThunk; // RVA to Import Lookup Table
|
|
||||||
public uint TimeDateStamp;
|
|
||||||
public uint ForwarderChain;
|
|
||||||
public uint Name; // RVA to the DLL name
|
|
||||||
public string DllName; // Actual DLL name
|
|
||||||
public uint FirstThunk; // RVA to Import Address Table
|
|
||||||
public List<ImportedFunction> Functions; // List of imported functions
|
|
||||||
|
|
||||||
public ImportDescriptor()
|
|
||||||
{
|
|
||||||
Functions = new List<ImportedFunction>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an imported function
|
|
||||||
/// </summary>
|
|
||||||
public class ImportedFunction
|
|
||||||
{
|
|
||||||
public bool IsOrdinal; // True if imported by ordinal
|
|
||||||
public ushort Ordinal; // Ordinal value (if IsOrdinal is true)
|
|
||||||
public string Name; // Function name (if IsOrdinal is false)
|
|
||||||
public ushort Hint; // Hint value (if IsOrdinal is false)
|
|
||||||
public uint ThunkRVA; // RVA in the Import Address Table
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
@ -1,153 +1,27 @@
|
|||||||
using System;
|
using X86Disassembler.Analysers;
|
||||||
using System.IO;
|
using X86Disassembler.PE;
|
||||||
|
using X86Disassembler.ProjectSystem;
|
||||||
|
|
||||||
namespace X86Disassembler
|
namespace X86Disassembler;
|
||||||
|
|
||||||
|
public class Program
|
||||||
{
|
{
|
||||||
internal class Program
|
private const string FilePath = @"C:\Program Files (x86)\Nikita\Iron Strategy\Terrain.dll";
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// Path to the DLL file to disassemble
|
byte[] fileBytes = File.ReadAllBytes(FilePath);
|
||||||
private const string DllPath = @"C:\Windows\SysWOW64\msvcrt.dll"; // Example path, replace with your target DLL
|
PeFile peFile = new PeFile(fileBytes);
|
||||||
|
peFile.Parse();
|
||||||
static void Main(string[] args)
|
|
||||||
|
var projectPeFile = new ProjectPeFile()
|
||||||
{
|
{
|
||||||
Console.WriteLine("X86 Disassembler and Decompiler");
|
ImageBase = new VirtualAddress(0, peFile.OptionalHeader.ImageBase),
|
||||||
Console.WriteLine("--------------------------------");
|
Architecture = peFile.OptionalHeader.Is64Bit()
|
||||||
|
? "64-bit"
|
||||||
Console.WriteLine($"Loading file: {DllPath}");
|
: "32-bit",
|
||||||
|
Name = Path.GetFileName(FilePath),
|
||||||
// Load the DLL file
|
EntryPointAddress = new FileAbsoluteAddress(peFile.OptionalHeader.AddressOfEntryPoint, peFile.OptionalHeader.ImageBase)
|
||||||
byte[] binaryData = File.ReadAllBytes(DllPath);
|
};
|
||||||
|
|
||||||
Console.WriteLine($"Successfully loaded {DllPath}");
|
|
||||||
Console.WriteLine($"File size: {binaryData.Length} bytes");
|
|
||||||
|
|
||||||
// Parse the PE format
|
|
||||||
Console.WriteLine("\nParsing PE format...");
|
|
||||||
PEFormat peFile = new PEFormat(binaryData);
|
|
||||||
|
|
||||||
// Display basic PE information
|
|
||||||
DisplayPEInfo(peFile);
|
|
||||||
|
|
||||||
// Display exported functions
|
|
||||||
DisplayExportedFunctions(peFile);
|
|
||||||
|
|
||||||
// Display imported functions
|
|
||||||
DisplayImportedFunctions(peFile);
|
|
||||||
|
|
||||||
// Find code sections for disassembly
|
|
||||||
var codeSections = peFile.GetCodeSections();
|
|
||||||
Console.WriteLine($"\nFound {codeSections.Count} code section(s):");
|
|
||||||
|
|
||||||
foreach (int sectionIndex in codeSections)
|
|
||||||
{
|
|
||||||
var section = peFile.SectionHeaders[sectionIndex];
|
|
||||||
Console.WriteLine($" - {section.Name}: Size={section.SizeOfRawData} bytes, RVA=0x{section.VirtualAddress:X8}");
|
|
||||||
|
|
||||||
// Get the section data for disassembly
|
|
||||||
byte[] sectionData = peFile.GetSectionData(sectionIndex);
|
|
||||||
|
|
||||||
// TODO: Implement disassembling logic here
|
|
||||||
// This is where we would pass the section data to our disassembler
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("\nPress any key to exit...");
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DisplayPEInfo(PEFormat peFile)
|
|
||||||
{
|
|
||||||
Console.WriteLine("\nPE File Information:");
|
|
||||||
Console.WriteLine($"Architecture: {(peFile.Is64Bit ? "64-bit" : "32-bit")}");
|
|
||||||
Console.WriteLine($"Entry Point: 0x{peFile.OptionalHeader.AddressOfEntryPoint:X8}");
|
|
||||||
Console.WriteLine($"Image Base: 0x{peFile.OptionalHeader.ImageBase:X}");
|
|
||||||
Console.WriteLine($"Number of Sections: {peFile.FileHeader.NumberOfSections}");
|
|
||||||
|
|
||||||
// Display section information
|
|
||||||
Console.WriteLine("\nSections:");
|
|
||||||
for (int i = 0; i < peFile.SectionHeaders.Count; i++)
|
|
||||||
{
|
|
||||||
var section = peFile.SectionHeaders[i];
|
|
||||||
string flags = "";
|
|
||||||
|
|
||||||
if ((section.Characteristics & 0x00000020) != 0) flags += "Code "; // IMAGE_SCN_CNT_CODE
|
|
||||||
if ((section.Characteristics & 0x20000000) != 0) flags += "Exec "; // IMAGE_SCN_MEM_EXECUTE
|
|
||||||
if ((section.Characteristics & 0x40000000) != 0) flags += "Read "; // IMAGE_SCN_MEM_READ
|
|
||||||
if ((section.Characteristics & 0x80000000) != 0) flags += "Write"; // IMAGE_SCN_MEM_WRITE
|
|
||||||
|
|
||||||
Console.WriteLine($" {i}: {section.Name,-8} VA=0x{section.VirtualAddress:X8} Size={section.SizeOfRawData,-8} [{flags}]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DisplayExportedFunctions(PEFormat peFile)
|
|
||||||
{
|
|
||||||
if (peFile.ExportDirectory == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("\nNo exported functions found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("\nExported Functions:");
|
|
||||||
Console.WriteLine($"DLL Name: {peFile.ExportDirectory.DllName}");
|
|
||||||
Console.WriteLine($"Number of Functions: {peFile.ExportDirectory.NumberOfFunctions}");
|
|
||||||
Console.WriteLine($"Number of Names: {peFile.ExportDirectory.NumberOfNames}");
|
|
||||||
|
|
||||||
// Display the first 10 exported functions (if any)
|
|
||||||
int count = Math.Min(10, peFile.ExportedFunctions.Count);
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var function = peFile.ExportedFunctions[i];
|
|
||||||
Console.WriteLine($" {i}: {function.Name} (Ordinal={function.Ordinal}, RVA=0x{function.Address:X8})");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peFile.ExportedFunctions.Count > 10)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" ... and {peFile.ExportedFunctions.Count - 10} more");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DisplayImportedFunctions(PEFormat peFile)
|
|
||||||
{
|
|
||||||
if (peFile.ImportDescriptors.Count == 0)
|
|
||||||
{
|
|
||||||
Console.WriteLine("\nNo imported functions found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("\nImported Functions:");
|
|
||||||
Console.WriteLine($"Number of Imported DLLs: {peFile.ImportDescriptors.Count}");
|
|
||||||
|
|
||||||
// Display the first 5 imported DLLs and their functions
|
|
||||||
int dllCount = Math.Min(5, peFile.ImportDescriptors.Count);
|
|
||||||
for (int i = 0; i < dllCount; i++)
|
|
||||||
{
|
|
||||||
var descriptor = peFile.ImportDescriptors[i];
|
|
||||||
Console.WriteLine($" DLL: {descriptor.DllName}");
|
|
||||||
|
|
||||||
// Display the first 5 functions from this DLL
|
|
||||||
int funcCount = Math.Min(5, descriptor.Functions.Count);
|
|
||||||
for (int j = 0; j < funcCount; j++)
|
|
||||||
{
|
|
||||||
var function = descriptor.Functions[j];
|
|
||||||
if (function.IsOrdinal)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {j}: Ordinal {function.Ordinal}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine($" {j}: {function.Name} (Hint={function.Hint})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.Functions.Count > 5)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" ... and {descriptor.Functions.Count - 5} more");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peFile.ImportDescriptors.Count > 5)
|
|
||||||
{
|
|
||||||
Console.WriteLine($" ... and {peFile.ImportDescriptors.Count - 5} more DLLs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
14
X86Disassembler/ProjectSystem/ProjectPeFile.cs
Normal file
14
X86Disassembler/ProjectSystem/ProjectPeFile.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
namespace X86Disassembler.ProjectSystem;
|
||||||
|
|
||||||
|
public class ProjectPeFile
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string Architecture { get; set; }
|
||||||
|
|
||||||
|
public Address EntryPointAddress { get; set; }
|
||||||
|
|
||||||
|
public Address ImageBase { get; set; }
|
||||||
|
}
|
14
X86Disassembler/ProjectSystem/ProjectPeFileSection.cs
Normal file
14
X86Disassembler/ProjectSystem/ProjectPeFileSection.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using X86Disassembler.Analysers;
|
||||||
|
|
||||||
|
namespace X86Disassembler.ProjectSystem;
|
||||||
|
|
||||||
|
public class ProjectPeFileSection
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public Address VirtualAddress { get; set; }
|
||||||
|
|
||||||
|
public ulong Size { get; set; }
|
||||||
|
|
||||||
|
public SectionFlags Flags { get; set; }
|
||||||
|
}
|
11
X86Disassembler/ProjectSystem/SectionFlags.cs
Normal file
11
X86Disassembler/ProjectSystem/SectionFlags.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace X86Disassembler.ProjectSystem;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum SectionFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Code = 1,
|
||||||
|
Exec = 2,
|
||||||
|
Read = 4,
|
||||||
|
Write = 8
|
||||||
|
}
|
19
X86Disassembler/X86/Constants.cs
Normal file
19
X86Disassembler/X86/Constants.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace X86Disassembler.X86;
|
||||||
|
|
||||||
|
public class Constants
|
||||||
|
{
|
||||||
|
// ModR/M byte masks
|
||||||
|
public const byte MOD_MASK = 0xC0; // 11000000b
|
||||||
|
public const byte REG_MASK = 0x38; // 00111000b
|
||||||
|
public const byte RM_MASK = 0x07; // 00000111b
|
||||||
|
|
||||||
|
// SIB byte masks
|
||||||
|
public const byte SIB_SCALE_MASK = 0xC0; // 11000000b
|
||||||
|
public const byte SIB_INDEX_MASK = 0x38; // 00111000b
|
||||||
|
public const byte SIB_BASE_MASK = 0x07; // 00000111b
|
||||||
|
|
||||||
|
// Register names for different sizes
|
||||||
|
public static readonly string[] RegisterNames16 = {"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"};
|
||||||
|
public static readonly string[] RegisterNames32 = {"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"};
|
||||||
|
|
||||||
|
}
|
87
X86Disassembler/X86/Disassembler.cs
Normal file
87
X86Disassembler/X86/Disassembler.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86;
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Core x86 instruction disassembler
|
||||||
|
/// </summary>
|
||||||
|
public class Disassembler
|
||||||
|
{
|
||||||
|
// The buffer containing the code to disassemble
|
||||||
|
private readonly byte[] _codeBuffer;
|
||||||
|
|
||||||
|
// The length of the buffer
|
||||||
|
private readonly int _length;
|
||||||
|
|
||||||
|
// The base address of the code
|
||||||
|
private readonly ulong _baseAddress;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the Disassembler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeBuffer">The buffer containing the code to disassemble</param>
|
||||||
|
/// <param name="baseAddress">The base address of the code</param>
|
||||||
|
public Disassembler(byte[] codeBuffer, ulong baseAddress)
|
||||||
|
{
|
||||||
|
_codeBuffer = codeBuffer;
|
||||||
|
_length = codeBuffer.Length;
|
||||||
|
_baseAddress = baseAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disassembles the code buffer sequentially and returns all disassembled instructions
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of disassembled instructions</returns>
|
||||||
|
public List<Instruction> Disassemble()
|
||||||
|
{
|
||||||
|
List<Instruction> instructions = new List<Instruction>();
|
||||||
|
|
||||||
|
// Create an instruction decoder
|
||||||
|
InstructionDecoder decoder = new InstructionDecoder(_codeBuffer, _length);
|
||||||
|
|
||||||
|
// Decode instructions until the end of the buffer is reached
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int position = decoder.GetPosition();
|
||||||
|
|
||||||
|
// Check if we've reached the end of the buffer
|
||||||
|
if (!decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the position before decoding to handle prefixes properly
|
||||||
|
int startPosition = position;
|
||||||
|
|
||||||
|
// Decode the instruction
|
||||||
|
Instruction? instruction = decoder.DecodeInstruction();
|
||||||
|
|
||||||
|
if (instruction != null)
|
||||||
|
{
|
||||||
|
// Adjust the instruction address to include the base address
|
||||||
|
instruction.Address = _baseAddress + (uint)startPosition;
|
||||||
|
|
||||||
|
// Add the instruction to the list
|
||||||
|
instructions.Add(instruction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If decoding failed, create a dummy instruction for the unknown byte
|
||||||
|
byte unknownByte = decoder.ReadByte();
|
||||||
|
|
||||||
|
Instruction dummyInstruction = new Instruction
|
||||||
|
{
|
||||||
|
Address = _baseAddress + (uint)position,
|
||||||
|
Type = InstructionType.Unknown,
|
||||||
|
StructuredOperands = [OperandFactory.CreateImmediateOperand(unknownByte, 8),]
|
||||||
|
};
|
||||||
|
|
||||||
|
instructions.Add(dummyInstruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
}
|
33
X86Disassembler/X86/FpuRegisterIndex.cs
Normal file
33
X86Disassembler/X86/FpuRegisterIndex.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace X86Disassembler.X86;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the index values for x87 floating-point unit registers.
|
||||||
|
/// These values correspond to the encoding used in x87 FPU instructions
|
||||||
|
/// for identifying the specific ST(i) register operands.
|
||||||
|
/// </summary>
|
||||||
|
public enum FpuRegisterIndex
|
||||||
|
{
|
||||||
|
/// <summary>FPU register ST(0)</summary>
|
||||||
|
ST0 = 0,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(1)</summary>
|
||||||
|
ST1 = 1,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(2)</summary>
|
||||||
|
ST2 = 2,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(3)</summary>
|
||||||
|
ST3 = 3,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(4)</summary>
|
||||||
|
ST4 = 4,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(5)</summary>
|
||||||
|
ST5 = 5,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(6)</summary>
|
||||||
|
ST6 = 6,
|
||||||
|
|
||||||
|
/// <summary>FPU register ST(7)</summary>
|
||||||
|
ST7 = 7,
|
||||||
|
}
|
71
X86Disassembler/X86/Handlers/Adc/AdcAccumulatorImmHandler.cs
Normal file
71
X86Disassembler/X86/Handlers/Adc/AdcAccumulatorImmHandler.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC AX/EAX, imm16/32 instruction (opcode 0x15)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcAccumulatorImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcAccumulatorImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcAccumulatorImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADC AX/EAX, imm16/32 is encoded as 0x15
|
||||||
|
return opcode == 0x15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADC AX/EAX, imm16/32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Determine operand size based on prefix
|
||||||
|
int operandSize = Decoder.HasOperandSizePrefix() ? 16 : 32;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (operandSize == 16 && !Decoder.CanReadUShort())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (operandSize == 32 && !Decoder.CanReadUInt())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the accumulator register operand (AX or EAX)
|
||||||
|
var accumulatorOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, operandSize);
|
||||||
|
|
||||||
|
// Read and create the immediate operand based on operand size
|
||||||
|
var immOperand = operandSize == 16
|
||||||
|
? OperandFactory.CreateImmediateOperand(Decoder.ReadUInt16(), operandSize)
|
||||||
|
: OperandFactory.CreateImmediateOperand(Decoder.ReadUInt32(), operandSize);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
accumulatorOperand,
|
||||||
|
immOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
64
X86Disassembler/X86/Handlers/Adc/AdcAlImmHandler.cs
Normal file
64
X86Disassembler/X86/Handlers/Adc/AdcAlImmHandler.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC AL, imm8 instruction (0x14)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcAlImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcAlImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcAlImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
return opcode == 0x14;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC AL, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate byte
|
||||||
|
var imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the AL register operand
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL);
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
82
X86Disassembler/X86/Handlers/Adc/AdcImmToRm16Handler.cs
Normal file
82
X86Disassembler/X86/Handlers/Adc/AdcImmToRm16Handler.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m16, imm16 instruction (0x81 /2 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcImmToRm16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcImmToRm16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcImmToRm16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADC r/m16, imm16 is encoded as 0x81 /2 with 0x66 prefix
|
||||||
|
if (opcode != 0x81)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 2 (ADC)
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return reg == 2 && Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADC r/m16, imm16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Read the ModR/M byte, specifying that we're dealing with 16-bit operands
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 16-bit by the ReadModRM16 method
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadUShort())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
ushort imm16 = Decoder.ReadUInt16();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm16, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m16, imm8 (sign-extended) instruction (0x83 /2 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcImmToRm16SignExtendedHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcImmToRm16SignExtendedHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcImmToRm16SignExtendedHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADC r/m16, imm8 (sign-extended) is encoded as 0x83 /2 with 0x66 prefix
|
||||||
|
if (opcode != 0x83)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 2 (ADC)
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return reg == 2 && Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADC r/m16, imm8 (sign-extended) instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// For ADC r/m16, imm8 (sign-extended) (0x83 /2 with 0x66 prefix):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The immediate value is the source operand (sign-extended from 8 to 16 bits)
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 16-bit by the ReadModRM16 method
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value (sign-extended from 8 to 16 bits)
|
||||||
|
short imm16 = (sbyte)Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand((ushort)imm16, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
77
X86Disassembler/X86/Handlers/Adc/AdcImmToRm32Handler.cs
Normal file
77
X86Disassembler/X86/Handlers/Adc/AdcImmToRm32Handler.cs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m32, imm32 instruction (0x81 /2)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcImmToRm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcImmToRm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcImmToRm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x81)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 2 (ADC)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 2; // 2 = ADC
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r/m32, imm32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
if (!Decoder.CanReadUInt())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value in little-endian format
|
||||||
|
var imm32 = Decoder.ReadUInt32();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var immOperand = OperandFactory.CreateImmediateOperand(imm32, 32);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
immOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m32, imm8 (sign-extended) instruction (0x83 /2)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcImmToRm32SignExtendedHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcImmToRm32SignExtendedHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcImmToRm32SignExtendedHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x83)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 2 (ADC)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 2; // 2 = ADC
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r/m32, imm8 (sign-extended) instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value (sign-extended from 8 to 32 bits)
|
||||||
|
sbyte imm32 = (sbyte) Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
OperandFactory.CreateImmediateOperand((uint)imm32)
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
81
X86Disassembler/X86/Handlers/Adc/AdcImmToRm8Handler.cs
Normal file
81
X86Disassembler/X86/Handlers/Adc/AdcImmToRm8Handler.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m8, imm8 instruction (0x80 /2)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcImmToRm8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcImmToRm8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcImmToRm8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x80)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 2 (ADC)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 2; // 2 = ADC
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r/m8, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADC r/m8, imm8 (0x80 /2):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The immediate value is the source operand
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm8, 8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
72
X86Disassembler/X86/Handlers/Adc/AdcR16Rm16Handler.cs
Normal file
72
X86Disassembler/X86/Handlers/Adc/AdcR16Rm16Handler.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r16, r/m16 instruction (0x13 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcR16Rm16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcR16Rm16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcR16Rm16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADC r16, r/m16 is encoded as 0x13 with 0x66 prefix
|
||||||
|
if (opcode != 0x13)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADC r16, r/m16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For ADC r16, r/m16 (0x13 with 0x66 prefix):
|
||||||
|
// - The reg field of the ModR/M byte specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 16-bit by the ReadModRM16 method
|
||||||
|
|
||||||
|
// Create the destination register operand with 16-bit size
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
66
X86Disassembler/X86/Handlers/Adc/AdcR32Rm32Handler.cs
Normal file
66
X86Disassembler/X86/Handlers/Adc/AdcR32Rm32Handler.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r32, r/m32 instruction (0x13)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// Only handle opcode 0x13 when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return opcode == 0x13 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADC r32, r/m32 (0x13):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
64
X86Disassembler/X86/Handlers/Adc/AdcR8Rm8Handler.cs
Normal file
64
X86Disassembler/X86/Handlers/Adc/AdcR8Rm8Handler.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r8, r/m8 instruction (0x12)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcR8Rm8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcR8Rm8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcR8Rm8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
return opcode == 0x12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r8, r/m8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADC r8, r/m8 (0x12):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand8(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
72
X86Disassembler/X86/Handlers/Adc/AdcRm16R16Handler.cs
Normal file
72
X86Disassembler/X86/Handlers/Adc/AdcRm16R16Handler.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m16, r16 instruction (0x11 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcRm16R16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcRm16R16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcRm16R16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADC r/m16, r16 is encoded as 0x11 with 0x66 prefix
|
||||||
|
if (opcode != 0x11)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADC r/m16, r16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For ADC r/m16, r16 (0x11 with 0x66 prefix):
|
||||||
|
// - The reg field of the ModR/M byte specifies the source register
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 16-bit by the ReadModRM16 method
|
||||||
|
|
||||||
|
// Create the source register operand with 16-bit size
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand(reg, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
66
X86Disassembler/X86/Handlers/Adc/AdcRm32R32Handler.cs
Normal file
66
X86Disassembler/X86/Handlers/Adc/AdcRm32R32Handler.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m32, r32 instruction (0x11)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcRm32R32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcRm32R32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcRm32R32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// Only handle opcode 0x11 when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return opcode == 0x11 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r/m32, r32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADC r/m32, r32 (0x11):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The reg field specifies the source register
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
64
X86Disassembler/X86/Handlers/Adc/AdcRm8R8Handler.cs
Normal file
64
X86Disassembler/X86/Handlers/Adc/AdcRm8R8Handler.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Adc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADC r/m8, r8 instruction (0x10)
|
||||||
|
/// </summary>
|
||||||
|
public class AdcRm8R8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AdcRm8R8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AdcRm8R8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
return opcode == 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADC r/m8, r8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Adc;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADC r/m8, r8 (0x10):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The reg field specifies the source register
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand8(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
65
X86Disassembler/X86/Handlers/Add/AddAlImmHandler.cs
Normal file
65
X86Disassembler/X86/Handlers/Add/AddAlImmHandler.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD AL, imm8 instruction (opcode 04)
|
||||||
|
/// </summary>
|
||||||
|
public class AddAlImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddAlImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddAlImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD AL, imm8 is encoded as 04 ib
|
||||||
|
return opcode == 0x04;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD AL, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the destination register operand (AL)
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL);
|
||||||
|
|
||||||
|
// Create the source immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
71
X86Disassembler/X86/Handlers/Add/AddAxImmHandler.cs
Normal file
71
X86Disassembler/X86/Handlers/Add/AddAxImmHandler.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD AX, imm16 instruction (0x05 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AddAxImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddAxImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddAxImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD AX, imm16 is encoded as 0x05 with 0x66 prefix
|
||||||
|
if (opcode != 0x05)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD AX, imm16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadUShort())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
ushort imm16 = Decoder.ReadUInt16();
|
||||||
|
|
||||||
|
// Create the AX register operand
|
||||||
|
var axOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 16);
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var immOperand = OperandFactory.CreateImmediateOperand(imm16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
axOperand,
|
||||||
|
immOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
62
X86Disassembler/X86/Handlers/Add/AddEaxImmHandler.cs
Normal file
62
X86Disassembler/X86/Handlers/Add/AddEaxImmHandler.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD EAX, imm32 instruction (0x05)
|
||||||
|
/// </summary>
|
||||||
|
public class AddEaxImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddEaxImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddEaxImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD EAX, imm32 is encoded as 0x05 without 0x66 prefix
|
||||||
|
if (opcode != 0x05)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
return !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD EAX, imm32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadUInt())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the 32-bit immediate value
|
||||||
|
uint imm32 = Decoder.ReadUInt32();
|
||||||
|
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
OperandFactory.CreateRegisterOperand(RegisterIndex.A),
|
||||||
|
OperandFactory.CreateImmediateOperand(imm32)
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
83
X86Disassembler/X86/Handlers/Add/AddImmToRm16Handler.cs
Normal file
83
X86Disassembler/X86/Handlers/Add/AddImmToRm16Handler.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m16, imm16 instruction (opcode 81 /0 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AddImmToRm16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddImmToRm16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddImmToRm16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD r/m16, imm16 is encoded as 0x81 with 0x66 prefix
|
||||||
|
if (opcode != 0x81)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
if (!Decoder.HasOperandSizePrefix())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 0 (ADD)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 0; // 0 = ADD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADD r/m16, imm16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadUShort())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
ushort imm16 = Decoder.ReadUInt16();
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
OperandFactory.CreateImmediateOperand(imm16)
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m16, imm8 instruction (opcode 83 /0 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AddImmToRm16SignExtendedHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddImmToRm16SignExtendedHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddImmToRm16SignExtendedHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD r/m16, imm8 is encoded as 0x83 with 0x66 prefix
|
||||||
|
if (opcode != 0x83)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
if (!Decoder.HasOperandSizePrefix())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 0 (ADD)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 0; // 0 = ADD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a ADD r/m16, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanRead(1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value (sign-extended from 8-bit to 16-bit)
|
||||||
|
sbyte imm8 = (sbyte)Decoder.ReadByte();
|
||||||
|
short signExtendedImm = imm8;
|
||||||
|
uint immValue = (ushort)signExtendedImm; // Convert to uint for the operand factory
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
OperandFactory.CreateImmediateOperand(immValue)
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
73
X86Disassembler/X86/Handlers/Add/AddImmToRm32Handler.cs
Normal file
73
X86Disassembler/X86/Handlers/Add/AddImmToRm32Handler.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m32, imm32 instruction (0x81 /0)
|
||||||
|
/// </summary>
|
||||||
|
public class AddImmToRm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddImmToRm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddImmToRm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x81)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 0 (ADD)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 0; // 0 = ADD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r/m32, imm32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the mnemonic
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
if (!Decoder.CanReadUInt())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value in little-endian format
|
||||||
|
var imm = Decoder.ReadUInt32();
|
||||||
|
|
||||||
|
instruction.StructuredOperands = [
|
||||||
|
destOperand,
|
||||||
|
OperandFactory.CreateImmediateOperand(imm)
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m32, imm8 (sign-extended) instruction (0x83 /0)
|
||||||
|
/// </summary>
|
||||||
|
public class AddImmToRm32SignExtendedHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddImmToRm32SignExtendedHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddImmToRm32SignExtendedHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x83)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 0 (ADD)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 0; // 0 = ADD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r/m32, imm8 (sign-extended) instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value as a signed byte and automatically sign-extend it to int
|
||||||
|
sbyte imm = (sbyte) Decoder.ReadByte();
|
||||||
|
|
||||||
|
instruction.StructuredOperands = [
|
||||||
|
destOperand,
|
||||||
|
OperandFactory.CreateImmediateOperand((uint)imm),
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
78
X86Disassembler/X86/Handlers/Add/AddImmToRm8Handler.cs
Normal file
78
X86Disassembler/X86/Handlers/Add/AddImmToRm8Handler.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m8, imm8 instruction (0x80 /0)
|
||||||
|
/// </summary>
|
||||||
|
public class AddImmToRm8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddImmToRm8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddImmToRm8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x80)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 0; // 0 = ADD
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r/m8, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type and mnemonic
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte, specifying that we're dealing with 8-bit operands
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm8, 8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
72
X86Disassembler/X86/Handlers/Add/AddR16Rm16Handler.cs
Normal file
72
X86Disassembler/X86/Handlers/Add/AddR16Rm16Handler.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r16, r/m16 instruction (opcode 03 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AddR16Rm16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddR16Rm16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddR16Rm16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD r16, r/m16 is encoded as 0x03 with 0x66 prefix
|
||||||
|
if (opcode != 0x03)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r16, r/m16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For ADD r16, r/m16 (0x03 with 0x66 prefix):
|
||||||
|
// - The reg field of the ModR/M byte specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 16-bit by the ReadModRM16 method
|
||||||
|
|
||||||
|
// Create the destination register operand with 16-bit size
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
66
X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs
Normal file
66
X86Disassembler/X86/Handlers/Add/AddR32Rm32Handler.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r32, r/m32 instruction (0x03)
|
||||||
|
/// </summary>
|
||||||
|
public class AddR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// Only handle opcode 0x03 when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return opcode == 0x03 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADD r32, r/m32 (0x03):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
// The sourceOperand is already created by ModRMDecoder based on mod and rm fields
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the destination register operand from the reg field
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
65
X86Disassembler/X86/Handlers/Add/AddR8Rm8Handler.cs
Normal file
65
X86Disassembler/X86/Handlers/Add/AddR8Rm8Handler.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r8, r/m8 instruction (opcode 02)
|
||||||
|
/// </summary>
|
||||||
|
public class AddR8Rm8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddR8Rm8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddR8Rm8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD r8, r/m8 is encoded as 02 /r
|
||||||
|
return opcode == 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r8, r/m8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADD r8, r/m8 (02 /r):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Create the destination register operand using the 8-bit register type
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand8(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
72
X86Disassembler/X86/Handlers/Add/AddRm16R16Handler.cs
Normal file
72
X86Disassembler/X86/Handlers/Add/AddRm16R16Handler.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m16, r16 instruction (opcode 01 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AddRm16R16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddRm16R16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddRm16R16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD r/m16, r16 is encoded as 0x01 with 0x66 prefix
|
||||||
|
if (opcode != 0x01)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r/m16, r16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For ADD r/m16, r16 (0x01 with 0x66 prefix):
|
||||||
|
// - The reg field of the ModR/M byte specifies the source register
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 16-bit by the ReadModRM16 method
|
||||||
|
|
||||||
|
// Create the source register operand with 16-bit size
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand(reg, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
66
X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs
Normal file
66
X86Disassembler/X86/Handlers/Add/AddRm32R32Handler.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m32, r32 instruction (0x01)
|
||||||
|
/// </summary>
|
||||||
|
public class AddRm32R32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddRm32R32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddRm32R32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// Only handle opcode 0x01 when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return opcode == 0x01 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r/m32, r32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADD r/m32, r32 (0x01):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The reg field specifies the source register
|
||||||
|
// The destinationOperand is already created by ModRMDecoder based on mod and rm fields
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the source register operand from the reg field
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
65
X86Disassembler/X86/Handlers/Add/AddRm8R8Handler.cs
Normal file
65
X86Disassembler/X86/Handlers/Add/AddRm8R8Handler.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for ADD r/m8, r8 instruction (opcode 00)
|
||||||
|
/// </summary>
|
||||||
|
public class AddRm8R8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AddRm8R8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AddRm8R8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// ADD r/m8, r8 is encoded as 00 /r
|
||||||
|
return opcode == 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an ADD r/m8, r8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Add;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For ADD r/m8, r8 (00 /r):
|
||||||
|
// - The reg field specifies the source register
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand8(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
64
X86Disassembler/X86/Handlers/And/AndAlImmHandler.cs
Normal file
64
X86Disassembler/X86/Handlers/And/AndAlImmHandler.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND AL, imm8 instruction (0x24)
|
||||||
|
/// </summary>
|
||||||
|
public class AndAlImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndAlImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndAlImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
return opcode == 0x24;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND AL, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Create the destination register operand (AL)
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand8(RegisterIndex8.AL);
|
||||||
|
|
||||||
|
// Read immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read immediate value
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the source immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm8, 8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
71
X86Disassembler/X86/Handlers/And/AndAxImmHandler.cs
Normal file
71
X86Disassembler/X86/Handlers/And/AndAxImmHandler.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND AX, imm16 instruction (0x25 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AndAxImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndAxImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndAxImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// AND AX, imm16 is encoded as 0x25 with 0x66 prefix
|
||||||
|
if (opcode != 0x25)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND AX, imm16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadUShort())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
ushort imm16 = Decoder.ReadUInt16();
|
||||||
|
|
||||||
|
// Create the AX register operand
|
||||||
|
var axOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 16);
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var immOperand = OperandFactory.CreateImmediateOperand(imm16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
axOperand,
|
||||||
|
immOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
71
X86Disassembler/X86/Handlers/And/AndEaxImmHandler.cs
Normal file
71
X86Disassembler/X86/Handlers/And/AndEaxImmHandler.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND EAX, imm32 instruction (0x25)
|
||||||
|
/// </summary>
|
||||||
|
public class AndEaxImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndEaxImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndEaxImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// AND EAX, imm32 is encoded as 0x25 without 0x66 prefix
|
||||||
|
if (opcode != 0x25)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
return !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND EAX, imm32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Create the destination register operand (EAX)
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(RegisterIndex.A, 32);
|
||||||
|
|
||||||
|
// Read immediate value
|
||||||
|
if (!Decoder.CanReadUInt())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read immediate value
|
||||||
|
uint imm32 = Decoder.ReadUInt32();
|
||||||
|
|
||||||
|
// Create the source immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm32, 32);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
84
X86Disassembler/X86/Handlers/And/AndImmToRm16Handler.cs
Normal file
84
X86Disassembler/X86/Handlers/And/AndImmToRm16Handler.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m16, imm16 instruction (0x81 /4 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AndImmToRm16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndImmToRm16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndImmToRm16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// AND r/m16, imm16 is encoded as 0x81 with 0x66 prefix
|
||||||
|
if (opcode != 0x81)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
if (!Decoder.HasOperandSizePrefix())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 4 (AND)
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
return reg == 4; // 4 = AND
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m16, imm16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Read the ModR/M byte to get the destination operand
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadUShort())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
ushort imm16 = Decoder.ReadUInt16();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m16, imm8 instruction (0x83 /4 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AndImmToRm16SignExtendedHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndImmToRm16SignExtendedHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndImmToRm16SignExtendedHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// AND r/m16, imm8 is encoded as 0x83 with 0x66 prefix
|
||||||
|
if (opcode != 0x83)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
if (!Decoder.HasOperandSizePrefix())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 4 (AND)
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
return reg == 4; // 4 = AND
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m16, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Read the ModR/M byte to get the destination operand
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value and sign-extend it to 16 bits
|
||||||
|
sbyte imm8 = (sbyte)Decoder.ReadByte();
|
||||||
|
short signExtendedImm = imm8;
|
||||||
|
ushort imm16 = (ushort)signExtendedImm;
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
78
X86Disassembler/X86/Handlers/And/AndImmToRm32Handler.cs
Normal file
78
X86Disassembler/X86/Handlers/And/AndImmToRm32Handler.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m32, imm32 instruction (0x81 /4)
|
||||||
|
/// </summary>
|
||||||
|
public class AndImmToRm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndImmToRm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndImmToRm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x81)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 4 (AND)
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
return reg == 4; // 4 = AND
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m32, imm32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
var (_, _, _, destOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
if (!Decoder.CanReadUInt())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value in little-endian format
|
||||||
|
var imm = Decoder.ReadUInt32();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var immOperand = OperandFactory.CreateImmediateOperand(imm);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
immOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m32, imm8 (sign-extended) instruction (0x83 /4)
|
||||||
|
/// </summary>
|
||||||
|
public class AndImmToRm32SignExtendedHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndImmToRm32SignExtendedHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndImmToRm32SignExtendedHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x83)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte to check the reg field (bits 5-3)
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
// reg = 4 means AND operation
|
||||||
|
return reg == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m32, imm8 (sign-extended) instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For AND r/m32, imm8 (sign-extended) (0x83 /4):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The immediate value is the source operand (sign-extended from 8 to 32 bits)
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false; // Not enough bytes for the immediate value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value as a signed byte and automatically sign-extend it to int
|
||||||
|
sbyte imm = (sbyte)Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the source immediate operand with the sign-extended value
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand((uint)imm);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
83
X86Disassembler/X86/Handlers/And/AndImmToRm8Handler.cs
Normal file
83
X86Disassembler/X86/Handlers/And/AndImmToRm8Handler.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m8, imm8 instruction (0x80 /4)
|
||||||
|
/// </summary>
|
||||||
|
public class AndImmToRm8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndImmToRm8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndImmToRm8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
if (opcode != 0x80)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte to check the reg field (bits 5-3)
|
||||||
|
var reg = ModRMDecoder.PeakModRMReg();
|
||||||
|
|
||||||
|
// reg = 4 means AND operation
|
||||||
|
return reg == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m8, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Read the ModR/M byte, specifying that we're dealing with 8-bit operands
|
||||||
|
// For AND r/m8, imm8 (0x80 /4):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The immediate value is the source operand
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false; // Not enough bytes for the immediate value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate value
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the source immediate operand
|
||||||
|
var sourceOperand = OperandFactory.CreateImmediateOperand(imm8, 8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
65
X86Disassembler/X86/Handlers/And/AndMemRegHandler.cs
Normal file
65
X86Disassembler/X86/Handlers/And/AndMemRegHandler.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m32, r32 instruction (0x21)
|
||||||
|
/// </summary>
|
||||||
|
public class AndMemRegHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndMemRegHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndMemRegHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// Only handle opcode 0x21 when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return opcode == 0x21 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m32, r32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For AND r/m32, r32 (0x21):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The reg field specifies the source register
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the source register operand
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand(reg, 32);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
70
X86Disassembler/X86/Handlers/And/AndR16Rm16Handler.cs
Normal file
70
X86Disassembler/X86/Handlers/And/AndR16Rm16Handler.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r16, r/m16 instruction (0x23 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AndR16Rm16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndR16Rm16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndR16Rm16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// AND r16, r/m16 is encoded as 0x23 with 0x66 prefix
|
||||||
|
if (opcode != 0x23)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r16, r/m16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For AND r16, r/m16 (0x23 with 0x66 prefix):
|
||||||
|
// - The reg field of the ModR/M byte specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Create the destination register operand with 16-bit size
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
65
X86Disassembler/X86/Handlers/And/AndR32Rm32Handler.cs
Normal file
65
X86Disassembler/X86/Handlers/And/AndR32Rm32Handler.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r32, r/m32 instruction (0x23)
|
||||||
|
/// </summary>
|
||||||
|
public class AndR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// Only handle opcode 0x23 when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return opcode == 0x23 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For AND r32, r/m32 (0x23):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the destination register operand
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg, 32);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
78
X86Disassembler/X86/Handlers/And/AndR8Rm8Handler.cs
Normal file
78
X86Disassembler/X86/Handlers/And/AndR8Rm8Handler.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r8, r/m8 instruction (0x22)
|
||||||
|
/// </summary>
|
||||||
|
public class AndR8Rm8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndR8Rm8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndR8Rm8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
return opcode == 0x22;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r8, r/m8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte, specifying that we're dealing with 8-bit operands
|
||||||
|
var (mod, reg, rm, srcOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Create the destination register operand using the 8-bit register type
|
||||||
|
var destOperand = OperandFactory.CreateRegisterOperand8(reg);
|
||||||
|
|
||||||
|
// For mod == 3, both operands are registers
|
||||||
|
if (mod == 3)
|
||||||
|
{
|
||||||
|
// Create a register operand for the r/m field using the 8-bit register type
|
||||||
|
var rmOperand = OperandFactory.CreateRegisterOperand8(rm);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
rmOperand
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else // Memory operand
|
||||||
|
{
|
||||||
|
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
srcOperand
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
70
X86Disassembler/X86/Handlers/And/AndRm16R16Handler.cs
Normal file
70
X86Disassembler/X86/Handlers/And/AndRm16R16Handler.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using X86Disassembler.X86.Operands;
|
||||||
|
|
||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m16, r16 instruction (0x21 with 0x66 prefix)
|
||||||
|
/// </summary>
|
||||||
|
public class AndRm16R16Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndRm16R16Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndRm16R16Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// AND r/m16, r16 is encoded as 0x21 with 0x66 prefix
|
||||||
|
if (opcode != 0x21)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is present
|
||||||
|
return Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m16, r16 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
// Check if we can read the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For AND r/m16, r16 (0x21 with 0x66 prefix):
|
||||||
|
// - The reg field of the ModR/M byte specifies the source register
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM16();
|
||||||
|
|
||||||
|
// Create the source register operand with 16-bit size
|
||||||
|
var sourceOperand = OperandFactory.CreateRegisterOperand(reg, 16);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
78
X86Disassembler/X86/Handlers/And/AndRm8R8Handler.cs
Normal file
78
X86Disassembler/X86/Handlers/And/AndRm8R8Handler.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.And;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for AND r/m8, r8 instruction (0x20)
|
||||||
|
/// </summary>
|
||||||
|
public class AndRm8R8Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the AndRm8R8Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public AndRm8R8Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
return opcode == 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes an AND r/m8, r8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.And;
|
||||||
|
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte, specifying that we're dealing with 8-bit operands
|
||||||
|
var (mod, reg, rm, destOperand) = ModRMDecoder.ReadModRM8();
|
||||||
|
|
||||||
|
// Create the source register operand using the 8-bit register type
|
||||||
|
var srcOperand = OperandFactory.CreateRegisterOperand8(reg);
|
||||||
|
|
||||||
|
// For mod == 3, both operands are registers
|
||||||
|
if (mod == 3)
|
||||||
|
{
|
||||||
|
// Create a register operand for the r/m field using the 8-bit register type
|
||||||
|
var rmOperand = OperandFactory.CreateRegisterOperand8(rm);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
rmOperand,
|
||||||
|
srcOperand
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else // Memory operand
|
||||||
|
{
|
||||||
|
// Note: The operand size is already set to 8-bit by the ReadModRM8 method
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destOperand,
|
||||||
|
srcOperand
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
84
X86Disassembler/X86/Handlers/Bit/BsfR32Rm32Handler.cs
Normal file
84
X86Disassembler/X86/Handlers/Bit/BsfR32Rm32Handler.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Bit;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for BSF r32, r/m32 instruction (0F BC)
|
||||||
|
/// </summary>
|
||||||
|
public class BsfR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BsfR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public BsfR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// BSF r32, r/m32 is a two-byte opcode: 0F BC
|
||||||
|
if (opcode != 0x0F)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the second opcode byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the second byte is BC
|
||||||
|
var secondByte = Decoder.PeakByte();
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return secondByte == 0xBC && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a BSF r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Bsf;
|
||||||
|
|
||||||
|
// Read the second opcode byte (BC)
|
||||||
|
Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For BSF r32, r/m32 (0F BC):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
84
X86Disassembler/X86/Handlers/Bit/BsrR32Rm32Handler.cs
Normal file
84
X86Disassembler/X86/Handlers/Bit/BsrR32Rm32Handler.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Bit;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for BSR r32, r/m32 instruction (0F BD)
|
||||||
|
/// </summary>
|
||||||
|
public class BsrR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BsrR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public BsrR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// BSR r32, r/m32 is a two-byte opcode: 0F BD
|
||||||
|
if (opcode != 0x0F)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the second opcode byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the second byte is BD
|
||||||
|
var secondByte = Decoder.PeakByte();
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return secondByte == 0xBD && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a BSR r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Bsr;
|
||||||
|
|
||||||
|
// Read the second opcode byte (BD)
|
||||||
|
Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For BSR r32, r/m32 (0F BD):
|
||||||
|
// - The reg field specifies the destination register
|
||||||
|
// - The r/m field with mod specifies the source operand (register or memory)
|
||||||
|
var (_, reg, _, sourceOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var destinationOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
sourceOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
84
X86Disassembler/X86/Handlers/Bit/BtR32Rm32Handler.cs
Normal file
84
X86Disassembler/X86/Handlers/Bit/BtR32Rm32Handler.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Bit;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for BT r32, r/m32 instruction (0F A3)
|
||||||
|
/// </summary>
|
||||||
|
public class BtR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BtR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public BtR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// BT r32, r/m32 is a two-byte opcode: 0F A3
|
||||||
|
if (opcode != 0x0F)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the second opcode byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the second byte is A3
|
||||||
|
var secondByte = Decoder.PeakByte();
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return secondByte == 0xA3 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a BT r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Bt;
|
||||||
|
|
||||||
|
// Read the second opcode byte (A3)
|
||||||
|
Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For BT r/m32, r32 (0F A3):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The reg field specifies the bit index register
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var bitIndexOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
bitIndexOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
101
X86Disassembler/X86/Handlers/Bit/BtRm32ImmHandler.cs
Normal file
101
X86Disassembler/X86/Handlers/Bit/BtRm32ImmHandler.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Bit;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for BT r/m32, imm8 instruction (0F BA /4)
|
||||||
|
/// </summary>
|
||||||
|
public class BtRm32ImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BtRm32ImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public BtRm32ImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// BT r/m32, imm8 is encoded as 0F BA /4
|
||||||
|
if (opcode != 0x0F)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the second opcode byte
|
||||||
|
if (!Decoder.CanRead(2))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (secondByte, modRm) = Decoder.PeakTwoBytes();
|
||||||
|
|
||||||
|
// Check if the second byte is BA
|
||||||
|
if (secondByte != 0xBA)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 4 (BT)
|
||||||
|
var reg = ModRMDecoder.GetRegFromModRM(modRm);
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return reg == 4 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a BT r/m32, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Bt;
|
||||||
|
|
||||||
|
// Read the second opcode byte (BA)
|
||||||
|
Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For BT r/m32, imm8 (0F BA /4):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The immediate value specifies the bit index
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate byte for the bit position
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var bitIndexOperand = OperandFactory.CreateImmediateOperand(imm8, 8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
bitIndexOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
84
X86Disassembler/X86/Handlers/Bit/BtcR32Rm32Handler.cs
Normal file
84
X86Disassembler/X86/Handlers/Bit/BtcR32Rm32Handler.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Bit;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for BTC r32, r/m32 instruction (0F BB)
|
||||||
|
/// </summary>
|
||||||
|
public class BtcR32Rm32Handler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BtcR32Rm32Handler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public BtcR32Rm32Handler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// BTC r32, r/m32 is a two-byte opcode: 0F BB
|
||||||
|
if (opcode != 0x0F)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the second opcode byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the second byte is BB
|
||||||
|
var secondByte = Decoder.PeakByte();
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return secondByte == 0xBB && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a BTC r32, r/m32 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Btc;
|
||||||
|
|
||||||
|
// Read the second opcode byte (BB)
|
||||||
|
Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For BTC r/m32, r32 (0F BB):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The reg field specifies the bit index register
|
||||||
|
var (_, reg, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Create the register operand for the reg field
|
||||||
|
var bitIndexOperand = OperandFactory.CreateRegisterOperand(reg);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
bitIndexOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
101
X86Disassembler/X86/Handlers/Bit/BtcRm32ImmHandler.cs
Normal file
101
X86Disassembler/X86/Handlers/Bit/BtcRm32ImmHandler.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
namespace X86Disassembler.X86.Handlers.Bit;
|
||||||
|
|
||||||
|
using Operands;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler for BTC r/m32, imm8 instruction (0F BA /7)
|
||||||
|
/// </summary>
|
||||||
|
public class BtcRm32ImmHandler : InstructionHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the BtcRm32ImmHandler class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="decoder">The instruction decoder that owns this handler</param>
|
||||||
|
public BtcRm32ImmHandler(InstructionDecoder decoder)
|
||||||
|
: base(decoder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this handler can decode the given opcode
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode to check</param>
|
||||||
|
/// <returns>True if this handler can decode the opcode</returns>
|
||||||
|
public override bool CanHandle(byte opcode)
|
||||||
|
{
|
||||||
|
// BTC r/m32, imm8 is encoded as 0F BA /7
|
||||||
|
if (opcode != 0x0F)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have enough bytes to read the second opcode byte
|
||||||
|
if (!Decoder.CanRead(2))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (secondByte, modRm) = Decoder.PeakTwoBytes();
|
||||||
|
|
||||||
|
// Check if the second byte is BA
|
||||||
|
if (secondByte != 0xBA)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the reg field of the ModR/M byte is 7 (BTC)
|
||||||
|
var reg = ModRMDecoder.GetRegFromModRM(modRm);
|
||||||
|
|
||||||
|
// Only handle when the operand size prefix is NOT present
|
||||||
|
// This ensures 16-bit handlers get priority when the prefix is present
|
||||||
|
return reg == 7 && !Decoder.HasOperandSizePrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes a BTC r/m32, imm8 instruction
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcode">The opcode of the instruction</param>
|
||||||
|
/// <param name="instruction">The instruction object to populate</param>
|
||||||
|
/// <returns>True if the instruction was successfully decoded</returns>
|
||||||
|
public override bool Decode(byte opcode, Instruction instruction)
|
||||||
|
{
|
||||||
|
// Set the instruction type
|
||||||
|
instruction.Type = InstructionType.Btc;
|
||||||
|
|
||||||
|
// Read the second opcode byte (BA)
|
||||||
|
Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the ModR/M byte
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the ModR/M byte
|
||||||
|
// For BTC r/m32, imm8 (0F BA /7):
|
||||||
|
// - The r/m field with mod specifies the destination operand (register or memory)
|
||||||
|
// - The immediate value specifies the bit index
|
||||||
|
var (_, _, _, destinationOperand) = ModRMDecoder.ReadModRM();
|
||||||
|
|
||||||
|
// Check if we have enough bytes for the immediate value
|
||||||
|
if (!Decoder.CanReadByte())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the immediate byte for the bit position
|
||||||
|
byte imm8 = Decoder.ReadByte();
|
||||||
|
|
||||||
|
// Create the immediate operand
|
||||||
|
var bitIndexOperand = OperandFactory.CreateImmediateOperand(imm8, 8);
|
||||||
|
|
||||||
|
// Set the structured operands
|
||||||
|
instruction.StructuredOperands =
|
||||||
|
[
|
||||||
|
destinationOperand,
|
||||||
|
bitIndexOperand
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user