前几天,我看了网上流传的有关KeUserModeCallBack 函数的使用方法(主要是:如果在回调你的应用程序的时候,应用程序发生了崩溃,或者在弹出那个消息框的时候,
你用任务管理器个KILL了的话,你的进程就会成为"僵尸",在系统中无法删除了),感觉有些不好,于是自己重新编写了代码,这个代码可以实现类似WINDOWS消息机制的一个机制,
来在进程间传递消息,如果在内核中使用的话,还可以从不同的内核线程中传递消息到你的应用程序(由于我编写的驱动是提供给应用程序调用的,所以我加上了有关内存读写的限制,
如果在驱动中使用的话,请去掉这些限制)



KeUserModeCallBack的调用过程,我相信大家已经知道了,
我就不重复了,我现在将代码贴出,并在每一句上给予讲解,讲的不好的请大家原谅,又不懂的,请大家在QQ上问我 


驱动程序编译环境 WDK 7600.16385.1
应用程序编译环境 VC6.0

驱动程序代码
 源文件名 SendMessage.c
//////////////////////////////////////////////////////////////
程序定义
#include <ntifs.h>
#include "debug.h" //这个头文件中主要含有一个调试宏,用来测试是否是调试版本
#include "IoCreateDriver.h"
#include "EXTNDDRV.H"
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
HANDLE ProcessHandle = NULL;
ULONG ApiIndex = 0;
LIST_ENTRY DataList;
LIST_ENTRY SendingDataList;
KEVENT  ListEvent;
KSPIN_LOCK  Lock;

typedef struct _SENDTOUSERMSG
{
ULONG MessageIndex;
size_t Size;
size_t DataSize;
char Dataof[1];
}SENDTOUSERMSG,*PSENDTOUSERMSG;//通过KeUserModeCallBack发送到你的应用程序处理函数的数据结构
typedef struct _MSGDATA
{
ULONG MessageIndex;
PVOID pData;
size_t DataSize;
NTSTATUS Status;
NTSTATUS *UserRetNtstatus;
int IsSend;
KEVENT  Event;
LIST_ENTRY DataList;

}MSGDATA,*PMSGDATA;//用来将要发送的数据临时保存在链表中的一个数据结构
ULONG GetCurrentProcessPEB(VOID);//取得本进程的PEB结构
LARGE_INTEGER Time;
NTSTATUS SendData(PMSGDATA MsgData);//用于向你的处理函数发送数据的函数
NTSTATUS
DriverEntry(
  IN PDRIVER_OBJECT    DriverObject,
  IN PUNICODE_STRING    RegistryPath
  );//入口函数
NTKERNELAPI
NTSTATUS
KeUserModeCallback(
  IN ULONG ApiNumber,
  IN PVOID InputBuffer,
  IN ULONG InputLength,
  OUT PVOID *OutputBuffer,
  IN PULONG OutputLength
    );
#if DBG
VOID
CallbackUnload(
  IN PDRIVER_OBJECT    DriverObject
  );
#endif
NTSYSAPI
NTSTATUS
NTAPI  SetCallBack(ULONG FunctionIndex); //用于设置索引,系统要根据这个索引,在KernelCallBackTable中取得指定函数的地址(在RING 3的代码中,我会贴出如何在本线程的KernelCallBackTable中添加自己的处理函数)
NTSYSAPI
NTSTATUS
NTAPI  UnSetCallBack();//删除所有回调,并且让处于等待中的要处理的请求返回请求发起程序
NTSYSAPI
NTSTATUS
NTAPI  CallBack();//用于激发KeUserNodeCallBack调用
VOID Process(
    IN HANDLE  ParentId,
    IN HANDLE  ProcessId,
    IN BOOLEAN  Create
    );//用于收尾工作
UNICODE_STRING  DeviceName;
UNICODE_STRING  LinkName;
unsigned int MyStartingServiceId;
unsigned int StartingServiceId;
NTSTATUS
DriverDispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    );
NTSYSAPI
NTSTATUS
NTAPI  UnSetCallBackA();//用于替代UnSetCallBack的一个函数
NTSYSAPI
NTSTATUS
NTAPI  SendMessage(ULONG MessageIndex,PVOID Msg,size_t MsgSize,NTSTATUS *UserRetStatus);//消息发送函数,由消息发送方发起
#if DBG
PVOID  ServiceTableBase[]={(PVOID)CallBack,(PVOID)UnSetCallBack,(PVOID)SetCallBack,(PVOID)SendMessage};//我们自己构建的一张SSDT表
#else
PVOID  ServiceTableBase[]={(PVOID)CallBack,(PVOID)UnSetCallBackA,(PVOID)SetCallBack,(PVOID)SendMessage};
#endif
unsigned char ParamTableBase[]={0,0,4,sizeof(ULONG)+sizeof(PVOID)+sizeof(size_t)+sizeof(NTSTATUS *)};//用于描述我们自己SSDT表的一个参数表(SSPT表)
__declspec(dllimport)PMDL NTAPI IoCreateWriteMdlForAddress(PVOID InAddress,PVOID *OutAddress,size_t Size);//自己封装的一个函数,用于生成一个MDL
__declspec(dllimport)VOID NTAPI IoFreeMdlForAddress(PVOID Address,PMDL pMdl);//自己封装的一个函数,用于销毁一个MDL
__declspec(dllimport)NTSTATUS _stdcall AddServices(PVOID *ServiceTableBase,unsigned char *ParamTableBase,unsigned int *MyStartingServiceId,unsigned int NumberOfServices);
//自己封装的一个函数,用来向系统的SSDT表,添加新的函数,参数MyStartingServiceId返回的是新函数在SSDT中的索引,也就是我们自己构建的SSDT表中第一个函数的索引)
NTSYSAPI
NTSTATUS
NTAPI
ZwQueryInformationProcess (
    IN HANDLE           ProcessHandle,
    IN PROCESSINFOCLASS ProcessInformationClass,
    OUT PVOID           ProcessInformation,
    IN ULONG            ProcessInformationLength,
    OUT PULONG          ReturnLength OPTIONAL
);

NTSTATUS
NTAPI  UnSetCallBackA()
{
return 0;
}
NTSTATUS
DriverEntry(
  IN PDRIVER_OBJECT    DriverObject,
  IN PUNICODE_STRING    RegistryPath
  )
{
PDEVICE_OBJECT  DeviceObject;
PVOID  DeviceExtensionAddress;
NTSTATUS Status = 0;

Time.QuadPart = (100*DELAY_ONE_MICROSECOND);
KeInitializeEvent(&ListEvent,SynchronizationEvent,0);
DriversUnload(DriverObject,CallbackUnload);
InitializeListHead(&DataList);
InitializeListHead(&SendingDataList);
KeInitializeSpinLock(&Lock);
DEBUG;//这个代码在调试版本中为 _asm int 3 因此如果你编译的是调试版本的话,请不要在没有打开任何内核调试器,或者系统调试模式的状态下使用,否则会直接BOSD,如果编译的是发行版本
//则没有这个限制,因为在发行版本中 DEBUG 为空
RtlInitUnicodeString(&DeviceName,L"\\Device\\GetInt");
RtlInitUnicodeString(&LinkName,L"\\DosDevices\\GetInt");

   DriverObject->MajorFunction[IRP_MJ_CREATE]         = DriverDispatch;
   DriverObject->MajorFunction[IRP_MJ_CLOSE]          = DriverDispatch;
   DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDispatch; 
Status=MyIoCreateDevice(DriverObject,0,&DeviceExtensionAddress,&DeviceName,&LinkName,FILE_DEVICE_UNKNOWN,
0,FALSE,&DeviceObject,NULL);
if (NT_SUCCESS(Status))
{
Status = AddServices(ServiceTableBase,ParamTableBase,&MyStartingServiceId,4);//向SSDT添加服务函数
StartingServiceId=MyStartingServiceId;
}
if (NT_SUCCESS(Status))
{
SetFlag(DeviceObject->Flags, DO_BUFFERED_IO);
PsSetCreateProcessNotifyRoutine(Process,FALSE);
}else
{
IoDeleteDevice(DeviceObject);
IoDeleteSymbolicLink(&LinkName);
}
return Status;
}
#if DBG
VOID
CallbackUnload(
  IN PDRIVER_OBJECT    DriverObject
  )
{
}
#endif

NTSTATUS
NTAPI  SetCallBack(ULONG FunctionIndex)//主要保存应用发来的处理函数在KernelCallBackTable中的索引,这个函数必须由处理函数所在的线程调用
{
ULONG KernelCallBackTable;
ULONG PebAddr;
_try
{
PebAddr=GetCurrentProcessPEB();
if(PebAddr==NULL)
return STATUS_ACCESS_DENIED;
KernelCallBackTable=*(ULONG*)(PebAddr+0x2C);
if(KernelCallBackTable==0)
return STATUS_ACCESS_DENIED;
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}


if(FunctionIndex>500)//我这里设定,索引大于500时返回一个错误
return STATUS_ACCESS_DENIED;
if(FunctionIndex<1)
return STATUS_ACCESS_DENIED;
if((ProcessHandle!=NULL)|(ApiIndex!=NULL))
return STATUS_ACCESS_DENIED;
ProcessHandle=PsGetCurrentProcessId();//记录下调用进程的PID
ApiIndex=FunctionIndex;//记录下索引
return 0;
}


NTSTATUS
NTAPI  UnSetCallBack()
{
//用于处理在处理进程退出以后,还没有来得及处理,和正在处理,并且没有处理完成的请求的处理
PMSGDATA MsgData;
PLIST_ENTRY pList = NULL;
pList=ExInterlockedRemoveHeadList(&DataList,&Lock);//从等待处理的请求的链表中取出请求,如果链表为空,就停止取出等待处理的请求
while(pList!=NULL)
{
MsgData=CONTAINING_RECORD(pList,MSGDATA,DataList);
MsgData->Status = STATUS_ACCESS_DENIED;
KeSetEvent(&MsgData->Event,IO_NO_INCREMENT,FALSE);
KeDelayExecutionThread(KernelMode,FALSE,&Time);
KeClearEvent(&MsgData->Event);

ExFreePool(MsgData);
pList=ExInterlockedRemoveHeadList(&DataList,&Lock);
}
pList=ExInterlockedRemoveHeadList(&SendingDataList,&Lock);//从正在处理的请求的链表中取出请求,如果链表为空,就停止取出正在处理的请求

while(pList!=NULL)
{
MsgData=CONTAINING_RECORD(pList,MSGDATA,DataList);
MsgData->Status = STATUS_ACCESS_DENIED;
KeSetEvent(&MsgData->Event,IO_NO_INCREMENT,FALSE);
KeDelayExecutionThread(KernelMode,FALSE,&Time);
KeClearEvent(&MsgData->Event);

ExFreePool(MsgData);
pList=ExInterlockedRemoveHeadList(&SendingDataList,&Lock);
}
ApiIndex=NULL;
ProcessHandle=NULL;
return 0;
}



NTSTATUS
NTAPI  CallBack()//这个函数用于激发KeUserModeCallBack的调用,这个函数必须在处理函数所在线程中调用
{
ULONG KernelCallBackTable;
NTSTATUS *pStatus=NULL;
PMSGDATA MsgData;
PLIST_ENTRY pList = NULL;
ULONG PebAddr;
_try
{
PebAddr=GetCurrentProcessPEB();//取得PEB结构
if(PebAddr==NULL)
return STATUS_ACCESS_DENIED;
KernelCallBackTable=*(ULONG*)(PebAddr+0x2C);//从PEB中取得KernelCallBackTable结构
if(KernelCallBackTable==0)
return STATUS_ACCESS_DENIED;
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
if((ProcessHandle==NULL)|(ApiIndex==NULL))//检查是否有处理函数的索引
return STATUS_ACCESS_DENIED;
if(ProcessHandle!=PsGetCurrentProcessId())
return STATUS_ACCESS_DENIED;
pList=ExInterlockedRemoveHeadList(&DataList,&Lock);//取出需要处理的请求
if(pList==NULL)
return -1;//如果没有请求,就返回
MsgData=CONTAINING_RECORD(pList,MSGDATA,DataList);//取出待发送的请求
MsgData->IsSend=1;//设置标志为正在处理
ExInterlockedInsertTailList(&SendingDataList,&MsgData->DataList,&Lock);//将请求插入正在处理链表
pStatus=MsgData->UserRetNtstatus;//指向一个接收函数状态的缓冲区
MsgData->Status = SendData(MsgData);//调用KeUserModeCallBack回调处理进程中的处理函数
*pStatus=MsgData->Status;//接收用户返回的状态
ExInterlockedRemoveHeadList(&SendingDataList,&Lock);//将请求从正在处理链表中删除
MsgData->IsSend=0;//恢复标志
KeSetEvent(&MsgData->Event,IO_NO_INCREMENT,FALSE);//通知请求发起程序,请求的处理已经完成
KeDelayExecutionThread(KernelMode,FALSE,&Time);//程序暂停100纳秒
KeClearEvent(&MsgData->Event);//清除事件,并且回收资源
ExFreePool(MsgData);
return 0;
}




NTSTATUS
NTAPI  SendMessage(ULONG MessageIndex,PVOID Msg,size_t MsgSize,NTSTATUS *UserRetStatus)
{
PVOID pDataAddress = NULL;
NTSTATUS UserStatus = -1;
PMDL pMdl = NULL;
PMSGDATA MsgData=NULL;
KEVENT  Event;
NTSTATUS Status = 0;
if(MsgSize==0)
return STATUS_INVALID_PARAMETER;
if((ProcessHandle==NULL)|(ApiIndex==NULL))
return STATUS_ACCESS_DENIED;

_try
{
ProbeForWrite(UserRetStatus,sizeof(NTSTATUS),sizeof(NTSTATUS));//测试返回缓冲区是否可写
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
return STATUS_INVALID_PARAMETER;
}
_try
{
ProbeForRead(Msg,MsgSize,sizeof(ULONG));//测试需要发送的数据缓冲区是否可读

}
_except(EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
return STATUS_INVALID_PARAMETER;
}
*UserRetStatus=(NTSTATUS)-1;
MsgData=(PMSGDATA)ExAllocatePool(NonPagedPool,sizeof(MSGDATA));分配用于记录要发送的请求的数据结构
if(MsgData==NULL)
return STATUS_INSUFFICIENT_RESOURCES;
pMdl=IoCreateWriteMdlForAddress(Msg,&pDataAddress,MsgSize);//为请求建立一个MDL,这个MDL 地址指向了请求缓冲区,并且这个缓冲区已经可以用在内核中了
if(pMdl==NULL)//MDL建立失败,这个时候必须返回失败
{
ExFreePool(MsgData);
return STATUS_INSUFFICIENT_RESOURCES;
}
MsgData->pData=pDataAddress;
MsgData->DataSize=MsgSize;

MsgData->MessageIndex=MessageIndex;
KeInitializeEvent(&MsgData->Event,SynchronizationEvent,0);//初始化一个事件
MsgData->UserRetNtstatus=&UserStatus;
MsgData->IsSend = 0;
ExInterlockedInsertTailList(&DataList,&MsgData->DataList,&Lock);//将请求插入等待处理的请求的链表中


KeWaitForSingleObject(&MsgData->Event,Executive,KernelMode,0,NULL);//等待请求的处理
IoFreeMdlForAddress(pDataAddress,pMdl);//回收资源
*UserRetStatus=UserStatus;
return 0;
}

VOID Process(
    IN HANDLE  ParentId,
    IN HANDLE  ProcessId,
    IN BOOLEAN  Create

    )
{
if(Create==FALSE)
if(ProcessHandle==ProcessId)
UnSetCallBack();//在处理进程退出前没有处理的请求,在这里给予处理
}
ULONG GetCurrentProcessPEB(VOID)
{
  PROCESS_BASIC_INFORMATION BasicInfo={0};
  NTSTATUS status;
  ULONG ReturenLength;
  status=ZwQueryInformationProcess(NtCurrentProcess(),
    ProcessBasicInformation,
    &BasicInfo,
    sizeof(PROCESS_BASIC_INFORMATION),
    &ReturenLength);
  if (NT_SUCCESS(status))
  {
    return (ULONG)BasicInfo.PebBaseAddress;
  }
  return 0;
}



NTSTATUS SendData(PMSGDATA MsgData)//获取请求,并且交予请求处理函数给予处理
{
  ULONG ResultLentgh;
  PVOID ResultBuffer;
PSENDTOUSERMSG SendToUserData = NULL;//指向用户缓冲区的数据结构指针
PVOID pBuf = NULL;
NTSTATUS Status = 0;
ULONG KernelCallBackTable;
ULONG PebAddr;
size_t DataSize= MsgData->DataSize+sizeof(SENDTOUSERMSG)+1;//计算要发送到处理函数的数据大小
PebAddr=GetCurrentProcessPEB();
  __try
  {
KernelCallBackTable=*(ULONG*)(PebAddr+0x2C);//根据PEB取得KernelCallBackTable的地址,如果KernelCallBackTable为空,那么我们回调过去的话,处理进程肯定崩溃,所以必须检查是否为0
if(KernelCallBackTable==0)
return 0;
Status=ZwAllocateVirtualMemory(NtCurrentProcess(),&pBuf,0,&DataSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);//由于应用程序无法直接处理驱动的数据,所以我们需要应用程序的缓冲区来接收数据
//
if (!NT_SUCCESS(Status))
return Status;
if(DataSize<MsgData->DataSize+sizeof(SENDTOUSERMSG)+1)//检查申请到的缓冲区大小,如果小于要发送的数据,就直接返回资源不足
{
ZwFreeVirtualMemory(NtCurrentProcess(),&pBuf,&DataSize,MEM_RELEASE);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(pBuf,DataSize);//清空缓冲区
SendToUserData=(PSENDTOUSERMSG)pBuf;//填写缓冲区指针
RtlCopyMemory(&SendToUserData->Dataof,MsgData->pData,MsgData->DataSize);//填充要发送的请求,请求保存在MsgData->pData的一个MDL映射到内存的一个缓冲区中,并且在内核是可以读取的
SendToUserData->MessageIndex=MsgData->MessageIndex;//填写我们自定义的请求号,用于我们自己函数的请求的区分
SendToUserData->DataSize=MsgData->DataSize;//填写请求的大小
SendToUserData->Size=sizeof(SENDTOUSERMSG)+MsgData->DataSize;//填写实际发送到处理函数的数据大小
Status=KeUserModeCallback(ApiIndex,pBuf,DataSize,&ResultBuffer,&ResultLentgh);//回调我们的处理函数,ApiIndex是我在处理进程中预先设定好的,他的值由SetCallBack函数传入
//然后系统会根据ApiIndex,在KernelCallBackTable中找到并且回调我们的处理函数
ZwFreeVirtualMemory(NtCurrentProcess(),&pBuf,&DataSize,MEM_RELEASE);
  }
  __except(1)
  {
    DbgPrint("Unknown Error occured.\n");
    return GetExceptionCode();
  }
return Status;
}


NTSTATUS
DriverDispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
{

    PIO_STACK_LOCATION  irpStack;
    PVOID               ioBuffer;
    ULONG               inputBufferLength;
    ULONG               outputBufferLength;
    NTSTATUS            ntStatus;

    Irp->IoStatus.Status      = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    irpStack = IoGetCurrentIrpStackLocation (Irp);

    switch (irpStack->MajorFunction)
    {
    case IRP_MJ_DEVICE_CONTROL:
  trace(("EXTNDDRV.SYS: IRP_MJ_CLOSE\n"));
  switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
  {
    case IOCTL_EXTNDDRV_GET_STARTING_SERVICEID:

    trace(("EXTNDDRV.SYS: IOCTL_EXTNDDRV_GET_STARTING_SERVICEID\n"));
    outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    if (outputBufferLength<sizeof(StartingServiceId)) {
      Irp->IoStatus.Status      = STATUS_INSUFFICIENT_RESOURCES;
    } else {
      ioBuffer     = (PULONG)Irp->AssociatedIrp.SystemBuffer;
      memcpy(ioBuffer, &StartingServiceId, sizeof(StartingServiceId));
      Irp->IoStatus.Information = sizeof(StartingServiceId);
    }
    break;
  }
  break;
    }
    ntStatus = Irp->IoStatus.Status;

    IoCompleteRequest (Irp,
           IO_NO_INCREMENT
           );

    return ntStatus;
}
/////////////////////////////////////////////////////////////////////////////////////////
应用程序代码


我主要讲解,如何在KernelCallBackTable中添加我们自定义的函数,其余部分请看我发上来的代码

int main(int argc, char* argv[])
{
  ULONG   *KernelCallbackTable = 0;
  if(!SetStartingServiceId())
    return 0;
  ::LoadLibrary("User32.dll");
  _asm mov         eax,fs:[18h] //首先,我们通过fs寄存器来取得我们的TEB
_asm  mov eax,dword ptr ds:[eax+30h] //TEB偏移0x30处即PEB,放到eax中
_asm mov eax,dword ptr ds:[eax+2Ch]//TEB偏移0x2Ch处即KernelCallbackTable,放到eax中
_asm mov KernelCallbackTable,eax
ULONG u = 0;
  if(KernelCallbackTable!=NULL)
while(KernelCallbackTable[u]!=0)//复制已经存在的处理函数指针,把他们复制进我们构建的一张新表中
{
  CallBack[u]=KernelCallbackTable[u];
  u++;
}
CallBack[u]=(ULONG)GetMsg;//在我们的新表中添加我们自己的处理函数的指针
_asm mov         eax,fs:[18h]//我们通过fs寄存器来取得我们的TEB
_asm  mov eax,dword ptr ds:[eax+30h]//TEB偏移0x30处即PEB,放到eax中
_asm lea ecx,CallBack//取得我们新表的地址
_asm mov [eax+2Ch],ecx//替换系统中原来的KernelCallbackTable的地址,由于原KernelCallbackTable表中的函数地址已经被复制到我们的新表中了,所以不怕来自GUI的调用
if(SetCallBack(u)==0)//这个时候的u 就是KeUserModeCallBack 回调时需要的索引号,它被记录在驱动的ApiIndex变量中
{
  printf("设置处理函数成功!正在等待处理请求!\n");
}
NTSTATUS Status = Rin0CallBack();

while(Exit==0)
{
    _try
{
  Sleep(10);
  Status = Rin0CallBack();
    }
  _except(EXCEPTION_EXECUTE_HANDLER)
  {
    _asm mov eax,0
    _asm int 2bh
    ::ExitProcess(GetExceptionCode());
    return GetExceptionCode();
    }
}

  printf("程序退出!清空所有处理请求!\n");
  return 0;
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
由于时间的关系,我只贴到这里,其余的请看我的代码 另外提醒一下,DEBUG; 这个代码在调试版本中为 _asm int 3 因此如果你编译的是调试版本的话,请不要在没有打开任何内核调试器,或者系统调试模式的状态下使用,否则会直接BOSD,如果编译的是发行版本
//则没有这个限制,因为在发行版本中 DEBUG 为空,请大家注意。



我的代码在WIN XP SP2中编译,并且运行成功 如果程序又什么错误,或者是稳定性上的问题的话,请同学们给予指正 

上传的附件 SendMessage.rar