进程防杀的实现

2024-03-05 02:58
文章标签 实现 进程 防杀

本文主要是介绍进程防杀的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进程防杀的实现 收藏
进程防杀的实现

在WINDOWS操作系统下,当我们无法结束或者不知道怎样结束一个程序的时候,或者是懒得去找“退出”按钮的时候,通常会按“CTRL+ALT+DEL”呼出任务管理器,找到想结束的程序,点一下“结束任务”就了事了,呵呵,虽然有点粗鲁,但大多数情况下都很有效,不是吗?

设想一下,如果有这么一种软件,它所要做的工作就是对某个使用者在某台电脑上的活动作一定的限制,而又不能被使用者通过“结束任务”这种方式轻易地解除限制,那该怎么做?无非有这么三种方法:1.屏蔽“CTRL+ALT+DEL”这个热键的组合;2.让程序不出现在任务管理器的列表之中;3.让任务管理器无法杀掉这个任务。对于第一种方法,这样未免也太残酷了,用惯了“结束任务”这种方法的人会很不习惯的;对于第二种方法,在WINDOWS 9X下可以很轻易地使用注册服务进程的方法实现,但是对于WINDOWS NT架构的操作系统没有这个方法了,进程很难藏身,虽然仍然可以实现隐藏,但实现机制较为复杂;对于第三种方法,实现起来比较简单,我的作品:IPGate 网址过滤器 就是采用的这种方式防杀的,接下来我就来介绍这种方法。

任务管理器的“结束任务”实际上就是强制终止进程,它所使用的杀手锏是一个叫做TerminateProcess()的Win32 API函数,我们来看看它的定义:

BOOL TerminateProcess(
HANDLE hProcess; // 将被结束进程的句柄
UINT uExitCode; // 指定进程的退出码
);

看到这里,是不是觉得不必往下看都知道接下来要做什么:Hook TerminateProcess()函数,每次TerminateProcess()被调用的时候先判断企图结束的进程是否是我的进程,如果是的话就简单地返回一个错误码就可以了。真的是这么简单吗?先提出一个问题,如何根据hProcess判断它是否是我的进程的句柄?答案是:在我的进程当中先获得我的进程的句柄,然后通过进程间通讯机制传递给钩子函数,与hProcess进行比较不就行了?错!因为句柄是一个进程相关的值,不同进程中得到的我的进程的句柄的值在进程间进行比较是无意义的。

怎么办?我们来考察一下我的hProcess它是如何得到的。一个进程只有它的进程ID是独一无二的,操作系统通过进程ID来标识一个进程,当某个程序要对这个进程进行访问的话,它首先得用OpenProcess这个函数并传入要访问的进程ID来获得进程的句柄,来看看它的参数:

HANDLE OpenProcess(
DWORD dwDesiredAccess, // 希望获得的访问权限
BOOL bInheritHandle, // 指明是否希望所获得的句柄可以继承
DWORD dwProcessId // 要访问的进程ID
);

脉络渐渐显现:在调用TerminateProcess()之前,必先调用OpenProcess(),而OpenProcess()的参数表中的dwProcessId是在系统范围内唯一确定的。得出结论:要Hook的函数不是TerminateProcess()而是OpenProcess(),在每次调用OpenProcess()的时候,我们先检查dwProcessId是否为我的进程的ID(利用进程间通讯机制),如果是的话就简单地返回一个错误码就可以了,任务管理器拿不到我的进程的句柄,它如何结束我的进程呢?

至此,疑团全部揭开了。由Hook TerminateProcess()到Hook OpenProcess()的这个过程,体现了一个逆向思维的思想。其实我当初钻进了TerminateProcess()的死胡同里半天出也不来,但最终还是蹦出了灵感的火花,注意力转移到了OpenProcess()上面,实现了进程防杀。喜悦之余,将这心得体会拿出来与大家分享。


windowsNT下的通过截获OpenProcess函数来禁止终止本进程的VC代码如下:

StickyApp32.cpp文件

#include
#include "HookAPI.h"

typedef HANDLE (__stdcall *OPENPROCESS_PROC)(DWORD, BOOL, DWORD);

OPENPROCESS_PROC pOpenProcess = NULL;
 
HANDLE __stdcall OpenProcess_Handler(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId)
{
  HANDLE RetValue = NULL;
  HWND hWnd;
  DWORD ProcessId;

  hWnd = FindWindow("ThunderRT5Form", "StickyApp32");

  GetWindowThreadProcessId(hWnd, &ProcessId);

  if (dwProcessId != ProcessId)
    RetValue = pOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);

  return RetValue;
}


__declspec(dllexport) LRESULT CALLBACK HookFunction(int code, WPARAM wParam, LPARAM lParam)
{
  if (pOpenProcess == NULL)
    pOpenProcess = (OPENPROCESS_PROC)HookAPIFunction(GetModuleHandle(NULL), "KERNEL32.DLL", "OpenProcess", (PROC)OpenProcess_Handler);

  return false;
}


BOOL WINAPI DllMain(HANDLE hInst, ULONG dwReason, LPVOID lpReserved)
{
  switch (dwReason)
  {
    case DLL_PROCESS_ATTACH:

      DisableThreadLibraryCalls(hInst);

    break;
  }

  return true;
}


HOOKAPI.cpp文件

// -----------------------------
//  HOOKAPI - Matt Pietrek 1995
// -----------------------------

#include
#include "HookAPI.h"

// Macro for adding pointers/DWORDs together without C arithmetic interfering

#define MakePtr(cast, ptr, addValue) (cast)((DWORD)(ptr)+(DWORD)(addValue))

PROC HookAPIFunction(HMODULE hFromModule,
                     PSTR pszFunctionModule,
                     PSTR pszFunctionName,
                     PROC pfnNewProc)
{
  PROC pfnOriginalProc;
  PIMAGE_DOS_HEADER pDosHeader;
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
  PIMAGE_THUNK_DATA pThunk;

  DWORD dwProtectionFlags;
  DWORD dwScratch;
 
  // Verify that a valid pfn was passed

  if (IsBadCodePtr(pfnNewProc)) return 0;
   
  // First, verify the the module and function names passed to use are valid

  pfnOriginalProc = GetProcAddress(GetModuleHandle(pszFunctionModule), pszFunctionName);

  if (!pfnOriginalProc) return 0;

  pDosHeader = (PIMAGE_DOS_HEADER)hFromModule;

  // Tests to make sure we're looking at a module image (the 'MZ' header)

  if (IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER))) return 0;

  if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return 0;

  // The MZ header has a pointer to the PE header

  pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew);

  // More tests to make sure we're looking at a "PE" image

  if (IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS))) return 0;

  if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) return 0;

  // We know have a valid pointer to the module's PE header.
  // Now go get a pointer to its imports section

  pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDosHeader,
                        pNTHeader->OptionalHeader.
                        DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
                        VirtualAddress);
                       
  // Bail out if the RVA of the imports section is 0 (it doesn't exist)

  if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader) return 0;

  // Iterate through the array of imported module descriptors, looking
  // for the module whose name matches the pszFunctionModule parameter

  while (pImportDesc->Name)
  {
    PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name);
       
    if (stricmp(pszModName, pszFunctionModule) == 0) break;

    // Advance to next imported module descriptor

    pImportDesc++;
  }

  // Bail out if we didn't find the import module descriptor for the
  // specified module.  pImportDesc->Name will be non-zero if we found it.

  if (pImportDesc->Name == 0) return 0;

  // Get a pointer to the found module's import address table (IAT)

  pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk);

  // Blast through the table of import addresses, looking for the one
  // that matches the address we got back from GetProcAddress above.

  while (pThunk->u1.Function)
  {
    if (pThunk->u1.Function == (PDWORD)pfnOriginalProc)
    {
      dwProtectionFlags = PAGE_READWRITE;

      VirtualProtect(&pThunk->u1.Function, 4096, dwProtectionFlags, &dwScratch);

      // We found it!  Overwrite the original address with the
      // address of the interception function.  Return the original
      // address to the caller so that they can chain on to it.

      pThunk->u1.Function = (PDWORD)pfnNewProc;
     
      return pfnOriginalProc;
    }
       
    // Advance to next imported function address

    pThunk++;
  }
   
  // Function not found

  return 0;
}


Hookapi.h文件

#ifndef HOOKAPI_H
#define HOOKAPI_H

PROC HookAPIFunction(HMODULE hFromModule,
                     PSTR pszFunctionModule,
                     PSTR pszFunctionName,
                     PROC pfnNewProc);

#endif

 

 

这篇关于进程防杀的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/775115

相关文章

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Mysql实现范围分区表(新增、删除、重组、查看)

《Mysql实现范围分区表(新增、删除、重组、查看)》MySQL分区表的四种类型(范围、哈希、列表、键值),主要介绍了范围分区的创建、查询、添加、删除及重组织操作,具有一定的参考价值,感兴趣的可以了解... 目录一、mysql分区表分类二、范围分区(Range Partitioning1、新建分区表:2、分

MySQL 定时新增分区的实现示例

《MySQL定时新增分区的实现示例》本文主要介绍了通过存储过程和定时任务实现MySQL分区的自动创建,解决大数据量下手动维护的繁琐问题,具有一定的参考价值,感兴趣的可以了解一下... mysql创建好分区之后,有时候会需要自动创建分区。比如,一些表数据量非常大,有些数据是热点数据,按照日期分区MululbU

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

IDEA中新建/切换Git分支的实现步骤

《IDEA中新建/切换Git分支的实现步骤》本文主要介绍了IDEA中新建/切换Git分支的实现步骤,通过菜单创建新分支并选择是否切换,创建后在Git详情或右键Checkout中切换分支,感兴趣的可以了... 前提:项目已被Git托管1、点击上方栏Git->NewBrancjsh...2、输入新的分支的

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存