windows平台中,某些进程做了各种保护,比如hook了terminateProcess,又或者注册了进程终止函数的回调。当调用这些API或任务管理器终止该进程时,会被绕过,典型如某些杀毒软件,怎么才能终止这些进程了?

进程是由线程组成的,如果该进程名下所有线程都终止,此进程也会被windows回收和注销,终止进程的问题就转化成了终止线程;但如果直接调用terminateThread,同样面临terminateProcess被hook的窘境。深入逆向分析terminateThread后发现,真正终止线程的函数是PspTerminateThreadByPointer,整个调用逻辑为:NtTerminateThread->PsTerminateSystemThread->PspTerminateThreadByPointer,其中PsTerminateSystemThread是导入未文档化函数,可在驱动层掉用MmGetSystemRoutineAddress函数获取地址,进而得到PspTerminateThreadByPointer的地址(当然也能使用https://www.cnblogs.com/theseventhson/p/13024325.html该方法获取),核心函数如下:

1、根据起始和终止地址、特征码查找代码偏移

PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = ; // 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判断特征码
for (m = ; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize)
{
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
} return pAddress;
}

2、(1)PsTerminateSystemThread是导出未文档化的函数,可以直接用MmGetSystemRoutineAddress得到函数地址

(2)windbg中根据PsTerminateSystemThread进一步查找PspTerminateThreadByPointer:这里用了e8作为特征码,直接定位到“e8dcf0fbff      call    nt!PspTerminateThreadByPointer (fffff803`d01c6210)”这行代码;

kd> u 0xfffff803`d0207110   //下面偏移x20 = 32byte处

nt!PsTerminateSystemThread:

fffff803`d0207110 4883ec28        sub     rsp,28h

fffff803`d0207114 8bd1            mov     edx,ecx

fffff803`d0207116 65488b0c2588010000 mov   rcx,qword ptr gs:[188h]

fffff803`d020711f f7417400040000  test    dword ptr [rcx+74h],400h

fffff803`d0207126 0f84a0630e00    je      nt!PsTerminateSystemThread+0xe63bc (fffff803`d02ed4cc)

fffff803`d020712c 41b001          mov     r8b,1

fffff803`d020712f e8dcf0fbff      call    nt!PspTerminateThreadByPointer (fffff803`d01c6210)

  熟悉x86汇编的都知道:e8是call的硬编码,后面dcf0fbff是目标地址当对于当前的偏移,偏移为0xfffbf0dc。这个偏移很大,根据经验判断应该是个负数,0n-266020,那么PspTerminateThreadByPointer的计算方法:

  PspTerminateThreadByPointer =  当前地址 + 4 + 偏移(负数)

=0xfffff800`63f7e130 + 0x4 + 0n-266020

= ‭FFFFF80063F7E134‬ + 0n-266020

= -8,794,415,832,780 - 266020 //统一转成10进制

= -8,794,416,098,800‬

= FFFF F800 63F3 D210‬

继续windbg查一下:发现这个地址确实是PspTerminateThreadByPointer的,没错:

kd> u 0xfffff800`63f3d210

nt!PspTerminateThreadByPointer:

fffff800`63f3d210 48895c2408      mov     qword ptr [rsp+8],rbx

fffff800`63f3d215 48896c2410      mov     qword ptr [rsp+10h],rbp

fffff800`63f3d21a 4889742418      mov     qword ptr [rsp+18h],rsi

fffff800`63f3d21f 57              push    rdi

fffff800`63f3d220 4883ec30        sub     rsp,30h

fffff800`63f3d224 8b81d0060000    mov     eax,dword ptr [rcx+6D0h]

fffff800`63f3d22a 418ae8          mov     bpl,r8b

fffff800`63f3d22d 488bb920020000  mov     rdi,qword ptr [rcx+220h]

  详细代码如下(这里pSpecialData用E8就好):

PVOID SearchPspTerminateThreadByPointer(PUCHAR pSpecialData, ULONG ulSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = ;
PVOID pPsTerminateSystemThread = NULL;
PVOID pPspTerminateThreadByPointer = NULL; // 先获取 PsTerminateSystemThread 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsTerminateSystemThread");
pPsTerminateSystemThread = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsTerminateSystemThread)
{
ShowError("MmGetSystemRoutineAddress", );
return pPspTerminateThreadByPointer;
} // 然后, 查找 PspTerminateThreadByPointer 函数地址
pAddress = SearchMemory(pPsTerminateSystemThread,
(PVOID)((PUCHAR)pPsTerminateSystemThread + 0xFF),//搜索255字节长度
pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
ShowError("SearchMemory", );
return pPspTerminateThreadByPointer;
} // 先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;//0n-266020。注意这里向前跳,偏移是负数,有符号
pPspTerminateThreadByPointer = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset); return pPspTerminateThreadByPointer;
}

3、得到PspTerminateThreadByPointer地址:

PVOID GetPspLoadImageNotifyRoutine()
{
PVOID pPspTerminateThreadByPointerAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { };
UCHAR pSpecialData[] = { };
ULONG ulSpecialDataSize = ; pSpecialData[] = 0xE8;
ulSpecialDataSize = ; // E8
pSpecialData[] = 0xE8;
ulSpecialDataSize = ; // 根据特征码获取地址
pPspTerminateThreadByPointerAddress = SearchPspTerminateThreadByPointer(pSpecialData, ulSpecialDataSize);
return pPspTerminateThreadByPointerAddress;
}

4、现在可以强杀进程了:

// 强制结束指定进程
NTSTATUS ForceKillProcess(HANDLE hProcessId)
{
PVOID pPspTerminateThreadByPointerAddress = NULL;
PEPROCESS pEProcess = NULL;
PETHREAD pEThread = NULL;
PEPROCESS pThreadEProcess = NULL;
NTSTATUS status = STATUS_SUCCESS;
ULONG i = ; #ifdef _WIN64
// 64 位
typedef NTSTATUS(__fastcall *PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);
#else
// 32 位
typedef NTSTATUS(*PSPTERMINATETHREADBYPOINTER) (PETHREAD pEThread, NTSTATUS ntExitCode, BOOLEAN bDirectTerminate);
#endif // 获取 PspTerminateThreadByPointer 函数地址
pPspTerminateThreadByPointerAddress = GetPspLoadImageNotifyRoutine();
if (NULL == pPspTerminateThreadByPointerAddress)
{
ShowError("GetPspLoadImageNotifyRoutine", );
return FALSE;
}
// 获取结束进程的进程结构对象EPROCESS
status = PsLookupProcessByProcessId(hProcessId, &pEProcess);
if (!NT_SUCCESS(status))
{
ShowError("PsLookupProcessByProcessId", status);
return status;
}
// 遍历所有线程, 并结束所有指定进程的线程
for (i = ; i < 0x80000; i = i + )
{
status = PsLookupThreadByThreadId((HANDLE)i, &pEThread);
if (NT_SUCCESS(status))
{
// 获取线程对应的进程结构对象
pThreadEProcess = PsGetThreadProcess(pEThread);
// 结束指定进程的线程
if (pEProcess == pThreadEProcess)
{
((PSPTERMINATETHREADBYPOINTER)pPspTerminateThreadByPointerAddress)(pEThread, , );
DbgPrint("PspTerminateThreadByPointer Thread:%d\n", i);
}
// 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏
ObDereferenceObject(pEThread);
}
}
// 凡是Lookup...,必需Dereference,否则在某些时候会造成蓝屏
ObDereferenceObject(pEProcess); return status;
}

5、测试环境:

windows:进程查杀的更多相关文章

  1. Linux 僵尸进程查杀

    僵尸进程概念 僵尸进程(Zombie process)通俗来说指那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸. 书面形式一点:一个进程结束了,但是他的父进程没有等待(调用wait ...

  2. windows查看端口占用情况及查杀进程

    我们平时在做web开发运行web服务器或运行某个应用时会报错,提示该应用的端口号已被占用,我们可以用以下的方法解决. 解决方法一:重新为应用配置端口. 解决方法二:找到占用端口的应用并关闭该应用释放占 ...

  3. shell脚本执行查找进程,然后查杀进程

    shell 执行查找进程,然后查杀进程脚本如下: ps -ef | grep 'IOE' |grep -v 'grep'| awk '{print \$2}' |while read pid; do ...

  4. centos clamav杀毒软件安装配置及查杀,没想到linux下病毒比windows还多!

    centos clamav杀毒软件安装配置及查杀,没想到linux下病毒比windows还多! 一.手动安装 1.下载(官网)    cd /soft     wget http://www.clam ...

  5. 查杀进程小工具——WPF和MVVM初体验

    最近因为工作需要,研究了一下桌面应用程序.在winform.WPF.Electron等几种技术里,最终选择了WPF作为最后的选型.WPF最吸引我的地方,就是MVVM模式了.MVVM模式完全把界面和业务 ...

  6. Linux进程管理:查杀进程

    一.查看进程 Linux下显示系统进程的命令ps,最常用的有ps -ef 和ps aux.这两个到底有什么区别呢? 两者没太大差别,讨论这个问题,要追溯到Unix系统中的两种风格,System V风格 ...

  7. Linux 僵尸进程的筛选和查杀

    一.筛选 ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' 二.查杀 ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' ...

  8. db2 查杀死锁进程

    db2 查杀死锁进命令 db2 get snapshot for locks on (需要snapshot的访问权限) db2 list applications db2 "force ap ...

  9. CMD查看进程ID并查杀进程

    开始-运行,输入CMD打开命令行界面,输入命令netstat -ano 结束该进程C:\>taskkill /f /t /im Wiz.exe 根据进程ID杀 >taskkill /F / ...

随机推荐

  1. Redis做为缓存的几个问题

    缓存理流程: 前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果. 1.缓存雪崩 解决方案3:如果缓存数据库是分布 ...

  2. mysql--数据插入覆盖和时间戳的问题

    1.数据插入问题 当我们insert数据时,插入datetime类型,此类型的字段,存储数据格式为:  YYYY-MM-DD,它支持的范围为'1000-01-01'到'9999-12-31',并且允许 ...

  3. .Net Core 2.2升级3.1的避坑指南

    写在前面 微软在更新.Net Core版本的时候,动作往往很大,使得每次更新版本的时候都得小心翼翼,坑实在是太多.往往是悄咪咪的移除了某项功能或者组件,或者不在支持XX方法,这就很花时间去找回需要的东 ...

  4. 在Access中执行SQL

    1.基本介绍 Microsoft Access在很多地方得到广泛使用,例如小型企业,大公司的部门.喜爱编程的开发人员亦利用它来制作处理数据的桌面系统.它也常被用来开发简单的WEB应用程序. 2.Ace ...

  5. 集训作业 洛谷P1010 幂次方

    这个…… 这个题看上去有点难的样子. 仔细看看,感觉有点简单.啊,是递归啊,正经的看一看,好像是把一个数分成2的几次方的和. 然后余数和比他小的最大的2的次方数如果不是2的一次方或者2的0次方,就继续 ...

  6. T133316 57级返校测试重测-T4-字符串的修改

    大致题意: 有一个A字符串和一个B字符串, 操作将A或A的一个后缀修改为B, 求最少的操作数. 有三个操作为: 删除: 删除掉 A 中的某一个字符. 添加: 将某一个字符添加到 A 中任意位置. 替换 ...

  7. DEBUG ArrayList

    1,ArrayList面试必问 说说ArrayList和LinkedList的区别? ArrayList基于数组实现,LinkedList基于链表实现,不同的数据结构决定了ArrayList查询效率比 ...

  8. NIO实践-HTTP交互实现暨简版Tomcat交互内核

    今天就NIO实现简单的HTTP交互做一下笔记,进而来加深Tomcat源码印象. 一.关于HTTP 1.HTTP的两个显著特点,HTTP是一种可靠的超文本传输协议 第一.实际中,浏览器作为客户端,每次访 ...

  9. tk.mybatis selectByPrimaryKey无法正确识别主键

    selectByPrimaryKey无法正确识别主键,查看日志,发现报如下错误: ==> Preparing: SELECT username,password,name,age,sex,bir ...

  10. super,this关键字用法 Java

    super 用法 1.调用父类变量2.调用父类方法3.子类构造方法第一句 this 用法 super关键字用来访问父类内容, this 关键字用来访问本类中的内容, 有三种用法 1.在本类的成员方法中 ...