APC结构
nt!_KAPC
+0x000 Type : Int2B apc类型为0x12
+0x002 Size : Int2B 结构体大小0x30
+0x004 Spare0 : Uint4B 未使用
+0x008 Thread : Ptr32 _KTHREAD 目标线程
+0x00c ApcListEntry : _LIST_ENTRY APC队列挂的是这个位置
+0x014 KernelRoutine : Ptr32 void 指向函数IopDeallocateApc (调用ExFreePoolWithTag释放APC)
+0x018 RundownRoutine : Ptr32 void
+0x01c NormalRoutine : Ptr32 void 用户APC总入口Ke UserApcDispatcher 或者真正内核apc函数
+0x020 NormalContext : Ptr32 Void 内核APC :NULL 用户APC :真正的apc函数
+0x024 SystemArgument1 : Ptr32 Void dwData
+0x028 SystemArgument2 : Ptr32 Void
+0x02c ApcStateIndex : Char 挂哪一个队列,0 1 2 3
+0x02d ApcMode : Char 内核APC 0 用户APC 1
+0x02e Inserted : UChar 表示本APC是否已挂入队列 挂入前 0 挂入后 1
TargetEnvironment=0: APC会挂到其亲生父亲的进程中去。
TargetEnvironment=1: APC会挂到其养父的进程中去。也就是挂靠的进程
TargetEnvironment=2: 当前环境,如果线程没有挂靠,就挂到亲生父亲进程。 如果线程挂靠了,就把APC挂到挂靠的进程。 在初始化APC函数里 判断了TargetEnvironment2 然后修正ApcStateIndex
TargetEnvironment=3: 插入APC时的当前环境。 意思是,APC初始化的时候线程可能是挂靠状态,而在插入APC的时候 线程可能不再处于挂靠状态, 那么取的值是非挂靠状态。在插入APC函数里 判断了TargetEnvironment3 然后修正ApcStateIndex
自己用的时候,2和3用不到。
**总结:如果要往亲生父亲进程发送APC TargetEnvironment =0 要发给挂靠的环境TargetEnvironment =1 **
自己实现APC插入
//参数1: 要执行的APC函数地址
//参数2:传给APC函数的参数
// 返回值是KAPC指针
PVOID InitApc(ULONG handle,PVOID NormalContext,ULONG Data)
{
//申请0x30字节的内存
ULONG Thread=0;
PKAPC pAPC=ExAllocatePoolWithTag(NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,0x30,0);
if (!pAPC)
{
DbgPrint("初始化APC申请内存失败\n");
return NULL;
}
//初始化APC结构体
RtlFillMemory(pAPC,0x30,0);
Thread=FindKernelObjectByHandle(handle);
//为APC结构体赋值。
pAPC->Type=0x12;
pAPC->Size=0x30;
pAPC->Thread=(PKTHREAD)Thread;
pAPC->KernelRoutine=(PVOID)0x80573f30 ; // 这个值来自于0x80573f30 是函数IopDeallocateApc的地址
pAPC->RundownRoutine=0; //0
pAPC->ApcStateIndex=0;
pAPC->NormalRoutine=(PVOID)0x7c82c0de; //这个值来自于QueueUserApc 反汇编代码中 调用ZwQueueThreadApc push的NormalRoutine的值
pAPC->NormalContext=NormalContext;
pAPC->SystemArgument1=(PVOID)Data;
pAPC->ApcMode=1;
pAPC->Inserted=0;
DbgPrint("---ETHREAD=---%x\n",Thread);
DbgPrint("---apc=---%x\n",(ULONG)pAPC);
return pAPC;
}
//需要传入KAPC结构体
VOID InsertApc(PKAPC pAPC)
{
PULONG pThread=NULL;
PULONG AttachAddr=NULL;
PLIST_ENTRY pUserApcLIst=NULL;
PLIST_ENTRY pApcListEntry=NULL;
PLIST_ENTRY Flink;
*(PULONG)0x41701c=0x11136;
if (pAPC->Inserted==1)
{
return;
}
pThread=(PULONG)(pAPC->Thread);
pUserApcLIst=(PLIST_ENTRY)(*(pThread+0x138/4)+8);
pApcListEntry=(PLIST_ENTRY)((PULONG)pAPC+3);
pApcListEntry->Flink = pUserApcLIst->Flink;
pApcListEntry->Blink = pUserApcLIst;
//插入头节点和第二个节点之间 当链表为空时,插入第一个元素 链表的Blink和Flink为同一个值,就是这个元素的地址
//而这个元素内ApcListEntry的Flink和Blink都是这个链表的地址(线程中的APC队列地址)
pUserApcLIst->Flink->Blink = pApcListEntry;
pUserApcLIst->Flink = pApcListEntry;
//插入APC完成
pAPC->Inserted=1;
//修改ETHREAD 的UserPending为1
*((PUCHAR)pThread+0x4a)=1;
}