У форм Windows есть меньше разрешений, чем у консольных приложений? - PullRequest
0 голосов
/ 20 октября 2018

Я создал библиотеку для внедрения файлов DLL в процессы, используя различные методы.Я тестирую это с графическим интерфейсом, используя Windows Forms.

Все методы работают должным образом, кроме случаев использования QueueUserAPC.Когда я пытаюсь использовать этот метод, процесс, в который я внедряю DLL-библиотеку, дает сбой.

Я создал базовое консольное приложение для тестирования этого метода вне формы Windows, и оно работало, как предполагалось, без сбоя процесса.Кроме того, моя проверка ошибок говорит мне, что DLL вводится без каких-либо ошибок при использовании метода QueueUserAPC из формы Windows, однако процесс все равно падает.

У меня такое ощущение, что причина сбоя процесса прииспользование формы Windows не связано с кодом метода QueueUserAPC, а с разрешениями формы Windows.Однако я могу ошибаться, поэтому приведу код для метода ниже.

pinvoke

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessPrivileges dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, MemoryAllocation flAllocationType, MemoryProtection flProtect);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, int lpNumberOfBytesWritten);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool QueueUserAPC(IntPtr pfnAPC, IntPtr hThread, IntPtr dwData);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern void CloseHandle(IntPtr handle);

[DllImport("kernel32.dll", SetLastError=true)]
public static extern void VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, MemoryAllocation dwFreeType);

public enum MemoryAllocation
{
    Commit = 0x1000,
    Reserve = 0x2000,
    Release = 0x8000,
    AllAccess = Commit | Reserve
}

public enum MemoryProtection
{
    PageReadWrite = 0x04,
    PageExecuteReadWrite = 0x40
}

public enum ThreadAccess
{
    SuspendResume = 0x02,
    GetContext = 0x08,
    SetContext = 0x010,
    AllAccess = SuspendResume | GetContext | SetContext
}

QueueUserAPC Method

public static class MQueueUserAPC
{
    public static bool Inject(string dllPath, string processName)
    {
        // Get the pointer to load library

        var loadLibraryPointer = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

        if (loadLibraryPointer == IntPtr.Zero)
        {
            return false;
        }

        // Get the handle of the specified process

        var processId = Process.GetProcessesByName(processName)[0].Id;

        var processHandle = OpenProcess(ProcessPrivileges.AllAccess, false, processId);

        if (processHandle == IntPtr.Zero)
        {
            return false;
        }

        // Allocate memory for the dll name

        var dllNameSize = dllPath.Length + 1;

        var dllMemoryPointer = VirtualAllocEx(processHandle, IntPtr.Zero, (uint) dllNameSize, MemoryAllocation.AllAccess, MemoryProtection.PageReadWrite);

        if (dllMemoryPointer == IntPtr.Zero)
        {
            return false;
        }

        // Write the dll name into memory

        var dllBytes = Encoding.Default.GetBytes(dllPath);

        if (!WriteProcessMemory(processHandle, dllMemoryPointer, dllBytes, (uint) dllNameSize, 0))
        {
            return false;
        }

        // Call QueueUserAPC on each thread

        foreach (var thread in Process.GetProcessesByName(processName)[0].Threads.Cast<ProcessThread>())
        {
            var threadId = thread.Id;

            // Get the threads handle

            var threadHandle = OpenThread(ThreadAccess.SetContext, false, (uint) threadId);

            // Add a user-mode APC to the APC queue of the thread

            QueueUserAPC(loadLibraryPointer, threadHandle, dllMemoryPointer);

            // Close the handle to the thread

            CloseHandle(threadHandle);
        }

        // Close the previously opened handle

        CloseHandle(processHandle);

        // Free the previously allocated memory

        VirtualFreeEx(processHandle, dllMemoryPointer, dllNameSize, MemoryAllocation.Release);

        return true;
    }  
}

Как я использую его в моем приложении Windows Form / Console

var injector = new Injector();

if(Injector.QueueUserAPC(dllPath, processName))
{
    MessageBox.Show("No error was raised");
}

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

Если вы хотите проверить библиотеку, она у меня установлена ​​на Github с инструкциями по его использованию.

1 Ответ

0 голосов
/ 20 октября 2018

процесс падает, потому что вы вызываете VirtualFreeEx для памяти, где вы храните имя dll (dllMemoryPointer).когда LoadLibrary начнет использовать эту память, она может быть уже недействительной. APC - это асинхронный вызов процедуры, поэтому вы не можете знать, в точке, где вы вызываете VirtualFreeEx, LoadLibrary уже выполнены или находятся в процессе, или даже не начинаются.

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

также необходимо понимать, что не все обрабатываемые потоки будут в состоянии оповещения после того, как вы введете в него APC .это возможно, несмотря на то, что APC будет успешно поставлен в очередь - он никогда не будет вызван.внедрить его во все обрабатываемые потоки, надеясь, что хотя бы один из них будет в состоянии оповещения неправильно.во-первых, может не быть, во-вторых - некоторые рабочие потоки могут вообще не быть предназначены для вызова LoadLibrary - скажем, поток может не иметь контекста активации, не подключаться к csrss, может быть другим.все это также может привести к сбою или неопределенным эффектам.и, наконец, это просто не эффективно.

внедрение через QueueUserAPC полезно, если вы создаете сам процесс (в приостановленном состоянии) и вставляете apc в начальный поток процесса, прежде чем возобновить его.это будет работать (как минимум, пока), потому что новый поток в процессе начинает выполняться в пользовательском режиме всегда с LdrInitializeThunk (где он инициализирует процесс и / или вызывает код загрузки dll) и до перехода к реальной точке входа всегда (во всех существующих окнах).версии) звоните ZwTestAlert.именно в этот момент ваш APC вызов будет выполнен.но, строго говоря, это также не на 100% правильный путь, потому что мы используем LoadLibrary[W/A] в качестве APC точки входа.но что будет, если APC будет превышен слишком рано, когда kernel32.dll еще не сопоставлен с процессом или еще не инициализирован?очевидно, что сбой.в чистых окнах не должно быть этой ситуации (apc будет вызываться после полной инициализации процесса, все статические dll загружены, непосредственно перед вызовом exe точки входа).но возможно, что некоторые драйверы введут собственный код для обработки через вызов APC (скажем, при сопоставлении kernel32.dll событие) и принудительно выполнят APC , выполняющийся в этот момент.в результате только 100% надежный способ здесь использует ZwQueueApcThread для шелл-кода, который будет вызывать только ntdll api, загружать dll через LdrLoadDll и сам dll имеет статический импорт только из ntdll.dll

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...