Skip to content
This repository was archived by the owner on Feb 24, 2026. It is now read-only.

Commit 2e2ffa5

Browse files
committed
Fix nullable implementation
Fixes #13
1 parent 4d136d1 commit 2e2ffa5

8 files changed

Lines changed: 69 additions & 72 deletions

File tree

src/VmmSharpEx/Extensions/Input/VmmInputManager.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ public VmmInputManager(Vmm vmm)
6363
_vmm = vmm;
6464
if (!_vmm.PidGetFromName("winlogon.exe", out _winLogonPid))
6565
throw new VmmException("Failed to get winlogon.exe PID");
66-
var pids = _vmm.PidGetAllFromName("csrss.exe");
67-
if (pids is null || pids.Length == 0)
68-
throw new VmmException("Failed to get csrss.exe PIDs");
66+
var pids = _vmm.PidGetAllFromName("csrss.exe") ?? throw new InvalidOperationException("Failed to get csrss process");
6967
ulong gafAsyncKeyStateExport = 0;
7068

7169
var exceptions = new List<Exception>();

src/VmmSharpEx/Extensions/VmmExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public static bool FixCr3(this Vmm vmm, string processName, uint pid)
144144
// Wait for progress to reach 100%
145145
while (true)
146146
{
147-
var percentBytes = vmm.VfsRead(@"\misc\procinfo\progress_percent.txt", 4);
147+
_ = vmm.VfsRead(@"\misc\procinfo\progress_percent.txt", out var percentBytes, 4);
148148
if (percentBytes is not null &&
149149
int.TryParse(Encoding.ASCII.GetString(percentBytes).Trim(), out int percent) &&
150150
percent == 100)
@@ -154,8 +154,8 @@ public static bool FixCr3(this Vmm vmm, string processName, uint pid)
154154
}
155155

156156
// VFS list and read DTBs
157-
//var vfsEntries = vmm.VfsList(@"\misc\procinfo\");
158-
var dtbRaw = vmm.VfsRead(@"\misc\procinfo\dtb.txt", 0x1000);
157+
_ = vmm.VfsList(@"\misc\procinfo\");
158+
_ = vmm.VfsRead(@"\misc\procinfo\dtb.txt", out var dtbRaw, 0x1000);
159159
if (dtbRaw is null)
160160
return false;
161161

src/VmmSharpEx/Internal/Vmmi.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,7 @@ public static unsafe partial bool VMMDLL_Log(
14621462
[return: MarshalAs(UnmanagedType.Bool)]
14631463
public static partial bool VMMDLL_LogCallback(
14641464
IntPtr hVMM,
1465-
Vmm.VMMDLL_LOG_CALLBACK_PFN pfnCB);
1465+
Vmm.VMMDLL_LOG_CALLBACK_PFN? pfnCB);
14661466

14671467
[LibraryImport("vmm.dll", EntryPoint = "VMMDLL_MemCallback")]
14681468
[return: MarshalAs(UnmanagedType.Bool)]

src/VmmSharpEx/LeechCore.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,14 @@ namespace VmmSharpEx;
3535
/// </remarks>
3636
public sealed class LeechCore : IDisposable
3737
{
38-
private readonly Vmm _parent;
38+
private readonly Vmm? _parent;
3939
private IntPtr _handle;
4040

4141
private LeechCore() { throw new NotImplementedException(); }
4242

4343
private LeechCore(IntPtr hLC)
4444
{
4545
_handle = hLC;
46-
_parent = null!; // Required for nullable, but will be set in internal constructor
4746
}
4847

4948
/// <summary>
@@ -152,7 +151,7 @@ public override string ToString()
152151
configErrorInfo.fUserInputRequest = e.fUserInputRequest;
153152
if (e.cwszUserText > 0)
154153
{
155-
configErrorInfo.strUserText = Marshal.PtrToStringUni((IntPtr)(pLcErrorInfo.ToInt64() + cbERROR_INFO)) ?? "";
154+
configErrorInfo.strUserText = Marshal.PtrToStringUni((IntPtr)(pLcErrorInfo.ToInt64() + cbERROR_INFO));
156155
}
157156
}
158157

@@ -513,18 +512,15 @@ public unsafe bool ExecuteCommand(LcCmd fOption, ReadOnlySpan<byte> dataIn, out
513512
/// Wraps native memory returned from a scatter read invocation.
514513
/// </summary>
515514
/// <remarks>
516-
/// The underlying native scatter page buffers are released via <see cref="Lci.LcMemFree(void*)"/> when this handle
515+
/// The underlying native scatter page buffers are released via <see cref="Lci.LcMemFree"/> when this handle
517516
/// is disposed.
518517
/// </remarks>
519518
public sealed class LcScatterHandle : IDisposable
520519
{
521520
private readonly PooledDictionary<ulong, ScatterData> _results;
522521
private IntPtr _mems;
523522

524-
private LcScatterHandle()
525-
{
526-
_results = new PooledDictionary<ulong, ScatterData>();
527-
}
523+
private LcScatterHandle() { throw new NotImplementedException(); }
528524

529525
internal LcScatterHandle(PooledDictionary<ulong, ScatterData> results, IntPtr mems)
530526
{
@@ -652,7 +648,7 @@ public struct LcMemScatter
652648
/// A read-only view over the page contents pointed at by <see cref="pb"/>.
653649
/// </summary>
654650
/// <remarks>
655-
/// DANGER: Do not access this memory after the memory is freed via <see cref="Lci.LcMemFree(void*)"/>.
651+
/// DANGER: Do not access this memory after the memory is freed via <see cref="Lci.LcMemFree"/>.
656652
/// </remarks>
657653
public readonly unsafe ReadOnlySpan<byte> Data =>
658654
new ReadOnlySpan<byte>(pb.ToPointer(), checked((int)cb));

src/VmmSharpEx/Scatter/VmmScatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ public void Clear(VmmFlags? flags = null, uint? pid = null)
495495
if (pid is uint p)
496496
_pid = p;
497497
_isPrepared = default;
498-
Completed = null;
498+
Completed = default;
499499
if (!Vmmi.VMMDLL_Scatter_Clear(_handle, _pid, _flags))
500500
throw new VmmException("Failed to clear VmmScatter Handle.");
501501
}

src/VmmSharpEx/Vmm.cs

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ public bool ConfigSet(VmmOption fOption, ulong qwValue)
284284
/// <param name="outputFile">If non-<see langword="null"/>, writes the memory map to disk at the specified output location.</param>
285285
/// <returns>Memory map ptr in string format.</returns>
286286
/// <exception cref="VmmException">Thrown if the memory map cannot be retrieved or applied.</exception>
287-
public string? GetMemoryMap(
287+
public string GetMemoryMap(
288288
bool applyMap = false,
289289
string? outputFile = null)
290290
{
@@ -684,12 +684,12 @@ public unsafe bool MemWriteSpan<T>(uint pid, ulong va, Span<T> span)
684684
/// </summary>
685685
/// <param name="pid">Process ID (PID) this operation will take place within.</param>
686686
/// <param name="va">Virtual address to translate from.</param>
687-
/// <returns>Physical address if successful; otherwise 0 on failure.</returns>
687+
/// <param name="pa">Translated physical address if successful, otherwise 0.</param>
688+
/// <returns>True if successful, otherwise False.</returns>
688689
[MethodImpl(MethodImplOptions.AggressiveInlining)]
689-
public ulong MemVirt2Phys(uint pid, ulong va)
690+
public bool MemVirt2Phys(uint pid, ulong va, out ulong pa)
690691
{
691-
_ = Vmmi.VMMDLL_MemVirt2Phys(_handle, pid, va, out var pa);
692-
return pa;
692+
return Vmmi.VMMDLL_MemVirt2Phys(_handle, pid, va, out pa);
693693
}
694694

695695
/// <summary>
@@ -865,56 +865,51 @@ public bool VfsList(string path, VfsContext ctx, VfsCallBack_AddFile callbackFil
865865
/// VFS list files and directories in a virtual file system path.
866866
/// </summary>
867867
/// <param name="path">Virtual path to enumerate.</param>
868-
/// <returns>A list with file and directory entries on success; an empty list on failure.</returns>
869-
public List<VfsEntry> VfsList(string path)
868+
/// <returns>A list with file and directory entries on success; a null list on failure.</returns>
869+
public List<VfsEntry>? VfsList(string path)
870870
{
871871
using var ctx = new VfsContext();
872-
VfsList(path, ctx, VfsList_AddFileCB, VfsList_AddDirectoryCB);
872+
if (!VfsList(path, ctx, VfsList_AddFileCB, VfsList_AddDirectoryCB))
873+
return null;
873874
return ctx.Entries;
874875
}
875876

876877
/// <summary>
877878
/// VFS read data from a virtual file.
878879
/// </summary>
879880
/// <param name="fileName">The file name/path within the VFS.</param>
880-
/// <param name="ntStatus">Receives the NTSTATUS value of the operation (success = 0).</param>
881+
/// <param name="data">The data read from the operation on success; otherwise null.</param>
881882
/// <param name="size">The maximum number of bytes to read. 0 = default = 16MB.</param>
882883
/// <param name="offset">Optional offset within the file to start reading at.</param>
883-
/// <returns>The data read on success. Zero-length data on failure. NB! Data read may be shorter than <paramref name="size"/>.</returns>
884-
public unsafe byte[] VfsRead(string fileName, out uint ntStatus, uint size = 0, ulong offset = 0)
884+
/// <returns>The NTSTATUS value of the operation (success = 0).</returns>
885+
public unsafe uint VfsRead(string fileName, out byte[]? data, uint size = 0, ulong offset = 0)
885886
{
886887
uint cbRead = 0;
887888
if (size == 0)
888889
{
889890
size = 0x01000000; // 16MB
890891
}
891892

892-
var data = new byte[size];
893-
fixed (byte* pb = data)
893+
var dataLocal = new byte[size];
894+
fixed (byte* pb = dataLocal)
894895
{
895-
ntStatus = Vmmi.VMMDLL_VfsRead(_handle, fileName.Replace('/', '\\'), pb, size, out cbRead, offset);
896-
var pbData = new byte[cbRead];
897-
if (cbRead > 0)
896+
uint ret = Vmmi.VMMDLL_VfsRead(_handle, fileName.Replace('/', '\\'), pb, size, out cbRead, offset);
897+
if (ret == 0) // STATUS_SUCCESS
898+
{
899+
if (cbRead < size)
900+
{
901+
Array.Resize(ref dataLocal, (int)cbRead);
902+
}
903+
data = dataLocal;
904+
}
905+
else
898906
{
899-
data.AsSpan(0, pbData.Length).CopyTo(pbData);
907+
data = null;
900908
}
901-
902-
return pbData;
909+
return ret;
903910
}
904911
}
905912

906-
/// <summary>
907-
/// VFS read data from a virtual file.
908-
/// </summary>
909-
/// <param name="fileName">The file name/path within the VFS.</param>
910-
/// <param name="size">The maximum number of bytes to read. 0 = default = 16MB.</param>
911-
/// <param name="offset">Optional offset within the file to start reading at.</param>
912-
/// <returns>The data read on success. Zero-length data on failure. NB! Data read may be shorter than <paramref name="size"/>.</returns>
913-
public byte[] VfsRead(string fileName, uint size = 0, ulong offset = 0)
914-
{
915-
return VfsRead(fileName, out _, size, offset);
916-
}
917-
918913
/// <summary>
919914
/// VFS write data to a virtual file.
920915
/// </summary>
@@ -989,7 +984,7 @@ public bool PidGetFromName(string sProcName, out uint pdwPID)
989984
/// Get all process IDs (PIDs) for a given process name.
990985
/// </summary>
991986
/// <param name="sProcName">Name of the process to look up.</param>
992-
/// <returns>Array of PIDs that match; null array if no matches.</returns>
987+
/// <returns>Array of PIDs that match, or null if failed.</returns>
993988
public uint[]? PidGetAllFromName(string sProcName)
994989
{
995990
var pids = new List<uint>();
@@ -1005,7 +1000,8 @@ public bool PidGetFromName(string sProcName, out uint pdwPID)
10051000
pids.Add(procInfo[i].dwPID);
10061001
}
10071002
}
1008-
return pids.ToArray();
1003+
return pids.Count > 0 ?
1004+
pids.ToArray() : null;
10091005
}
10101006

10111007
/// <summary>
@@ -1729,7 +1725,7 @@ public unsafe bool Map_GetHeap(uint pid, out HeapMap result)
17291725
/// Get the user-mode path of the process image.
17301726
/// </summary>
17311727
/// <param name="pid">Process ID (PID) for this operation.</param>
1732-
/// <returns>A string path on success; otherwise null.</returns>
1728+
/// <returns>A string path on success; otherwise a null string.</returns>
17331729
public string? GetProcessPathUser(uint pid)
17341730
{
17351731
return GetProcessInformationString(pid, VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_USER_IMAGE);
@@ -1739,7 +1735,7 @@ public unsafe bool Map_GetHeap(uint pid, out HeapMap result)
17391735
/// Get the kernel-mode path of the process image.
17401736
/// </summary>
17411737
/// <param name="pid">Process ID (PID) for this operation.</param>
1742-
/// <returns>A string path on success; otherwise null.</returns>
1738+
/// <returns>A string path on success; otherwise a null string.</returns>
17431739
public string? GetProcessPathKernel(uint pid)
17441740
{
17451741
return GetProcessInformationString(pid, VMMDLL_PROCESS_INFORMATION_OPT_STRING_PATH_KERNEL);
@@ -1749,7 +1745,7 @@ public unsafe bool Map_GetHeap(uint pid, out HeapMap result)
17491745
/// Get the process command line.
17501746
/// </summary>
17511747
/// <param name="pid">Process ID (PID) for this operation.</param>
1752-
/// <returns>The command line string on success; otherwise null.</returns>
1748+
/// <returns>The command line string on success; otherwise a null string.</returns>
17531749
public string? GetProcessCmdline(uint pid)
17541750
{
17551751
return GetProcessInformationString(pid, VMMDLL_PROCESS_INFORMATION_OPT_STRING_CMDLINE);
@@ -1760,18 +1756,24 @@ public unsafe bool Map_GetHeap(uint pid, out HeapMap result)
17601756
/// </summary>
17611757
/// <param name="pid">Process ID (PID) for this operation.</param>
17621758
/// <param name="fOptionString">A VMMDLL_PROCESS_INFORMATION_OPT_* flag indicating which string to fetch.</param>
1763-
/// <returns>The string value on success; otherwise null.</returns>
1759+
/// <returns>The string value on success; otherwise a null string.</returns>
17641760
public unsafe string? GetProcessInformationString(uint pid, uint fOptionString)
17651761
{
17661762
var pb = Vmmi.VMMDLL_ProcessGetInformationString(_handle, pid, fOptionString);
1767-
if (pb == null)
1763+
try
17681764
{
1769-
return null;
1770-
}
1765+
if (pb is null)
1766+
{
1767+
return null;
1768+
}
17711769

1772-
var s = Marshal.PtrToStringAnsi((IntPtr)pb);
1773-
Vmmi.VMMDLL_MemFree(pb);
1774-
return s;
1770+
var s = Marshal.PtrToStringAnsi((IntPtr)pb);
1771+
return s;
1772+
}
1773+
finally
1774+
{
1775+
Vmmi.VMMDLL_MemFree(pb);
1776+
}
17751777
}
17761778

17771779
/// <summary>
@@ -2285,8 +2287,8 @@ public struct ThreadEntry
22852287
public ulong vaStackLimitUser;
22862288
public ulong vaStackBaseKernel;
22872289
public ulong vaStackLimitKernel;
2288-
public ulong vaImpersonationToken;
22892290
public ulong vaTrapFrame;
2291+
public ulong vaImpersonationToken;
22902292
public ulong vaRIP;
22912293
public ulong vaRSP;
22922294
public ulong qwAffinity;
@@ -2793,6 +2795,7 @@ public struct PfnEntry
27932795
public uint dwPfn;
27942796
public PfnType tp;
27952797
public PfnTypeExtended tpExtended;
2798+
public ulong va;
27962799
public ulong vaPte;
27972800
public ulong OriginalPte;
27982801
public uint dwPID;
@@ -3240,7 +3243,7 @@ public struct PfnEntry
32403243
uint cbPfns;
32413244
var cbMAP = Marshal.SizeOf<Vmmi.VMMDLL_MAP_PFN>();
32423245
var cbENTRY = Marshal.SizeOf<Vmmi.VMMDLL_MAP_PFNENTRY>();
3243-
if (pfns.Length == 0)
3246+
if (pfns.IsEmpty)
32443247
{
32453248
return null;
32463249
}
@@ -3283,7 +3286,7 @@ public struct PfnEntry
32833286
};
32843287
if (e.tp == PfnType.Active && !e.fPrototype)
32853288
{
3286-
e.vaPte = n.va;
3289+
e.va = n.va;
32873290
e.dwPID = n.dwPfnPte[0];
32883291
}
32893292

@@ -3304,11 +3307,11 @@ public struct PfnEntry
33043307
/// </summary>
33053308
/// <param name="pid">Process ID (PID) whose module the symbols belong to.</param>
33063309
/// <param name="vaModuleBase">Module base address.</param>
3307-
/// <param name="szModuleName">Receives the module name if successful.</param>
3310+
/// <param name="szModuleName">Receives the module name if successful, otherwise null.</param>
33083311
/// <returns><see langword="true"/> if the PDB was loaded successfully; otherwise <see langword="false"/>.</returns>
3309-
public unsafe bool PdbLoad(uint pid, ulong vaModuleBase, out string szModuleName)
3312+
public unsafe bool PdbLoad(uint pid, ulong vaModuleBase, out string? szModuleName)
33103313
{
3311-
szModuleName = "";
3314+
szModuleName = null;
33123315
var data = new byte[260];
33133316
fixed (byte* pb = data)
33143317
{
@@ -3330,12 +3333,12 @@ public unsafe bool PdbLoad(uint pid, ulong vaModuleBase, out string szModuleName
33303333
/// </summary>
33313334
/// <param name="szModule">Module name.</param>
33323335
/// <param name="cbSymbolAddressOrOffset">Symbol address or module-relative offset.</param>
3333-
/// <param name="szSymbolName">Receives the symbol name.</param>
3336+
/// <param name="szSymbolName">Receives the symbol name if succesful, otherwise null.</param>
33343337
/// <param name="pdwSymbolDisplacement">Receives the displacement from the symbol.</param>
33353338
/// <returns><see langword="true"/> on success; otherwise <see langword="false"/>.</returns>
3336-
public unsafe bool PdbSymbolName(string szModule, ulong cbSymbolAddressOrOffset, out string szSymbolName, out uint pdwSymbolDisplacement)
3339+
public unsafe bool PdbSymbolName(string szModule, ulong cbSymbolAddressOrOffset, out string? szSymbolName, out uint pdwSymbolDisplacement)
33373340
{
3338-
szSymbolName = "";
3341+
szSymbolName = null;
33393342
pdwSymbolDisplacement = 0;
33403343
var data = new byte[260];
33413344
fixed (byte* pb = data)
@@ -3452,7 +3455,7 @@ public delegate void VMMDLL_LOG_CALLBACK_PFN(
34523455
/// </summary>
34533456
/// <param name="pfnCB">The callback function to register, or <see langword="null"/> to unregister an existing callback.</param>
34543457
/// <returns><see langword="true"/> if the callback was successfully registered/unregistered, otherwise <see langword="false"/>.</returns>
3455-
public bool LogCallback(VMMDLL_LOG_CALLBACK_PFN pfnCB)
3458+
public bool LogCallback(VMMDLL_LOG_CALLBACK_PFN? pfnCB)
34563459
{
34573460
return Vmmi.VMMDLL_LogCallback(_handle, pfnCB);
34583461
}

src/VmmSharpEx_Benchmarks/ScatterBenchmarks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void SetupScatter()
1818
if (!_vmm.PidGetFromName("explorer.exe", out _pid))
1919
throw new InvalidOperationException("Failed to setup VMM");
2020
var vads = _vmm.Map_GetVad(_pid);
21-
if (vads is null)
21+
if (vads.Length == 0)
2222
throw new InvalidOperationException("Failed to setup VMM");
2323
var vas = new List<ulong>();
2424
foreach (var vad in vads)

src/VmmSharpEx_Tests/Manual/VmmSharpEx_VfsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void TestVfsList()
4040
public void TestVfsRead()
4141
{
4242
const string file = "LICENSE.txt";
43-
var data = _vmm.VfsRead($"/{file}");
43+
_ = _vmm.VfsRead($"/{file}", out var data);
4444
Assert.NotNull(data);
4545
Assert.NotEmpty(data);
4646
_output.WriteLine(Encoding.UTF8.GetString(data));

0 commit comments

Comments
 (0)