伪造句柄方法结束进程(无需驱动)

2010-6-13
系统为WinXp
XP_5.1.2600.2180
EULAID: XPSP2_RM.0_PRO_RTL_CN


TerminateProcess函数分析:
TerminateProcess
NtTerminateProcess
PspTerminateThreadByPointer(ObReferenceObjectByHandle, PsGetNextProcessThread(loop))
->Self: PspExitThread
->Other: KeInsertQueueApc, KiInsertQueueApc 

/*
 * @implemented
 */
NTSTATUS
NTAPI
NtTerminateProcess(IN HANDLE ProcessHandle OPTIONAL,
                   IN NTSTATUS ExitStatus)
{
    NTSTATUS Status;
    PEPROCESS Process, CurrentProcess = PsGetCurrentProcess();
    PETHREAD Thread, CurrentThread = PsGetCurrentThread();
    BOOLEAN KillByHandle;
    PAGED_CODE();
    PSTRACE(PS_KILL_DEBUG,
            "ProcessHandle: %p ExitStatus: %p\n", ProcessHandle, ExitStatus);

    /* Were we passed a process handle? */
    if (ProcessHandle)
    {
        /* Yes we were, use it */
        KillByHandle = TRUE;
    }
    else
    {
        /* We weren't... we assume this is suicide */
        KillByHandle = FALSE;
        ProcessHandle = NtCurrentProcess();
    }

    /* Get the Process Object */
    Status = ObReferenceObjectByHandle(ProcessHandle,
                                       PROCESS_TERMINATE,
                                       PsProcessType,
                                       KeGetPreviousMode(),
                                       (PVOID*)&Process,
                                       NULL);
    if (!NT_SUCCESS(Status)) return(Status);

    /* Check if this is a Critical Process, and Bugcheck */
    if (Process->BreakOnTermination)
    {
        /* Break to debugger */
        PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n",
                              Process,
                              Process->ImageFileName);
    }

    /* Lock the Process */
    if (!ExAcquireRundownProtection(&Process->RundownProtect))
    {
        /* Failed to lock, fal */
        ObDereferenceObject (Process);
        return STATUS_PROCESS_IS_TERMINATING;
    }

    /* Set the delete flag, unless the process is comitting suicide */
    if (KillByHandle) PspSetProcessFlag(Process, PSF_PROCESS_DELETE_BIT);

    /* Get the first thread */
    Status = STATUS_NOTHING_TO_TERMINATE;
    Thread = PsGetNextProcessThread(Process, NULL);
    if (Thread)
    {
        /* We know we have at least a thread */
        Status = STATUS_SUCCESS;

        /* Loop and kill the others */
        do
        {
            /* Ensure it's not ours*/
            if (Thread != CurrentThread)
            {
                /* Kill it */
                PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
            }

            /* Move to the next thread */
            Thread = PsGetNextProcessThread(Process, Thread);
        } while (Thread);
    }

    /* Unlock the process */
    ExReleaseRundownProtection(&Process->RundownProtect);

    /* Check if we are killing ourselves */
    if (Process == CurrentProcess)
    {
        /* Also make sure the caller gave us our handle */
        if (KillByHandle)
        {
            /* Dereference the project */
            ObDereferenceObject(Process);

            /* Terminate ourselves */
            PspTerminateThreadByPointer(CurrentThread, ExitStatus, TRUE);
        }
    }
    else if (ExitStatus == DBG_TERMINATE_PROCESS)
    {
        /* Disable debugging on this process */
        DbgkClearProcessDebugObject(Process, NULL);
    }

    /* Check if there was nothing to terminate, or if we have a Debug Port */
    if ((Status == STATUS_NOTHING_TO_TERMINATE) ||
        ((Process->DebugPort) && (KillByHandle)))
    {
        /* Clear the handle table */
        ObClearProcessHandleTable(Process);

        /* Return status now */
        Status = STATUS_SUCCESS;
    }

    /* Decrease the reference count we added */
    ObDereferenceObject(Process);

    /* Return status */
    return Status;
}

/*  H:\ntoskrnl\ps\kill.c
 * See "Windows Internals" - Chapter 13, Page 49
 */
NTSTATUS
NTAPI
PspTerminateThreadByPointer(IN PETHREAD Thread,
                            IN NTSTATUS ExitStatus,
                            IN BOOLEAN bSelf)
{
    PKAPC Apc;
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG Flags;
    PAGED_CODE();
    PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %p\n", Thread, ExitStatus);
    PSREFTRACE(Thread);

    /* Check if this is a Critical Thread, and Bugcheck */
    if (Thread->BreakOnTermination)
    {
        /* Break to debugger */
        PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n",
                              Thread,
                              Thread->ThreadsProcess->ImageFileName);
    }

    /* Check if we are already inside the thread */
    if ((bSelf) || (PsGetCurrentThread() == Thread))
    {
        /* This should only happen at passive */
        ASSERT_IRQL_EQUAL(PASSIVE_LEVEL);

        /* Mark it as terminated */
        PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT);

        /* Directly terminate the thread */
        PspExitThread(ExitStatus);
    }

    /* This shouldn't be a system thread */
    if (Thread->SystemThread) return STATUS_ACCESS_DENIED;

    /* Allocate the APC */
    Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);

    /* Set the Terminated Flag */
    Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT;

    /* Set it, and check if it was already set while we were running */
    if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) &
          CT_TERMINATED_BIT))
    {
        /* Initialize a Kernel Mode APC to Kill the Thread */
        KeInitializeApc(Apc,
                        &Thread->Tcb,
                        OriginalApcEnvironment,
                        PsExitSpecialApc,
                        PspExitApcRundown,
                        PspExitNormalApc,
                        KernelMode,
                        (PVOID)ExitStatus);

        /* Insert it into the APC Queue */
        if (!KeInsertQueueApc(Apc, Apc, NULL, 2))
        {
            /* The APC was already in the queue, fail */
            ExFreePool(Apc);
            Status = STATUS_UNSUCCESSFUL;
        }
        else
        {
            /* Forcefully resume the thread and return */
            KeForceResumeThread(&Thread->Tcb);
            return Status;
        }
    }

    /* We failed, free the APC */
    ExFreePool(Apc);

    /* Return Status */
    return Status;
}



伪造句柄结束进程, 创建一个进程句柄, 修改EPROCESS_XP 的 ThreadListHead 指向目标的进程的线程, 然后 调用TerminateProcess
CreateProcessA
pKrnProc = (NNtKrn::EPROCESS_XP *)ShellProc.dwObjAddr;
dwWriteAddr = (DWORD)(__int64)&pKrnProc->ThreadListHead;
dwSize = sizeof(NNtKrn::LIST_ENTRY);
Nntdll::WriteKernelMem(dwWriteAddr, dwSize, &DestThreadList);  //写入首线程

结果: 篮屏
分析:
经WinDbg调试发现死循环 循环代码
    Thread = PsGetNextProcessThread(Process, NULL);
    if (Thread)
    {
        /* We know we have at least a thread */
        Status = STATUS_SUCCESS;

        /* Loop and kill the others */
        do
        {
            /* Ensure it's not ours*/
            if (Thread != CurrentThread)
            {
                /* Kill it */
                PspTerminateThreadByPointer(Thread, ExitStatus, FALSE);
            }

            /* Move to the next thread */
            Thread = PsGetNextProcessThread(Process, Thread);
        } while (Thread);
    }
->PsGetNextProcessThread
   if (Thread)
    {
        /* Start where we left off */
        Entry = Thread->ThreadListEntry.Flink;
    }
    else
    {
        /* Start at the beginning */
        Entry = Process->ThreadListHead.Flink;
    }

    /* Set the list head and start looping */
    ListHead = &Process->ThreadListHead;
    while (ListHead != Entry)
    {
        /* Get the Thread */
        FoundThread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);

        /* Safe reference the thread */
        if (ObReferenceObjectSafe(FoundThread)) break;

        /* Nothing found, keep looping */
        FoundThread = NULL;
        Entry = Entry->Flink;
    }

原因:
线程链没有闭合, 导致循环 PspTerminateThreadByPointer -> KeInsertQueueApc
KeInsertQueueApc 过多导致资源不够, 出现访问异常

结论: 
修改EPROCESS_XP 的方法会导致死循环


再次尝试: 修改线程链
NNtKrn::GetLastThreadAddr
MokeList.dwObjAddr = (DWORD)(__int64)&pKrnDestLastThread->ThreadListEntry;
NNtKrn::WriteKrnObject(MokeList);    //写入结束线程

结果: 进程CPU 50% 然后无法响应
分析: 
WinDbg 调试发现 线程 PspExitThread 循环没退出所以(CPU 50%, 无法响应)

  CurrentProcess = Thread->ThreadsProcess;       (ShellProc)
  ...
    FirstEntry = &CurrentProcess->ThreadListHead;  
    CurrentEntry = FirstEntry->Flink;
        while (FirstEntry != CurrentEntry)
        {
            /* Get the thread on the list */
            OtherThread = CONTAINING_RECORD(CurrentEntry,
                                            ETHREAD,
                                            ThreadListEntry);

            /* Check if it's a thread that's still alive */
            if ((OtherThread != Thread) &&
                !(KeReadStateThread(&OtherThread->Tcb)) &&
                (ObReferenceObjectSafe(OtherThread)))
            {
                /* It's a live thread and we referenced it, unlock process */
                ExReleasePushLockExclusive(&CurrentProcess->ProcessLock);
                KeLeaveCriticalRegion();

                /* Wait on the thread */
                KeWaitForSingleObject(OtherThread,
                                      Executive,
                                      KernelMode,
                                      FALSE,
                                      NULL);

                /* Check if we had a previous thread to dereference */
                if (PreviousThread) ObDereferenceObject(PreviousThread);

                /* Remember the thread and re-lock the process */
                PreviousThread = OtherThread;
                KeEnterCriticalRegion();
                ExAcquirePushLockExclusive(&CurrentProcess->ProcessLock);
            }

            /* Go to the next thread */
            CurrentEntry = CurrentEntry->Flink;
        }
原因跟前面一样, 因为线程没有闭合, 导致死循环


尝试修改线程的Proc指针
dwAddr = (DWORD)(__int64)&pKrnDestLastThread->pThreadsProcess;
Nntdll::WriteKernelMem(dwAddr, sizeof(NNtKrn::EPROCESS_XP *), &ShellProc.dwObjAddr);  //修改线程的Proc指针


结果:篮屏, 
分析: PspExitThread
PAGE:805D18D6                 mov     eax, [esi+KTHREAD_XP.ApcState.Process]
PAGE:805D18D9                 cmp     edi, eax
PAGE:805D18DB                 jz      short loc_805D18EF
判定 当前进程不正常, 导致异常(KTHREAD_XP.ApcState.Process), 修改线程的Proc指针还有其他问题, 换个方法



回到前面的方法(CPU 50%, 无法响应)
在TerminateProcess 之后再恢复目标线程的链
结果: OK


想过修改句柄表, 这样更简单, 不过句柄表结构有点复杂

  • 标 题:答复
  • 作 者:zzz3265
  • 时 间:2010-06-13 11:55:05

主要代码, 没有整理...

BOOL  CHandleList::OnSupperKill()
{
  if(m_MenuOpItem.dwTypeTag != (DWORD &)"Proc")
    return FALSE;

  HANDLE        hProc;
  STARTUPINFOA    si;
  PROCESS_INFORMATION pi;

  NNtKrn::EPROCESS_XP  *    pKrnProc;
  DWORD            dwKrnAddr;
  DWORD            dwSize;
  DWORD            dwWriteAddr;
  EPROCESS_XP_R3        ShellProc;
  ETHREAD_XP_R3        ShellThread;

  DWORD          dwAddr;
  DWORD          dwAddrDlt;


  EPROCESS_XP_R3      DestProc;
  ETHREAD_XP_R3      DestThread;


  NNtKrn::LIST_ENTRY      ShellThreadList;
  NNtKrn::LIST_ENTRY      DestThreadList;



  memset(&si, 0, sizeof(si));
  si.cb = sizeof(si);


  CreateProcessA(NULL, "Notepad.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);

//  壳 数据
  dwKrnAddr = NNtKrn::GetHandleKrnObject(pi.hProcess);
  if(dwKrnAddr == 0)
    return FALSE;

  NLog::LogString("Shell dwKrnAddr: %08X", dwKrnAddr);

  ShellProc.dwObjAddr = dwKrnAddr;
  NNtKrn::ReadKrnObject(ShellProc);


  dwAddr = GetEThreadByThreadListHead(&ShellProc.Obj.ThreadListHead);
  ShellThread.dwObjAddr = dwAddr;
  NNtKrn::ReadKrnObject(ShellThread);


  if((DWORD)(__int64)ShellProc.Obj.UniqueProcessId != pi.dwProcessId)
    return FALSE;
  if((DWORD)(__int64)ShellThread.Obj.Cid.UniqueProcess != pi.dwProcessId)
    return FALSE;


//  目标 数据
  DestProc.dwObjAddr = (DWORD)(__int64)m_MenuOpItem.Base.Object;
  DestProc.Obj = m_MenuOpItem.KrnObj.EProcessXp;

  dwAddr = GetEThreadByThreadListHead(&DestProc.Obj.ThreadListHead);
  DestThread.dwObjAddr = dwAddr;
  NNtKrn::ReadKrnObject(DestThread);
  if((DWORD)(__int64)DestProc.Obj.UniqueProcessId != (DWORD)(__int64)DestThread.Obj.Cid.UniqueProcess)
    return FALSE;

  dwAddr  = NNtKrn::GetLastThreadAddr((DWORD)(__int64)m_MenuOpItem.Base.Object);
  if(dwAddr == 0)
    return FALSE;

  DestThread.dwObjAddr = dwAddr;
  NNtKrn::ReadKrnObject(DestThread);

  if((DWORD)(__int64)DestProc.Obj.UniqueProcessId != (DWORD)(__int64)DestThread.Obj.Cid.UniqueProcess)
    return FALSE;

  NNtKrn::ETHREAD_XP *    pKrnDestLastThread;
  NNtKrn::LIST_ENTRY *    pHead;
  LIST_ENTRY_R3        OldList;
  LIST_ENTRY_R3        MokeList;

  pKrnProc = (NNtKrn::EPROCESS_XP *)ShellProc.dwObjAddr;
  pHead = &pKrnProc->ThreadListHead;

  MokeList.Obj.Blink = pHead;
  MokeList.Obj.Flink = pHead;

  pKrnDestLastThread = (NNtKrn::ETHREAD_XP *)DestThread.dwObjAddr;
  MokeList.dwObjAddr = (DWORD)(__int64)&pKrnDestLastThread->ThreadListEntry;
  OldList.dwObjAddr = MokeList.dwObjAddr;
  NNtKrn::ReadKrnObject(OldList);
  NNtKrn::WriteKrnObject(MokeList);    //写入结束线程

  pKrnDestLastThread = (NNtKrn::ETHREAD_XP *)DestThread.dwObjAddr;
  dwAddr = (DWORD)(__int64)&pKrnDestLastThread->pThreadsProcess;
//  Nntdll::WriteKernelMem(dwAddr, sizeof(NNtKrn::EPROCESS_XP *), &ShellProc.dwObjAddr);  //修改线程的Proc指针


//  伪造
  ShellThreadList = ShellProc.Obj.ThreadListHead;
  DestThreadList = DestProc.Obj.ThreadListHead;

  pKrnProc = (NNtKrn::EPROCESS_XP *)ShellProc.dwObjAddr;
  dwWriteAddr = (DWORD)(__int64)&pKrnProc->ThreadListHead;

  dwSize = sizeof(NNtKrn::LIST_ENTRY);
  Nntdll::WriteKernelMem(dwWriteAddr, dwSize, &DestThreadList);  //写入首线程
  TerminateProcess(pi.hProcess, 0);

  NNtKrn::WriteKrnObject(OldList);      //恢复原来的列表


  Nntdll::WriteKernelMem(dwWriteAddr, dwSize, &ShellThreadList);  //恢复首线程
  TerminateProcess(pi.hProcess, 0);
  return TRUE;
}