企业即时通讯 - Enterprise Instant Messenger

局域网聊天工具,文字讯息、文件发送、语音通讯、高清视频通讯、远程桌面控制。

用户操作
[即时聊天] [发私信] [加为好友]
FreeEIM StudioID:i_like_cpp
972409次访问,排名32,好友4人,关注者7人。
i_like_cpp的文章
原创 888 篇
翻译 4 篇
转载 69 篇
评论 1148 篇
FreeEIM Studio的公告
最近评论
ScanerKi:#include <stdio.h>

int asm(int s)
{
int t=0;
__asm
{
mov eax, DWORD PTR [ebp+8] ;把s的值传给eax
mov t, eax ;把eax的值传给t
}
printf("- %d……
li_delong:谢谢
li_delong:谢谢
li_delong:谢谢
XUETUJIAN:
文章分类
收藏
相册
EIM 截图
相关软件图片
PI的最精确值
FreeEIM 标签
FreeEIM华军下载
XP SP2 SDK
下载FreeEIM
盛天龙
飞鸽传书
不错的网站
CPPBLOG
局域网聊天
泡妞专家
用VC写Assembly代码(RSS)
百度的Blog(RSS)
存档
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 内核级利用通用Hook函数方法检测进程收藏

新一篇: FreeBSD下构建安全的Web服务器 | 旧一篇: headerf.h

介绍通用Hook的一点思想:
    在系统内核级中,MS的很多信息都没公开,包括函数的参数数目,每个参数的类型等。在系统内核中,访问了大量的寄存器,而很多寄存器的值,是上层调用者提供的。如果值改变系统就会变得不稳定。很可能出现不可想象的后果。另外有时候对需要Hook的函数的参数不了解,所以不能随便就去改变它的堆栈,如果不小心也有可能导致蓝屏。所以Hook的最佳原则是在自己的Hook函数中呼叫原函数的时候,所有的寄存器值,堆栈里面的值和Hook前的信息一样。这样就能保证在原函数中不会出错。一般我们自己的Hook的函数都是写在C文件里面的。例如Hook的目标函数KiReadyThread。
那么一般就自己实现一个:
MyKiReadyThread(...)
{
    ......
    call KiReadyThread
    ......
}
但是用C编译器编译出来的代码会出现一个堆栈帧:
Push ebp
mov  ebp,esp
这就和我们的初衷不改变寄存器的数违背了。所以我们可以自己用汇编来实现MyKiReadyThread。

_func@0 proc
    pushad        ;保存通用寄存器
    call _cfunc@0  ;这里是在进入原来函数前进行的一些处理。
    popad         ;恢复通用寄存器
    push eax  
    mov eax,[esp+4] ;得到系统在call 目标函数时入栈的返回地址。
    mov ds:_OrgRet,eax ;保存在一个临时变量中
    pop eax
    mov [esp],retaddr  ;把目标函数的返回地址改成自己的代码空间的返回地址,使其返回                后能接手继续的处理
    jmp _OrgDestFunction ;跳到原目标函数中
retaddr:
    pushad         ;原函数处理完后保存寄存器
    call _HookDestFunction@0 ;再处理
    popad      ;回复寄存器
    jmp ds:_OrgRet ;跳到系统调用目标函数的下一条指令。
_func@0 endp

当我们要拦截目标API的时候,只要修改原函数头5个字节的机器为一个JMP _func就行了。
然后把原来的5字节保存。在跳入原函数时,恢复那5个字节即可。

Hook KiReadyThread检测系统中的进程:
在线程调度抢占的的时候会调用KiReadyThread,它的原型为
VOID FASTCALL KiReadyThread (IN PRKTHREAD Thread)
在进入KiReadyThread时,ecx指向Thread。
所以完全可以Hook KiReadyThread 然后用ecx的值得到但前线程的进程信息。
KiReadyThread没被ntosknrl.exe导出,所以通过硬编码来。在2000Sp4中地址为0x8043141f

具体实现:
////////////////////////////////
// 1.cpp
////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif

#include "ntddk.h"
#include "string.h"
#include "ntifs.h"
#include "stdio.h"

#define FILE_DEVICE_EVENT  0x8000

#define IOCTL_PASSBUF \
    CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)

void DriverUnload (IN PDRIVER_OBJECT pDriverObject);

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);

void cfunc ();

void HookDestFunction();
NTSTATUS DeviceIoControlDispatch(IN  PDEVICE_OBJECT  DeviceObject,
                                 IN  PIRP            pIrp);
extern void func();

void ResumeDestFunction();

const WCHAR devLink[]  = L"\\??\\MyEvent";
const WCHAR devName[]  = L"\\Device\\MyEvent";
UNICODE_STRING          devNameUnicd;
UNICODE_STRING          devLinkUnicd;    

ULONG OrgDestFunction = (ULONG)0x8043141f; //KiReadyThread

char JmpMyCode [] = {0xE9,0x00,0x00,0x00,0x00};
char OrgCode [5];

char OutBuf[128][16];

int Count = 0;

ULONG orgcr0;
#ifdef __cplusplus
}
#endif

VOID DisableWriteProtect( PULONG pOldAttr)
{

     ULONG uAttr;

     _asm
    {
          push eax;
          mov  eax, cr0;
          mov  uAttr, eax;
          and  eax, 0FFFEFFFFh; // CR0 16 BIT = 0
          mov  cr0, eax;
          pop  eax;
    };

     *pOldAttr = uAttr; //保存原有的 CRO 属性

}

VOID EnableWriteProtect( ULONG uOldAttr )
{

  _asm
  {
       push eax;
       mov  eax, uOldAttr; //恢复原有 CR0 属性
       mov  cr0, eax;
       pop  eax;
  };

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS                Status;
    PDEVICE_OBJECT            pDevice;

    DbgPrint("DriverEntry called!\n");
    RtlInitUnicodeString (&devNameUnicd, devName );
    RtlInitUnicodeString (&devLinkUnicd, devLink );
    Status = IoCreateDevice ( pDriverObject,
    0,
         &devNameUnicd,
    FILE_DEVICE_UNKNOWN,
       0,
       TRUE,
       &pDevice );
        if( !NT_SUCCESS(Status))
        {
        DbgPrint(("Can not create device.\n"));
        return Status;
    }
       Status = IoCreateSymbolicLink (&devLinkUnicd, &devNameUnicd);
       if( !NT_SUCCESS(Status))
       {
           DbgPrint(("Cannot create link.\n"));
           return Status;
       }
       pDriverObject->DriverUnload  = DriverUnload;
       pDriverObject->MajorFunction[IRP_MJ_CREATE] =
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =    DeviceIoControlDispatch;
      
    pDriverObject->DriverUnload = DriverUnload;
    * ( (ULONG*) (JmpMyCode+1) ) = (ULONG)func - (ULONG)OrgDestFunction - 5;
    memcpy(OrgCode,(char*)OrgDestFunction,5);
    HookDestFunction();
    
    return STATUS_SUCCESS;
}

void DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
    NTSTATUS            status;
    ResumeDestFunction();
    if(pDriverObject->DeviceObject != NULL)
        {
           status=IoDeleteSymbolicLink( &devLinkUnicd );
        if ( !NT_SUCCESS( status ) )
            {
                    DbgPrint((  "IoDeleteSymbolicLink() failed\n" ));
            }
            IoDeleteDevice( pDriverObject->DeviceObject );
        }
}

void DisplayName(PKTHREAD Thread)
{
    PKPROCESS Process = Thread->ApcState.Process;
    PEPROCESS pEprocess = (PEPROCESS)Process;
    DbgPrint("ImageFileName = %s \n",pEprocess->ImageFileName);
    sprintf(OutBuf[Count++],"%s",pEprocess->ImageFileName);
}

void cfunc (void)
{
    ULONG PKHeader=0;
    __asm
    {
        mov PKHeader,ecx  //ecx寄存器是KiReadyThread中的PRKTHREAD参数
    }
    ResumeDestFunction();
    
    if ( PKHeader != 0 && Count < 128 )
    {
        DisplayName((PKTHREAD)PKHeader);    
    }    
}

void HookDestFunction()
{
    DisableWriteProtect(&orgcr0);
    memcpy((char*)OrgDestFunction,JmpMyCode,5);
    EnableWriteProtect(orgcr0);    
}

void ResumeDestFunction()
{
    DisableWriteProtect(&orgcr0);
    memcpy((char*)OrgDestFunction,OrgCode,5);
    EnableWriteProtect(orgcr0);
}

NTSTATUS DeviceIoControlDispatch(
                                 IN  PDEVICE_OBJECT  DeviceObject,
                                 IN  PIRP            pIrp
                                 )
{
    PIO_STACK_LOCATION              irpStack;
    NTSTATUS                        status;
    PVOID                           inputBuffer;
    ULONG                           inputLength;
    PVOID                           outputBuffer;
    ULONG                           outputLength;
    OBJECT_HANDLE_INFORMATION       objHandleInfo;

    status = STATUS_SUCCESS;
    // 取出IOCTL请求代码
    irpStack = IoGetCurrentIrpStackLocation(pIrp);

    switch (irpStack->MajorFunction)
    {
    case IRP_MJ_CREATE :
        DbgPrint("Call IRP_MJ_CREATE\n");
        break;
    case IRP_MJ_CLOSE:
        DbgPrint("Call IRP_MJ_CLOSE\n");
        break;
    case IRP_MJ_DEVICE_CONTROL:
        DbgPrint("IRP_MJ_DEVICE_CONTROL\n");
        inputLength=irpStack->Parameters.DeviceIoControl.InputBufferLength;
        outputLength=irpStack->Parameters.DeviceIoControl.OutputBufferLength;
        switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
        {
            case    IOCTL_PASSBUF:
            {
                RtlCopyMemory(pIrp->UserBuffer, OutBuf, 20*16);
                
                memset(OutBuf,0,128*16);
                Count = 0;
                break;
            }
            default:
                break;
        }

    default:
        DbgPrint("Call IRP_MJ_UNKNOWN\n");
        break;
    }
    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest (pIrp, IO_NO_INCREMENT);
    return status;
}

////////////////////////////////
// 1.asm
////////////////////////////////
.386
.model small

.data
_OrgRet dd 0

.code
public _func@0
extrn _cfunc@0:near
extrn _HookDestFunction@0:near
extrn _OrgDestFunction:DWORD

_func@0 proc
    pushad
    call _cfunc@0
    popad
    push eax
    mov eax,[esp+4]
    mov ds:_OrgRet,eax
    pop eax
    mov [esp],retaddr
    jmp _OrgDestFunction
retaddr:
    pushad
    call _HookDestFunction@0
    popad
    jmp ds:_OrgRet
_func@0 endp
END

//////////////////////////////////////////
// app.cpp
//////////////////////////////////////////

#include <windows.h>
#include <stdio.h>

#define FILE_DEVICE_EVENT  0x8000
#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

#define FILE_ANY_ACCESS                 0
#define METHOD_BUFFERED                 0
#define FILE_DEVICE_UNKNOWN             0x00000022

#define IOCTL_PASSBUF \
    CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)

int main()
{
       HANDLE      hDevice;    
        bool        status;
       ULONG       dwReturn;
        char        outbuf[129][16];
        hDevice = NULL;
        m_hCommEvent = NULL;
        hDevice = CreateFile( "\\\\.\\MyEvent",
                    GENERIC_READ|GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
        if(hDevice == INVALID_HANDLE_VALUE)
        {
           printf("createfile wrong\n");
           getchar();
           return 0;
        }
    while(1)
    {
        memset(outbuf,0,129*16);
        status =DeviceIoControl(hDevice,
                    IOCTL_PASSBUF,
                    NULL,
                    0,
                    &outbuf,
                    128*16,
                    &dwReturn,NULL);
        if( !status)
        {
            printf("IO wrong+%d\n", GetLastError());
            getchar();
            return 0;
        }
        int c=0;
        while( *((char*)(&outbuf)+c*16) )
        {
            //把csrss.exe和自身进程信息跳过,因为会产生有大量的信息。
            if ( strcmp((char*)(&outbuf)+c*16,"app.exe") && \
                 strcmp((char*)(&outbuf)+c*16,"csrss.exe")  )
                printf("%s\n",(char*)(&outbuf)+c*16);
            c++;
        }
        Sleep(1);
    }
}

试验结果:
......
TTPlayer.exe
System
TTPlayer.exe
vrvmon.exe
TTPlayer.exe
System
System
Explorer.EXE
Explorer.EXE
Explorer.EXE
......
测试,编译环境 2000 Sp4 2000 DDK
没写出线程的隐藏进程代码。不过基本上实现得差不多了,只需要把返回的信息,和Ring3级查询得到的信息进行适时对比就能查出异常进程了。

本人水平有限,如哪里有错误,欢迎高手不吝赐教。
感谢:sinister大哥对小弟的指点及其鼓励
毕业在即将次愚作献给我的母校---重庆科技学院

发表于 @ 2005年06月28日 10:33:00|评论(loading...)|编辑

新一篇: FreeBSD下构建安全的Web服务器 | 旧一篇: headerf.h

评论

#SymbianLover 发表于2005-07-15 05:27:00  IP:
TrackBack来自《内核级利用通用Hook函数方法检测进程 》

Ping Back来自:blog.csdn.net
发表评论  


登录
Csdn Blog version 3.1a
Copyright © FreeEIM Studio