Используйте Struct as Buffer в C # [дубликат]

Если вы не инициализировали ссылочный тип и хотите установить или прочитать одно из его свойств, он будет генерировать исключение NullReferenceException.

Пример:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

Вы можно просто избежать этого, проверив, является ли переменная не нулевой:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

Чтобы полностью понять, почему выбрано исключение NullReferenceException, важно знать разницу между типами значений и ссылочные типы .

Итак, если вы имеете дело со типами значений, NullReferenceExceptions не может произойти. Хотя вам нужно поддерживать оповещение при работе со ссылочными типами!

Только ссылочные типы, как следует из названия, могут содержать ссылки или буквально буквально ничто (или «нуль»). Если типы значений всегда содержат значение.

Типы ссылок (эти должны быть проверены):

  • динамический
  • объект
  • string

Типы значений (вы можете просто игнорировать эти):

  • Числовые типы
  • Интегральные типы
  • Типы с плавающей запятой
  • decimal
  • bool
  • Пользовательские структуры

21
задан Roman Starkov 5 January 2012 в 20:45
поделиться

4 ответа

Ключ должен вызвать функцию NtQueryInformationThread . Это не полностью «официальная» функция (возможно, недокументированная в прошлом?), Но в документации нет альтернативы для получения начального адреса потока.

Я завернул его в .NET. -другой вызов, который принимает идентификатор потока и возвращает начальный адрес как IntPtr. Этот код был протестирован в режиме x86 и x64, а в последнем он был протестирован как на 32-битном, так и на 64-битном целевом процессе.

Одна вещь, которую я не тестировал, выполняла это с низким привилегии; Я бы ожидал, что этот код требует, чтобы вызывающий абонент имел SeDebugPrivilege.

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        PrintProcessThreads(Process.GetCurrentProcess().Id);
        PrintProcessThreads(4156); // some other random process on my system
        Console.WriteLine("Press Enter to exit.");
        Console.ReadLine();
    }

    static void PrintProcessThreads(int processId)
    {
        Console.WriteLine(string.Format("Process Id: {0:X4}", processId));
        var threads = Process.GetProcessById(processId).Threads.OfType<ProcessThread>();
        foreach (var pt in threads)
            Console.WriteLine("  Thread Id: {0:X4}, Start Address: {1:X16}",
                              pt.Id, (ulong) GetThreadStartAddress(pt.Id));
    }

    static IntPtr GetThreadStartAddress(int threadId)
    {
        var hThread = OpenThread(ThreadAccess.QueryInformation, false, threadId);
        if (hThread == IntPtr.Zero)
            throw new Win32Exception();
        var buf = Marshal.AllocHGlobal(IntPtr.Size);
        try
        {
            var result = NtQueryInformationThread(hThread,
                             ThreadInfoClass.ThreadQuerySetWin32StartAddress,
                             buf, IntPtr.Size, IntPtr.Zero);
            if (result != 0)
                throw new Win32Exception(string.Format("NtQueryInformationThread failed; NTSTATUS = {0:X8}", result));
            return Marshal.ReadIntPtr(buf);
        }
        finally
        {
            CloseHandle(hThread);
            Marshal.FreeHGlobal(buf);
        }
    }

    [DllImport("ntdll.dll", SetLastError = true)]
    static extern int NtQueryInformationThread(
        IntPtr threadHandle,
        ThreadInfoClass threadInformationClass,
        IntPtr threadInformation,
        int threadInformationLength,
        IntPtr returnLengthPtr);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, int dwThreadId);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr hObject);

    [Flags]
    public enum ThreadAccess : int
    {
        Terminate = 0x0001,
        SuspendResume = 0x0002,
        GetContext = 0x0008,
        SetContext = 0x0010,
        SetInformation = 0x0020,
        QueryInformation = 0x0040,
        SetThreadToken = 0x0080,
        Impersonate = 0x0100,
        DirectImpersonation = 0x0200
    }

    public enum ThreadInfoClass : int
    {
        ThreadQuerySetWin32StartAddress = 9
    }
}

Вывод в моей системе:

Process Id: 2168    (this is a 64-bit process)
  Thread Id: 1C80, Start Address: 0000000001090000
  Thread Id: 210C, Start Address: 000007FEEE8806D4
  Thread Id: 24BC, Start Address: 000007FEEE80A74C
  Thread Id: 12F4, Start Address: 0000000076D2AEC0
Process Id: 103C    (this is a 32-bit process)
  Thread Id: 2510, Start Address: 0000000000FEA253
  Thread Id: 0A0C, Start Address: 0000000076F341F3
  Thread Id: 2438, Start Address: 0000000076F36679
  Thread Id: 2514, Start Address: 0000000000F96CFD
  Thread Id: 2694, Start Address: 00000000025CCCE6

, кроме материала в круглых скобках, поскольку это требует


Что касается ошибки SymFromAddress «модуль не найден», я просто хотел упомянуть, что нужно называть SymInitialize с fInvadeProcess = true ИЛИ загружать модуль вручную, , как описано в MSDN .

Я знаю, что вы говорите, что это не так в вашей ситуации, но я оставлю это в интересах любого, кто находит этот вопрос через те ключевые слова.

15
ответ дан Roman Starkov 27 August 2018 в 22:41
поделиться

Вот что я понимаю о проблеме.

У вас есть приложение C #, APP1, которое создает цепочку потоков.

Эти потоки, в свою очередь, создают процесс , Я предполагаю, что эти потоки остаются в живых и отвечают за мониторинг процесса, который он породил.

Итак, для каждого потока в APP1 вы хотите, чтобы он перечислял информацию о потоках, порожденных в дочернем процессе этого потока.

Они могли бы сделать это в добрые старые дни:

  • Кодировать мой мониторинг потоков Win32 для данного процесса Win32 в DLL
  • Внедрить DLL в процесс, который я хотел контролировать
  • Использовать именованный канал или другой механизм RPC для связи из инъецированного процесса Win32 на хост APP1

Итак, в вашем основном threadproc в C # вы должны создать и контролировать именованный канал для вашего процесса для связи после его ввода.

В C ++-стране псевдокод должен был бы создать приостановить процесс, выделить некоторую память в этом процессе, вставить вашу DLL в процесс, а затем создать удаленный поток, который будет выполнять вашу вложенную dll:

char * dllName = "your cool dll with thread monitoring stuff.dll"

// Create a suspended process
CreateProces("your Win32 process.exe", ...CREATE_SUSPENDED..., pi)

// Allocate memory in the process to hold your DLL name to load
lpAlloc = VirtualAlloc(ph.hProcess, ... MEM_COMMIT, PAGE_READWRITE)

// Write the name of your dll to load in the process memory
WriteProcessMemeory(pi.hProcess, lpAlloc, dllName, ...)

// Get the address of LoadLibrary
fnLoadLibrary = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA")

// Create a remote thread in the process, giving it the threadproc for LoadLibrary
// and the argument of your DLL name
hTrhead = CreateRemoteThread(pi.hProcess, ..., fnLoadLibrary, lpAlloc, ...)

// Wait for your dll to load
WaitForSingleObject(hThread)

// Go ahead and start the Win32 process
ResumeThread(ph.hThread)

. В вашей DLL вы можете поместить код в DLL_PR OCESS_ATTACH, который будет подключаться к именованному каналу, который вы настроили, и инициализировать все свои вещи. Затем запустите функцию, чтобы начать мониторинг и отчет по именованному каналу.

Ваш C # threadproc будет контролировать именованный канал для своего процесса и сообщать об этом до APP1.

UPDATE:

Я пропустил тот факт, что вы управляете кодом для Win32-процесса. В этом случае я просто передал бы аргумент процессу, который бы контролировал механизм RPC по вашему выбору для связи (общая память, именованные каналы, служба очереди, буфер обмена (ha) и т. Д.).

путь, ваш C # threadproc настраивает канал связи RPC и мониторинг, а затем предоставляет информацию о «адресе» вашему процессу Win32, чтобы он мог «набрать вас».

Я оставлю другой материал там, в случае, если это полезно для всех, кто хочет контролировать процесс Win32, где они не отвечают за код.

2
ответ дан GalacticJello 27 August 2018 в 22:41
поделиться

Ну, это определенно не простой подход, но, возможно, это поможет вам как-то. Вы должны иметь возможность получить трассировку стека другого потока способом, используемым этим проектом (StackWalk64), и в конечном итоге увидеть имя желаемой функции. У этого есть свои проблемы, в особенности производительность этого подхода, вероятно, не будет слишком высокой, но, как я понял, это одноразовая работа в потоке. Вопрос в том, сможет ли он в целом правильно пройти стек ваших (возможно оптимизированных) приложений.

0
ответ дан konrad.kruczynski 27 August 2018 в 22:41
поделиться

Во-первых, вы не можете действительно сделать это надежно: если вам придётся получить доступ к Thread.StartAddress до того, как поток выполнит указатель на функцию или после возвращения функции, вы не сможете узнать, что означает начальная функция.

Во-вторых, более вероятным ответом является то, что при запуске функции запуска потока нет прямого сопоставления с функцией запуска.

0
ответ дан MSN 27 August 2018 в 22:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: