Anyhow, today I decided to try my C# to do things that are somewhat low-level and I know that C# is not designed for low-level tasks but wanted to try how it is like with C#. So I decided to get to Process Environment Block(PEB) and find the command-line arguments of a given process. In this particular example, I just used my own process but it is relatively easy to change it to point to other process by providing other process handle.
With that, let me show my code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace PEB
{
class Program
{
enum PROCESSINFOCLASS : int
{
ProcessBasicInformation = 0,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters // omitted the rest
};
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public int ExitStatus;
public int PebBaseAddress;
public int AffinityMask;
public int BasePriority;
public int UniqueProcessId;
public int InheritedFromUniqueProcessId;
public uint Size
{
get { return (6 * sizeof(int)); }
}
};
// following two definitions are from Windows via C C++
// check 'allow unsafe code' in properties page
[StructLayout(LayoutKind.Sequential)]
unsafe struct __PEB
{
public fixed uint Filler[4];
public uint InfoBlockAddress;
};
[StructLayout(LayoutKind.Sequential)]
unsafe struct __INFOBLOCK
{
public fixed uint Filler[17];
// Windows keeps this as Unicdoe(wide character)
// Without the following option, it only gets to the first character
[MarshalAs(UnmanagedType.LPWStr)]
public string cmdAddress;
}
// Finding NtQueryInformationProcess API from ntdll is relatively easy
[DllImport("ntdll.dll", SetLastError = true)]
static extern int NtQueryInformationProcess(
IntPtr processHandle,
PROCESSINFOCLASS processInformationClass,
ref PROCESS_BASIC_INFORMATION processInformation,
uint processInformationLength,
ref int returnLength);
static void Main(string[] args)
{
int status;
int returnLength = 0;
Process currentProcess = Process.GetCurrentProcess();
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
status = NtQueryInformationProcess(
currentProcess.Handle,
PROCESSINFOCLASS.ProcessBasicInformation,
ref pbi,
pbi.Size,
ref returnLength);
if (status == 0)
{
Console.WriteLine("PEB address: {0}", pbi.PebBaseAddress);
// It is little tricky to do casting in C# - two steps
// First, it is necessary to get the pointer
// Second, cast that to what we need using Marshal.PtrToStructure
IntPtr pebAddress = (IntPtr)pbi.PebBaseAddress;
__PEB peb = (__PEB)Marshal.PtrToStructure(pebAddress, typeof(__PEB));
IntPtr infoblkAddr= (IntPtr)peb.InfoBlockAddress;
__INFOBLOCK infoBlock =
(__INFOBLOCK)Marshal.PtrToStructure(infoblkAddr, typeof(__INFOBLOCK));
Console.WriteLine("command line: " + infoBlock.cmdAddress);
}
}
}
}
I put comments on my code to explain what I am trying to do but basically at the end of the day, the lesson I learned from my exercise is that I should choose the right language for the job. I felt that C# is probably not the best language to work on low-level ntdll stuff. I think that for this kind of job C/C++ is better language. Perhaps it is just that I am still in the process so I am just not good.
Anyway, I am enjoying learning C# so hopefully next time, I will write something more meaningful and related to C#.
Please read Microsoft C# naming conventions. Your C experience can be seen from the style a bit too much ;D
ReplyDeleteThank you very much, you did a great work! I'v searched for exactly this :)
ReplyDelete