Skip to content

Commit 6d3c870

Browse files
committed
Added NodeDissector.
1 parent 10b81f4 commit 6d3c870

12 files changed

+389
-61
lines changed

Memory/Memory.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,26 @@ public byte ReadByte(IntPtr offset)
6767

6868
public byte ReadByte(int offset)
6969
{
70+
if (Offset + offset < 0 || Offset + offset > data.Length)
71+
{
72+
throw new IndexOutOfRangeException();
73+
}
74+
7075
return data[Offset + offset];
7176
}
7277

78+
public byte[] ReadBytes(int offset, int length)
79+
{
80+
if (Offset + offset < 0 || Offset + offset + length > data.Length)
81+
{
82+
throw new IndexOutOfRangeException();
83+
}
84+
85+
var b = new byte[length];
86+
Array.Copy(data, Offset + offset, b, 0, length);
87+
return b;
88+
}
89+
7390
public T ReadObject<T>(IntPtr offset) where T : struct
7491
{
7592
return ReadObject<T>(offset.ToInt32());
@@ -78,7 +95,7 @@ public T ReadObject<T>(IntPtr offset) where T : struct
7895
public T ReadObject<T>(int offset) where T : struct
7996
{
8097
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
81-
var obj = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject() + offset, typeof(T));
98+
var obj = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject() + Offset + offset, typeof(T));
8299
handle.Free();
83100

84101
return obj;

Memory/NodeDissector.cs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics.Contracts;
4+
using ReClassNET.Nodes;
5+
using ReClassNET.Util;
6+
7+
namespace ReClassNET.Memory
8+
{
9+
public class NodeDissector
10+
{
11+
public static void DissectNodes(IEnumerable<BaseHexNode> nodes, MemoryBuffer memory)
12+
{
13+
Contract.Requires(nodes != null);
14+
Contract.Requires(memory != null);
15+
16+
foreach (var node in nodes)
17+
{
18+
var type = GuessType(node, memory);
19+
if (type != null)
20+
{
21+
node.ParentNode.ReplaceChildNode(node, type);
22+
}
23+
}
24+
}
25+
26+
public static Type GuessType(BaseHexNode node, MemoryBuffer memory)
27+
{
28+
Contract.Requires(node != null);
29+
Contract.Requires(memory != null);
30+
31+
var offset = node.Offset.ToInt32();
32+
var is4ByteAligned = offset % 4 == 0;
33+
var is8ByteAligned = offset % 8 == 0;
34+
35+
// The node is not aligned, skip it.
36+
if (!is4ByteAligned)
37+
{
38+
return null;
39+
}
40+
41+
var data64 = memory.ReadObject<UInt64FloatDoubleData>(offset);
42+
var data32 = memory.ReadObject<UInt32FloatData>(offset);
43+
44+
/*var raw = memory.ReadBytes(offset, node.MemorySize);
45+
if (raw.IsPrintableData())
46+
{
47+
return typeof(UTF8TextNode);
48+
}
49+
else if (raw.EveryNth(2).IsPrintableData())
50+
{
51+
return typeof(UTF16TextNode);
52+
}*/
53+
54+
if (is8ByteAligned)
55+
{
56+
#if WIN64
57+
var pointerType = GuessPointerType(data64.IntPtr, memory);
58+
if (pointerType != null)
59+
{
60+
return pointerType;
61+
}
62+
#endif
63+
}
64+
65+
if (is4ByteAligned)
66+
{
67+
#if WIN32
68+
var pointerType = GuessPointerType(data32.IntPtr, memory);
69+
if (pointerType != null)
70+
{
71+
return pointerType;
72+
}
73+
#endif
74+
75+
// 0 could be anything.
76+
if (data32.IntValue != 0)
77+
{
78+
// If the data represents a reasonable range, it could be a float.
79+
if (-99999.0f <= data32.FloatValue && data32.FloatValue <= 99999.0f && !data32.FloatValue.IsNearlyEqual(0.0f, 0.001f))
80+
{
81+
return typeof(FloatNode);
82+
}
83+
84+
if (-99999 <= data32.IntValue && data32.IntValue <= 99999)
85+
{
86+
return typeof(Int32Node);
87+
}
88+
}
89+
}
90+
91+
if (is8ByteAligned)
92+
{
93+
if (data64.LongValue != 0)
94+
{
95+
// If the data represents a reasonable range, it could be a double.
96+
if (-99999.0 <= data64.DoubleValue && data64.DoubleValue <= 99999.0 && !data64.DoubleValue.IsNearlyEqual(0.0, 0.001))
97+
{
98+
return typeof(DoubleNode);
99+
}
100+
}
101+
}
102+
103+
return null;
104+
}
105+
106+
private static Type GuessPointerType(IntPtr address, MemoryBuffer memory)
107+
{
108+
Contract.Requires(memory != null);
109+
110+
if (address.IsNull())
111+
{
112+
return null;
113+
}
114+
115+
var section = memory.Process.GetSectionToPointer(address);
116+
if (section != null) // If the address points to a section it's valid memory.
117+
{
118+
if (section.Category == RemoteProcess.SectionCategory.Code) // If the section contains code, it should be a function pointer.
119+
{
120+
return typeof(FunctionPtrNode);
121+
}
122+
else if (section.Category == RemoteProcess.SectionCategory.Data) // If the section contains data, it is at least a pointer to a class or something.
123+
{
124+
// Check if it is a vtable. Check if the first 3 values are pointers to a code section.
125+
bool valid = true;
126+
for (var i = 0; i < 3; ++i)
127+
{
128+
var pointee = memory.Process.ReadRemoteObject<IntPtr>(address);
129+
if (memory.Process.GetSectionToPointer(pointee)?.Category != RemoteProcess.SectionCategory.Code)
130+
{
131+
valid = false;
132+
break;
133+
}
134+
}
135+
if (valid)
136+
{
137+
return typeof(VTableNode);
138+
}
139+
140+
// Check if it is a string.
141+
var data = memory.Process.ReadRemoteMemory(address, IntPtr.Size);
142+
if (data.IsPrintableData())
143+
{
144+
return typeof(UTF8TextPtrNode);
145+
}
146+
else if (data.EveryNth(2).IsPrintableData())
147+
{
148+
return typeof(UTF16TextPtrNode);
149+
}
150+
/*else if (!data.EveryNth(4).Where(b => !((char)b).IsPrintable()).Any())
151+
{
152+
return typeof(UTF32TextPtrNode);
153+
}*/
154+
155+
// Now it could be a pointer to something else but we can't tell. :(
156+
//return typeof(ClassPtrNode);
157+
}
158+
}
159+
160+
return null;
161+
}
162+
}
163+
}

Memory/RemoteProcess.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,19 @@ public class Module
3838
public string Path;
3939
}
4040

41+
public enum SectionCategory
42+
{
43+
Unknown,
44+
Code,
45+
Data
46+
}
47+
4148
public class Section
4249
{
4350
public IntPtr Start;
4451
public IntPtr End;
4552
public string Name;
46-
public string Category;
53+
public SectionCategory Category;
4754
public NativeMethods.StateEnum State;
4855
public NativeMethods.AllocationProtectEnum Protection;
4956
public NativeMethods.TypeEnum Type;
@@ -368,17 +375,32 @@ public bool WriteRemoteMemory<T>(IntPtr address, T value) where T : struct
368375

369376
#endregion
370377

378+
public Section GetSectionToPointer(IntPtr address)
379+
{
380+
return sections
381+
.Where(s => s.Category != SectionCategory.Unknown)
382+
.Where(s => address.InRange(s.Start, s.End))
383+
.FirstOrDefault();
384+
}
385+
386+
public Module GetModuleToPointer(IntPtr address)
387+
{
388+
return modules
389+
.Where(m => address.InRange(m.Start, m.End))
390+
.FirstOrDefault();
391+
}
392+
371393
/// <summary>Tries to map the given address to a section or a module of the process.</summary>
372394
/// <param name="address">The address to map.</param>
373395
/// <returns>The named address or null if no mapping exists.</returns>
374396
public string GetNamedAddress(IntPtr address)
375397
{
376-
var section = sections.Where(s => s.Category != null).Where(s => address.InRange(s.Start, s.End)).FirstOrDefault();
398+
var section = GetSectionToPointer(address);
377399
if (section != null)
378400
{
379401
return $"<{section.Category}>{section.ModuleName}.{address.ToString("X")}";
380402
}
381-
var module = modules.Where(m => address.InRange(m.Start, m.End)).FirstOrDefault();
403+
var module = GetModuleToPointer(address);
382404
if (module != null)
383405
{
384406
return $"{module.Name}.{address.ToString("X")}";
@@ -428,13 +450,13 @@ public Task UpdateProcessInformationsAsync()
428450
{
429451
case ".text":
430452
case "code":
431-
section.Category = "CODE";
453+
section.Category = SectionCategory.Code;
432454
break;
433455
case ".data":
434456
case "data":
435457
case ".rdata":
436458
case ".idata":
437-
section.Category = "DATA";
459+
section.Category = SectionCategory.Data;
438460
break;
439461
}
440462
sections.Add(section);

Nodes/BaseContainerNode.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,37 @@ public int FindNodeIndex(BaseNode node)
3434
return Nodes.FindIndex(n => n == node);
3535
}
3636

37+
public virtual BaseNode ReplaceChildNode(BaseNode child, Type nodeType)
38+
{
39+
return ReplaceChildNode(FindNodeIndex(child), nodeType);
40+
}
41+
42+
/// <summary>Replaces the child at the specific position with the provided node.</summary>
43+
/// <param name="index">Zero-based position.</param>
44+
/// <param name="node">The node to add.</param>
45+
/// <returns>True if it succeeds, false if it fails.</returns>
46+
public virtual BaseNode ReplaceChildNode(int index, Type nodeType)
47+
{
48+
Contract.Requires(nodeType != null);
49+
Contract.Requires(nodeType.IsSubclassOf(typeof(BaseNode)));
50+
51+
var node = Activator.CreateInstance(nodeType) as BaseNode;
52+
53+
node.Intialize();
54+
55+
if (ReplaceChildNode(index, node))
56+
{
57+
return node;
58+
}
59+
60+
return null;
61+
}
62+
3763
/// <summary>Replaces the child at the specific position with the provided node.</summary>
3864
/// <param name="index">Zero-based position.</param>
3965
/// <param name="node">The node to add.</param>
4066
/// <returns>True if it succeeds, false if it fails.</returns>
41-
public virtual bool ReplaceChildNode(BaseNode child, BaseNode node)
67+
public bool ReplaceChildNode(BaseNode child, BaseNode node)
4268
{
4369
return ReplaceChildNode(FindNodeIndex(child), node);
4470
}

Nodes/BaseHexCommentNode.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,12 @@ protected int AddComment(ViewInfo view, int x, int y, float fvalue, IntPtr ivalu
7070
var data = view.Memory.Process.ReadRemoteMemory(ivalue, 64);
7171

7272
// First check if it could be an UTF8 string and if not try UTF16.
73-
if (!data.Take(IntPtr.Size).Where(b => !((char)b).IsPrintable()).Any())
73+
if (data.Take(IntPtr.Size).IsPrintableData())
7474
{
7575
var text = new string(Encoding.UTF8.GetChars(data).TakeWhile(c => c != 0).ToArray());
7676
x = AddText(view, x, y, Program.Settings.TextColor, HotSpot.NoneId, $"'{text}'") + view.Font.Width;
7777
}
78-
else if(!data.EveryNth(2).Take(IntPtr.Size).Where(b => !((char)b).IsPrintable()).Any())
78+
else if(data.EveryNth(2).Take(IntPtr.Size).IsPrintableData())
7979
{
8080
var text = new string(Encoding.Unicode.GetChars(data).TakeWhile(c => c != 0).ToArray());
8181
x = AddText(view, x, y, Program.Settings.TextColor, HotSpot.NoneId, $"L'{text}'") + view.Font.Width;

Properties/Resources.Designer.cs

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)