Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous Procedure Calls

I'm trying to get APC to work in my C++ code but I fail. I'm lost for words. In C# it works all fine (logically the same code). I want thread 2 to inject a call into thread 1. But in my C++ project it wont execute for some reason. What am I doing wrong ?

  • thread 1 (main thread)
  • thread 2 (sub thread, that needs the main thread to execute a function)


Code:
#include "stdio.h"
#include "windows.h"

#define TIME 2500
#define LAST_ERROR printf("last error: %i\r\n", GetLastError());

HANDLE handle1, handle2;

void ThreadInfo(char* prefix = "")
{
    printf("%sthread id: %i\r\n", prefix, GetCurrentThreadId());
}

VOID CALLBACK apc( _In_ ULONG_PTR data)
{
    ThreadInfo(" -> apc: 2 -> 1: ");
}

void run1()
{
    while (true)
    {
        Sleep(TIME);
        ThreadInfo("1: ");

        // apc
        //QueueUserAPC(apc, handle2, (ULONG_PTR) NULL);
    }
}

void run2()
{
    while (true)
    {
        Sleep(TIME);
        ThreadInfo("2: ");

        // apc
        QueueUserAPC(apc, handle1, (ULONG_PTR) NULL);
    }
}

void TestThreads()
{
    DWORD threadId;
    SECURITY_ATTRIBUTES a;
    a.nLength = 12;
    a.lpSecurityDescriptor = NULL;
    a.bInheritHandle = 1;

    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &handle1, 0, true, 2);
    LAST_ERROR

    handle2 = CreateThread(NULL, 100000, (LPTHREAD_START_ROUTINE)run2, NULL, 0, &threadId);
    printf("handles (1, 2): %i, %i\r\n", handle1, handle2);
    printf("ids (1, 2): %i, %i\r\n", threadId, GetCurrentThreadId());
    printf("--------------------------------\r\n");
    run1();
}

int main()
{
    TestThreads();

    printf("done.");
    getchar();
    return 0;
}
like image 633
Bitterblue Avatar asked May 19 '14 12:05

Bitterblue


People also ask

What is asynchronous procedure call APC injection?

Adversaries may inject malicious code into processes via the asynchronous procedure call (APC) queue in order to evade process-based defenses as well as possibly elevate privileges. APC injection is a method of executing arbitrary code in the address space of a separate live process.

What is an APC driver?

Drivers can be charged with Actual Physical Control, or APC for short, for simply being around their vehicle while intoxicated.

What is Queueuserapc?

The queuing of an APC is a request for the thread to call the APC function. The operating system issues a software interrupt to direct the thread to call the APC function. When a user-mode APC is queued, the thread is not directed to call the APC function unless it is in an alertable state.


1 Answers

  Sleep(TIME);

That's your problem statement. APCs are pretty dangerous, they permit code re-entrancy. The rough equivalent to the infamous Application.DoEvents() statement that got so many VB programmers in trouble. Windows doesn't just let them run, you have to be explicit that your code is re-entrant so that the APC can safely run without screwing up your program state.

The specific requirement is that your thread is in an "alertable wait state". Getting into a wait state is not the issue, the Sleep() call does that. It is however not an alertable state. You have to use this instead:

  SleepEx(TIME, TRUE);

Modify the run1() function in your test program and you'll now see the APC callback getting called. Compare to GetOverlappedResultEx(), SignalObjectAndWait(), WaitForSingleObjectEx() and WaitForMultipleObjectsEx(), other winapi calls that can put a thread in an alertable wait state. And yes, Thread.Sleep() in a managed program is alertable, the CLR calls SleepEx() under the hood.

like image 170
Hans Passant Avatar answered Nov 03 '22 01:11

Hans Passant