Shadow Hook实现网络安全打印监控

如何实现对打印的监控,微软提出的一种解决方案就是时刻检测放到打印队列中的打印任务,发现有任务出现,就从中筛选出来提供给调用者。对此功能的实现,微软的确公开了一套完整的代码,并且能够实现我们基本想要的功能,但是在实现功能之余,我又进行了更深一层的研究和测试,通过Hook win32k.sys内的打印相关的4个函数就完美地实现了打印监控功能。

我们先分析一下当系统完成一次打印任务需要调用的几个核心函数(对我们的程序有重要作用的),如表1所示。

 

Gdi32.dll

Win32k.sys

开始打印任务

StartDoc

NtGdiStartDoc

打印每一页

StartPage

NtGdiStartPage

结束每一页

EndPage

NtGdiEndPage

结束打印任务

EndDoc

NtGdiEndDoc

1

其中的StartDocEndDoc是来控制每次打印任务的开始和结束;StartPageEndPage控制每一页的打印,我的理解是需要打印的文档有多少页,StartPageEndPage函数对就要调用多少次,实际上对函数调用测试也验证了这一点。

既然已经清楚了函数的功能和对应关系,想必如何实现应该有了点思路吧?对了,就是在这几个函数上动手脚——Hook!到底是Ring3下的Hook还是Ring0下的Hook就应该是仁者见仁了,本文通过在Ring0下的Hook来实现打印监控的功能!

下面是我们目前要解决的问题,我将逐个击破。

1)  如何定位到KeServiceDescriptorTableShadow

2)在不同的操作系统版本下,Win32k.sys内的函数索引不同,我们该如何解决?

2)  Win32k.sys的特殊性,并不是在每个进程都有映射,该如何进行Shadow Hook

4)如何捕获到打印相关的信息?

如何定位到KeServiceDescriptorTableShadow,黑防以前的文章中都有描述,这里我只简单的说明一下。定位的思路是在KeAddSystemServiceTable函数中具体的信息,如何定位网上已给出了一套完整的代码,自己在Windows多个版本中测试时发现可以兼容!

 

nt!KeAddSystemServiceTable:

805a11d4 8bffmov     edi,edi

805a11d6 55push    ebp

805a11d7 8becmov     ebp,esp

805a11d9 837d1803cmp     dword ptr [ebp+18h],3

805a11dd 7760ja      nt!KeAddSystemServiceTable+0x6b (805a123f)

805a11df 8b4518mov     eax,dword ptr [ebp+18h]

805a11e2 c1e004shl     eax,4

805a11e5 83b800c7558000cmpdword ptr nt!KeServiceDescriptorTable (8055c700)[eax],0

805a11ec 7551jnent!KeAddSystemServiceTable+0x6b (805a123f)

805a11ee 8d88c0c65580leaecx,nt!KeServiceDescriptorTableShadow (8055c6c0)[eax]

805a11f4 833900cmpdword ptr [ecx],0

805a11f7 7546jnent!KeAddSystemServiceTable+0x6b (805a123f)

 

具体实现定位的代码如下:

 

ULONG GetAddressOfShadowTable()

//得到SSDT Shadow的函数地址

{

unsigned int i;

unsigned char *p;

unsigned int dwordatbyte;

p = (unsigned char*) KeAddSystemServiceTable;//该函数没有文档化,使用时需导出

for(i = 0; i < 4096; i++, p++)

{

__try

{

dwordatbyte = *(unsigned int*)p;

}

__except(EXCEPTION_EXECUTE_HANDLER)

{

return 0;

}

 

if(MmIsAddressValid((PVOID)dwordatbyte))

{

if(memcmp((PVOID)dwordatbyte, &KeServiceDescriptorTable, 16) == 0)

{

if((PVOID)dwordatbyte == &KeServiceDescriptorTable)

{

continue;

}

return dwordatbyte;

}

}//KeAddSystemServiceTable中搜索匹配得到KeServiceDescriptorTableShadow

}

return 0;

}

 

     既然定位KeServiceDescriptorTableShadow的代码能够在Windows的多个版本下准确的定位,我们的Hook也应该尽量做到最大的兼容性。不过有一个问题,就是在Shadow中的每个函数的具体索引并不能像SSDT那样可以轻松得到,我们可以通过不同的操作系统版本来给出具体的数值,毕竟只有4个函数,而我们做的只是判断一下当前运行的版本,然后填入正确的索引值即可!同时,这里似乎还隐含着一个问题,我们待会儿再说。先看看如何判断当前版本。

 

VOID   GetFunIndex()

{

ULONG majorVersion, minorVersion;

PsGetVersion( &majorVersion, &minorVersion, NULL, NULL );//这个函数是核心!

if ( majorVersion == 5 && minorVersion == 2 )

{

DbgPrint("Running on Windows 2003");

//进行数据的修正

}

else if ( majorVersion == 5 && minorVersion == 1 )

{

DbgPrint("Running on Windows XP");

//进行数据的修正

}

else if ( majorVersion == 5 && minorVersion == 0 )

{

DbgPrint("Running on Windows 2000");

//进行数据的修正

}

}

 

通过该函数就可以轻松地实现我们对不同版本的操作系统兼容性的要求,具体的数值接下来会用表格的形式呈现出来!

到了这里,我们应该就可以进行Hook了,但是如何确保win32k.sys已经映射到了我们的进程空间内呢?否则如果我们直接操作,后果就是BSOD啊!其实这个很简单,因为我们直接在DriverEntry函数中实现了这一系列的功能,也就是当前运行的进程空间处于SYSTEM进程内,而在SYSTEM进程内没有映射win32k.sys,所以我们对ShadowHook导致蓝屏也显而易见啦!所以,在我们Hook之前先Attache到一个确信映射了该文件的进程内就是必须的了,而Csrss.sys就是我们的首选!现在,我们的问题明确了,就是找到csrss.exe进程后Attach!我使用了遍历的方式来实现,不过只要能实现就OK啦!

 

ULONG GetCsrssProcessId()//如果找到,返回PID没有找到,返回

{

NTSTATUS m_status=STATUS_SUCCESS;

HANDLE m_process_id=0;//返回的ID

char m_name[16]={0};//进程的名字

ULONG m_index=0;

PEPROCESS m_eprocess;//进程的对象

for (m_index=0;m_index<65535;m_index+=4)

{

m_status=PsLookupProcessByProcessId((HANDLE)m_index,&m_eprocess);

if(NT_SUCCESS(m_status))

{

//检测一下当前得到的是不是活动进程

strncpy(m_name,(char *)((ULONG)m_eprocess+m_name_offset),16);

//m_name_offset就是上面提到的隐含问题,不过如果是通过另外的方式定位的话,这个问题就不存在了

if (_stricmp(m_name,"csrss.exe")==0)

{

//表明已经得到了该进程的PID

DbgPrint("获得到的PID:%d\n",m_index);

return m_index;

}

}

}

return 0;

}

 

如果Hook掉提到的那4个函数,方法和SSDTHook完全相同,这儿我就简单的贴出代码吧。

 

VOID TryToHookFun()//这儿执行的前提是已经Attach到了进程

{

PEPROCESS m_eprocess;

KIRQL m_irql;

NTSTATUS m_status=STATUS_SUCCESS;

ULONG m_process_id=0;

KAPC_STATE  m_apc_state;

m_process_id=GetCsrssProcessId();

m_status=PsLookupProcessByProcessId((HANDLE)m_process_id,&m_eprocess);

if(NT_SUCCESS(m_status))

{

m_irql=KeRaiseIrqlToDpcLevel();

//下面开始附加进程

KeStackAttachProcess(m_eprocess,&m_apc_state);//附加到csrss.exej进程

CloseProtected();//关闭保护---这个代码黒防到处都是!

……

//这里实现的就是对Shadow函数的具体替换

RecoverProtected();

KeUnstackDetachProcess(&m_apc_state);//取消进程的附加

KeLowerIrql(m_irql);

}

}

 

2是函数和进程名在不同版本下的具体数值或偏移量。

 

Windows 2000

Windows XP

Windows 2003

NtGdiStartDoc索引

280

290

289

NtGdiStartPage索引

281

291

290

NtGdiEndPage索引

126

131

131

NtGdiEndDoc索引

125

130

130

ImageFileName偏移

0x1fc

0x174

0x164

2

至此,我们的前3个问题终于完美解决了,开始我们的打印监控了!如上所说,这4个函数的配合使用就可以轻松获得打印信息!打印的开始时间、文档名称等信息在NtGdiStartDoc中获得;打印的页数从NtGdiStartPage的调用次数中获得;结束时间在NtGdiEndDoc中获得!根据这4个函数的参数来看,都有一个HDC 参数,是不是可以根据这个HDC来捕获到打印内容呢?这个我还没有去测试,至少当前可以捕获到的信息已经满足一定的要求了,有兴趣的可以试试!

对于这4个函数如何配合使用,应该说是仁者见仁,智者见智,本文我只是简单实现了打印监控,然后按照一定格式写入了文件;如果读取,就在Ring3层写个程序把文件解析显示即可,同样达到了内核和用户层的交互,毕竟打印监控没有实时性的要求!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Hook进程创建函数是一种监控和拦截进程的技术。当一个进程被创建时,操作系统会调用相应的进程创建函数来执行一系列的操作,例如分配内存、初始化资源等。通过hook进程创建函数,我们可以在进程创建的过程中插入自定义的代码,从而实现进程监控和拦截。 通过hook进程创建函数,我们可以实现以下功能: 1. 监控进程创建:通过hook进程创建函数,我们可以记录下每个进程的创建情况,包括进程的名称、进程ID等信息。这对于进程管理、调试和安全审计等方面都非常有用。 2. 拦截进程创建:当我们希望阻止某个进程的创建时,可以通过hook进程创建函数实现进程的拦截。例如,某些恶意程序会通过创建新进程的方式进行传播,我们可以通过hook进程创建函数拦截这些进程的创建,从而保护系统的安全。 3. 修改进程创建参数:通过hook进程创建函数,我们可以修改进程的创建参数,例如修改程序的启动参数、运行环境等。这对于进程的定制化和优化非常有用。 4. 绕过进程创建限制:有些情况下,操作系统会对进程的创建做一些限制,例如限制某个程序创建的进程数量、限制进程的权限等。通过hook进程创建函数,我们可以绕过这些限制,实现一些我们需要的功能。 总之,hook进程创建函数是一种非常有用的技术,可以实现进程监控和拦截。通过插入自定义代码来实现各种功能,从而对进程进行管理和控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值