火绒内核注入dll方式win7-win10通用x64下不触发PG

本文详细介绍了在Win7到Win10 x64系统下,如何通过火绒技术实现不触发PG的内核DLL注入。主要内容包括:判断加载dll、打开ProcessHandle、切换进程地址空间、获取系统关键函数地址、分配内存、写入shellcode,以及在64位系统下的注意事项和调用约定。文中提到的方法可能存在多加载问题,建议优化恢复原函数逻辑。

帖子原地址为:http://www.mengwuji.net/thread-6765-1-1.html

看起来相当简单.结果踩进去以后全都是坑

32位下完工代码地址:http://git.oschina.net/ockdieso/nahequdongzhurudll

只是对哪个帖子做一些补充

NTSTATUS Register_Load_Image()
{
	NTSTATUS status = PsSetLoadImageNotifyRoutine(Load_Image);
	return status;
}

注册回调

然后先看下原文的步骤

1.判断当前LoadImageNotify加载的dll名字是System32(SysWOW64)\\ntdll.dll
2.ObOpenObjectByPointer打开一个ProcessHandle
3.KeStackAttachProcess切到被注入的进程的地址空间去,
4.遍历导出表取ntdll!ZwTestAlert的地址(win10取ntdll!LdrGetProcedureAddressForCaller)

5.取ntdll!NtProtectVirtualMemory的地址

前四步都是一样的.至于这个NtProtectVirtualMemory的函数地址.我没有取

因为暂时不考虑还原.这个Test函数实际上我都不知道他什么时候才去调用

按照我的想法....这个函数估计一辈子也就跑一次吧?

不过为了实现功能.这些东西后续补上也不是问题

6.KeUnstackDetachProcess切回来
6.在ntdll附近分配注入shellcode需要的内存7.调用ntoskrnl!ZwReadVirtualMemory获取ntdll!ZwTestAlert处头5字节的数据保存起来
8.配置好shellcode及shellcode的需要的数据(如注入的dll路径、ntdll!NtProtectVirtualMemory地址等)

在32位下我没有管该内存地址分配的问题.而且测试并没有问题.只要不分配到8以上应该就不会有事

64位下我想应该也不需要去管.因为实现方式可能不同

char Shell_Code[] =
		"\x55" //push ebp 保存栈环境
		"\x8B\xEC" //mov ebp,esp
		//"\x50"//push eax
		"\x52"//push edx
		//第一个地址.偏移+5
		"\x68\x00\x00\x00\x00" //push 最后一个地址.handle的地址
		//第二个地址.偏移+9
		"\x68\x00\x00\x00\x00" //push 倒数第二个参数.此处为UNICODE的str地址
		"\x6A\x00" //push 0
		"\x6A\x00" //push 0
		//第三个地址.偏移+19
		"\xFF\x15\x00\x00\x00\x00" //FF 15 call 此处直接填变量地址.变量中储存函数地址.
		"\xB8\x74\x01\x00\x00" //mov eax,174h 存储SSDT表索引
		"\xBA\x00\x03\xFE\x7F" //mov edx,7FFE0300h 直接中断进内核层
		"\xFF\x12" //call dword ptr [edx]  call edx
		"\x33\xC0" //eax eax清零
		"\x5A"//pop edx
		//"\x58"//pop eax
		"\x5D" //pop ebp 还原堆栈
		"\xC3"; //C3 retrun

shell是从IDA里面提取的.然后自己手动加了一部分.xor eax清零了.所以eax就不进行push了

总得来说.进入shell code以后.做了一些环境保存.然后直接push参数call加载DLL函数.完毕之后我又吧Test函数的ASM代码抄了下来.在shell中再次进行一次调用.保证程序不会出什么问题.之后C3

不过其实问题也有啊...如果这个函数被多次调用...那么就有可能被载入很多次.所以还是尽量去还原Test函数比较好.

需要注意的是.x64下面的函数调用有相当大的区别.不会再像32下面那样push push push.具体可以百度一下

BBGetModuleExport是Hub上抄来的函数.用于实现R3下面的getaddr函数

用KeStackAttachProcess艹进去以后.获取了LdrLoadDll和ZwTestAlert

关于KPROCESSOR_MODE

这个东西很坑...我最后的办法是起条线程跑.这样可以绕过mode检测.Zw和Nt在这里有区别.详情可以百度一下

R3下面.Zw和Nt是同一个地址.

由于我的shell没有恢复.故此被hunter抓了出来

可以看到test函数被hook成了E9 XX XX XX XX

其实刚开始说让我用E9我是拒绝的.我想用FF15 不过想想可能出点啥问题.还是先用E9撑一下

OK.上面的步骤完成后就是SSDT函数的问题了

CG_NtWriteVirtualMemory 或者protect这种函数是不被导出的

想调用的话.必须拿到SSDT上的函数地址

这个地址想拿到的话,方法还是很多的.其中之一就是抓eax.因为在中断进内核的时候.eax会保存SSDT上面的索引.拿到索引以后.地址也就出来了

不过这种办法太麻烦.对我这样只会复制粘贴的人来说.当然是靠抄

是的,没错.windows常用系统就那么几个.区分好x64和x32.索引直接硬编码不谢!

 

#define SYSCALL_INDEX(ServiceFunction) (*(PULONG)((PUCHAR)ServiceFunction + 1))

//#define SYSCALL_FUNCTION(ServiceFunction)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(ServiceFunction)]

#define SYSCALL_FUNCTION(ServiceFunction) (ULONG)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase + SYSCALL_INDEX(ServiceFunction) * 4

#define TABLE_FUNCTION(Service_Function_Index) (ULONG)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase + Service_Function_Index * 4

这几个宏定义需要说一下

这是以前抄人家SSDT的东西.用索引的话有点问题.所以修改了一下

(ULONG)KeServiceDescriptorTable->ntoskrnl.ServiceTableBase + Service_Function_Index * 4

取地址+索引*4 SSDT的标准算法.值得注意的是取出来后的地址再取一遍值.听不懂的话建议回去看看内核入门书吧

该有的都有了.就欠东风

接下来的事情就很简单了.环境都搭好了.分配内存.装填shell.写入shell.然后修改jump.这些东西反而相当好写

这里有个小坑.我踩进去以后很懵逼.公司大佬看完后也很懵逼.最后debug给调出来了

typedef NTSTATUS(*CG_NtProtectVirtualMemory)(

IN HANDLE ProcessHandle,

IN OUT PVOID *BaseAddress,

IN OUT PULONG ProtectSize,

IN ULONG NewProtect,

OUT PULONG OldProtect);

这是NtProtectVirtualMemory的函数原型.看起来好像非常人畜无害的样子

过去我最后一步jump是这样写的

void *Test_Test = ZwTestAlert_Func;

status = NtProtectVirtualMemory(ProcessHandle, &Test_Test, &Protect_Size,

PAGE_EXECUTE_READWRITE, &Old_Protect);

status = NtWriteVirtualMemory(ProcessHandle, Test_Test, Test_Code, 5, NULL);

status = NtProtectVirtualMemory(ProcessHandle, &Test_Test, &Protect_Size,

Old_Protect, &Temp_Protect);

发现问题了么?

没有?

你再仔细瞅瞅?

还是没有?

IN OUT PVOID *BaseAddress!!!

IN OUT ???

OUT???

还没懂的话...麻烦你们都进坑里爽一爽吧...

 到这里32位下就完成了

x64区别不大.但是也有坑要踩.SSDT表要去自己手动翻出来

然后x64汇编会有区别.比如前8个E开头变R开头.比如RAX什么的

EBP升级为RBP.同时被当做通用寄存器玩

然后函数调用的时候不是全部都用push+call的方式.用了几个寄存器

具体可以参考下x64ASM

教程就到这里了.写的比较乱和杂.过多的原理性东西我也不太想讲.因为太麻烦了

代码我丢到了OSC.VS13+WDK8.1可过编译

不过过编译没问题.想跑起来要看运气.为了实现功能.里面有相当多比较操蛋的代码.比如strcpy什么的.然后返回值也没有正儿八经的判断.

我写程序的时候有一个毛病.在调试期间.所有函数视为返回成功的状态进行debug...

不过可以基于这份代码进行修改稳定化.然后移植64,或者VS15+WDK10也是很简单的

最后上个完工图

JAVA/C++讨论群:546110133(大多时候在吹B.瞎聊天)

转载于:https://my.oschina.net/u/2325943/blog/1239933

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值