APC,异步过程调用。
APC是线程相关的,每个线程都有自己独自的APC链表,因此可以让一段代码在指定的线程上下文中运行。系统中有两种APC,用户层和内核层APC.
应用:
APC被知道的最多的应用应该是对一些异步操作的支持,如ReadFile的异步方式等。以ReadFile为例,文件系统发现这是一个异步调用,就会标记为Pending,然后立刻返回。等读取操作完成的时候,在将结果拷贝到用户层的内存空间和调用用户层提供的完成函数时,就用到APC。
另外,挂钩APC相关函数可以达到阻止结束目标进程(ZwTerminateProcess)、阻止创建远程线程(CreateRemoteThread)、阻止对其调用GetContext SetContext等。(但直接读写进程空间或者Attach操作是阻止不了的)。
总结:
如果说其它进程或者线程间通信的方式(比如共享一个对象、一块映射内存、甚至套接字等等)是两个国家间的以合作的方式交流:我需要你的什么信息或者要送给你什么,要得到你的同意才行,那么APC就像是间谍:我安插在你选干部的队伍里(插入APC),等你请我去当你的干部。哪一天我当上了(交付),我就可以做我自己想做的事了,我同国外的沟通不用得到同意(目标进程里的代码可能完全不知道我的存在)。
下面的是在用户层插入APC的代码
DWORD GetNtDllString(HANDLE dllbase)
{
char* lpbuf = (char*)dllbase;
while(stricmp(++lpbuf, ".dll") && lpbuf - (char*)dllbase < 65535);
printf("%s", lpbuf - 5);
return lpbuf - (char*)dllbase;
}
int main(int argc, char* argv[])
{
HINSTANCE ntdll_dll = GetModuleHandle("ntdll.dll");
NTQUERYSYSTEMINFORMATION ZwQuerySystemInformation;
PSYSTEM_PROCESSES pSp=NULL;
ULONG retureSize = 0;
DWORD status;
unsigned char *buf;
char * dll_name = (char*)ntdll_dll + GetNtDllString(ntdll_dll) - 3;
printf("CurrentThreadId %d\ninject dll name:%s\n",
GetCurrentThreadId(), dll_name);
ZwQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)GetProcAddress(ntdll_dll, "ZwQuerySystemInformation");
status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, NULL, 0, &retureSize);
if (status != STATUS_INFO_LENGTH_MISMATCH)
return -1;
buf = malloc(retureSize);
if (buf ==NULL)
return -1;
status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, (PVOID)buf, retureSize, NULL);
if (status != STATUS_SUCCESS)
return -1;
pSp = (PSYSTEM_PROCESSES)buf;
do {
pSp = (PSYSTEM_PROCESSES)( (unsigned long)pSp + pSp->NextEntryDelta );
if (pSp->ProcessName.Buffer
&& _wcsicmp(pSp->ProcessName.Buffer, L"lsass.exe") == 0){
ULONG i;
for (i =0; i<pSp->ThreadCount; i++) {
HANDLE pthread;
pthread = OpenThread(THREAD_SET_CONTEXT, FALSE, (DWORD)pSp->Threads[i].ClientId.UniqueThread);
printf("进程 %d 线程:%d\r\n",
pSp->Threads[i].ClientId.UniqueProcess,
pSp->Threads[i].ClientId.UniqueThread);
if (!QueueUserAPC((PAPCFUNC)LoadLibraryA, pthread, (ULONG_PTR)dll_name))
printf("QueueUserAPC error\n");
CloseHandle(pthread);
}
break;
}
} while ( pSp->NextEntryDelta != 0 );
free(buf);
while (!kbhit())
SleepEx(100, TRUE);
return 0;
}