内核态下基于动态感染技术的应用程序执行保护(五 动态感染)

2023-10-17 14:10

本文主要是介绍内核态下基于动态感染技术的应用程序执行保护(五 动态感染),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

分类:技术汇编

源代码:http://download.csdn.net/detail/hitetoshi/3633188

        绝影做尘,铁甲四方逐鹿而争。风起云涌,八面九锡更兼策马奔。南面独傲,铜雀二乔,欲休却报诏到。假节钺,赞拜不名,剑履入殿谁能?青梅煮酒,本初不数,惊起雷霆咋休。玺绶之册,即阼亦重,却累身三秋。观之沧海,星汉一粟,不道身成五彩。隔江叹:景升之子,豚犬之流。

        扯远了,赶紧返回。今天这章是这篇系列文章的最后一章。

        前面我们的驱动程序已经可以监视进程的创建,下面我们希望做的事情有两点:一、判断进程是不是notepad.exe。二、如果是,我们向其进程注入一段代码并先于它原有的代码执行。所以,这里有三个重要的问题:一、从进程句柄获取进程名;二、向进程分配内存写入自定位的代码;三、修改进程原来的入口点,改为我们代码的入口点,在我们的代码执行完毕后,还得跳转到原来的入口点去。这整个过程本质上与文件感染没多少不同。

        如何从进程句柄获取到进程名呢?答案是EPROCESS,ObReferenceObjectByHandle可以让我们通过进程句柄获取它的EPROCESS。微软在WDK中对EPROCESS的说明非常简短:The EPROCESS structure is an opaque structure that serves as the process object for a process.EPROCESS结构中保存有进程名,但不幸的是EPROCESS结构的定义随操作系统的不同而不同,这也许也是为什么微软对它的描述非常少,他也不推荐你使用EPROCESS结构。

        在KmdKit中有个SharedEvent – ProcessMon例子,其中演示了通过EPROCESS获取进程名,它的做法是针对不同的系统定义不同的EPROCESS结构,在使用时先获取当前系统。

        在《Windows内核安全编程》中提到了另外一种方法:当我们的内核模块DriverEntry被调用时,我们的内核正处于System进程中,我们可以使用PsGetCurrentProcess获取到此时的EPROCESS,在其中暴力搜索“System”,如果搜索出来,我们就可以确定本系统中进程名相对于EPROCESS首地址的偏移,以后就可以用这个偏移加别的进程的EPROCESS首地址来获取别的进程的名了。

        后者显然要简洁得多,所以本文也采用了后者的办法。在DriverEntry中增加:

        invoke  GetNameOffset

        mov     g_uNameOffset,eax

        invoke  DbgPrint,$CTA0("Driver entry, name offset:%08X"),g_uNameOffset

GetNameOffset   proc    uses    ebx

    local   pProcess

    local   len

    local   dwOffset

   

    and     dwOffset,0

    invoke  PsGetCurrentProcess

    .if eax

        mov     pProcess,eax

        invoke  strlen,$CTA0("System")

        mov     len,eax

        xor     ebx,ebx

        .while  ebx<1024*3*4

            mov     eax,pProcess

            add     eax,ebx

            invoke  _strnicmp,$CTA0("System"),eax,len

            .if !eax

                mov     dwOffset,ebx

                .break

            .endif

            inc     ebx

        .endw

    .endif

   

    mov     eax,dwOffset

    ret

GetNameOffset   endp

这个问题解决了,我们先把HookProc.asm中Hook_NtCreateThread代码贴出来:

include Append.asm

   

Hook_NtCreateThread proc    ThreadHandle:PHANDLE,DesiredAccess:DWORD,ObjectAttributes:POBJECT_ATTRIBUTES,ProcessHandle:HANDLE,ClientId:PCLIENT_ID,ThreadContext:PCONTEXT,InitialTeb:PVOID,CreateSuspended:DWORD

    local   pProcess:PVOID

    local   ulEntryPoint:ULONG

    local   dwAllocationSize:DWORD

    local   pBaseAddress:PVOID

    local   pProcessName

    local   dwMemorySize:DWORD

    local   pAppendStart:PVOID

    local   pOldEntry:PVOID

    local   pAppendEntry:PVOID

    pushad

    pushfd

    .if ThreadContext&&CreateSuspended&&ProcessHandle&&ProcessHandle!=-1

        invoke  ObReferenceObjectByHandle,ProcessHandle,PROCESS_ALL_ACCESS,NULL,UserMode,addr pProcess,NULL

        .if eax==STATUS_SUCCESS

            .if g_uNameOffset

                mov     eax,pProcess

                add     eax,g_uNameOffset

                mov     pProcessName,eax

                invoke  DbgPrint,$CTA0("New process:%s"),pProcessName

               

                invoke  _stricmp,pProcessName,$CTA0("notepad.exe")

                .if !eax

                    mov     dwMemorySize,APPEND_CODE_LENGTH

                    mov     pAppendStart,offset APPEND_CODE_START

                    mov     pOldEntry,offset _ulOldEntry

                    mov     pAppendEntry,offset _AppendCodeEntry

                    mov     edi,ThreadContext

                    assume  edi:ptr CONTEXT

                    M2M     ulEntryPoint,[edi].regEax       ;保存入口点

                    .if ulEntryPoint

                        invoke  KeDetachProcess

                        invoke  KeAttachProcess,pProcess

                        ;分配内存

                        M2M     dwAllocationSize,dwMemorySize

                        mov     pBaseAddress,NULL

                        invoke  ZwAllocateVirtualMemory,NtCurrentProcess,addr pBaseAddress,0,addr dwAllocationSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE

                        .if eax==STATUS_SUCCESS

                            ;写入代码

                            invoke  memcpy,pBaseAddress,pAppendStart,dwMemorySize

                            ;填写Jmp地址

                            mov     eax,pOldEntry

                            sub     eax,pAppendStart

                            add     eax,pBaseAddress

                            M2M     dword ptr [eax],ulEntryPoint

                            ;修正入口点

                            mov     eax,pAppendEntry

                            sub     eax,pAppendStart

                            add     eax,pBaseAddress

                            mov     ulEntryPoint,eax

                        .endif

                        invoke  KeDetachProcess

                        M2M     [edi].regEax,ulEntryPoint

                    .endif

                .endif

            .endif

            assume  edi:nothing

            invoke  ObDereferenceObject,pProcess

        .endif

    .endif

    popfd

    popad

   

    invoke    g_lpOldNtCreateThread,ThreadHandle,DesiredAccess,ObjectAttributes,ProcessHandle,ClientId,ThreadContext,InitialTeb,CreateSuspended

   

    ret

Hook_NtCreateThread endp

        Append.asm中是我们要感染的代码,等会来看。进程创建时主线程也会被创建。但在我们的Hook中,线程还没有开始运行。其中ThreadContext(这个结构跟用户态下用GetThreadContext获取到的差不多)的regEax就是主线程的起始地址,也就是我们常说的入口点。代码的思路非常简单,在目标进程中分配一块APPEND_CODE_LENGTH大小的内存(这个大小刚好够装下我们需要注入的代码);把我们程序中APPEND_CODE_START地址拷贝那么多数据过去(这其实就是把我们的感染代码拷贝过去;把进程原来的入口点保存到拷贝过去的那份代码的某个位置(具体那个位置看Append.asm就明白了),吧ThreadContext.regEax设置为我们注入代码的起始地址。

        这下来看Append.asm

APPEND_CODE_START   equ this byte

include proto.inc

include Append.inc

_ulOldEntry             dd                      ?

_hKernel32              dd                      ?

_hUser32                dd                      ?

_szUser32               db                      'User32.dll',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_lpGetProcAddress       APIGetProcAddress       ?

_lpGetModuleHandleA     APIGetModuleHandle      ?

_lpLoadLibraryA         APILoadLibrary          ?

;-------------------------------------------------------------------------------

_szGetProcAddress       db                      'GetProcAddress',0

_szGetModuleHandleA     db                      'GetModuleHandleA',0

_szLoadLibraryA         db                      'LoadLibraryA',0,0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_lpMessageBoxA          APIMessageBox           ?

;------------------------------------------------------------------------------

_szMessageBoxA          db                      'MessageBoxA',0,0

_lpTitle                db                      'DynamicHook',0

_lpMsg                  db                      'My append code!',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

include GetKernel.asm

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_AppendCodeEntry:

    call    @F

    @@:

    pop     ebx

    sub     ebx,offset @B

   

    .if dword ptr [esp]>7FFFFFFFh

        jmp     OLD_ENTRY

    .endif

    invoke  _GetKernelBase,[esp]

    .if !eax

        jmp     OLD_ENTRY

    .endif

    mov     [ebx+_hKernel32],eax

   

    invoke  _GetApi,[ebx+_hKernel32],addr [ebx+offset _szGetProcAddress]

    .if !eax

        jmp     OLD_ENTRY

    .endif

    mov     [ebx+_lpGetProcAddress],eax

   

    lea     esi,[ebx+offset _szGetModuleHandleA]

    lea     edi,[ebx+offset _lpGetModuleHandleA]

    .while  TRUE

        invoke  [ebx+_lpGetProcAddress],[ebx+_hKernel32],esi

        .if !eax

            jmp     OLD_ENTRY

        .endif

        mov     [edi],eax

        add     edi,4

        @@:

        lodsb

        or      al,al

        jnz     @B

        .break  .if ! byte ptr [esi]

    .endw

   

    invoke  [ebx+_lpGetModuleHandleA],addr [ebx+_szUser32]

    .if !eax

        invoke  [ebx+_lpLoadLibraryA],addr [ebx+_szUser32]

        .if !eax

            jmp     OLD_ENTRY

        .endif

    .endif

    mov     [ebx+_hUser32],eax

    lea     esi,[ebx+offset _szMessageBoxA]

    lea     edi,[ebx+offset _lpMessageBoxA]

    .while  TRUE

        invoke  [ebx+_lpGetProcAddress],[ebx+_hUser32],esi

        .if !eax

            jmp     OLD_ENTRY

        .endif

        mov     [edi],eax

        add     edi,4

        @@:

        lodsb

        or      al,al

        jnz     @B

        .break  .if ! byte ptr [esi]

    .endw

   

    invoke  [ebx+_lpMessageBoxA],NULL,addr [ebx+_lpMsg],addr [ebx+_lpTitle],0

OLD_ENTRY:

    jmp     [ebx+_ulOldEntry]

APPEND_CODE_END     equ this byte

APPEND_CODE_LENGTH  equ offset APPEND_CODE_END-offset APPEND_CODE_START

        这下大家明白APPEND_CODE_LENGTH、APPEND_CODE_START、_AppendCodeEntry、_ulOldEntry的含义了吧。在这里汇编语言的好处就显而易见了:可以精确获取指定代码段的长度(你若用高级语言的话,就得估摸着分配一块足够大的内存);可以使用相对于ShellCode来说稍微高级一点的语言写代码(ShellCode要想完成复杂一点的功能还相当麻烦呢)。

        这里要注意的是,首先对所有用户态API的调用我们都不能直接调用,一是内核的导入库中根本没有提供这些函数,二是我们访问全局变量不能直接来访问,因为随着进程不同,我们注入到目标进程的代码起始地址也是不同的(分配的pBaseAddress不同)。这需要我们手动加载需要的DLL,获取API函数地址,并且编写自定位代码。这些技术早几年前就已经科普,这里就不再科普了。

        GetKernel.asm中的_GetKernelBase用来获取kernel32.dll的基址。方法也有很多,这里直接用的《Windows环境下32位会变语言程序设计》中的代码。

        现在来试运行一下:

        哈哈!当我们启动notepad.exe时首先会弹出我们的对话框。如果你用OllyDBG调试一下notepad.exe,选“设置第一次暂停于:主模块入口点”,你会发现,当OllyDBG中断时,我们的代码已经执行过了。notepad.exe根本就没有机会知道我们的代码已经影响了它。

        但是!任何事情就怕但是!notepad.exe在后面还有机会来扫描内存…….如果我们在

        OLD_ENTRY:

        jmp     [ebx+_ulOldEntry]

        处用下面的办法呢?(伪代码)

        push    8000

        push    APPEND_CODE_LENGTH

        push    offset [ebx+APPEND_CODE_START]

        push    dword ptr [ebx+_ulOldEntry]

        jmp     [ebx+_lpVirtualFree]

        这种动态感染的办法后面还有后话,我们的代码虽然很早就执行了,但系统创建进程时,还得先运行很多代码,OllyDBG仍然可以中断在系统断点上。可是,我们也不应该把别人憋死。给别人留条活路就是给自己留条活路。

 

这篇关于内核态下基于动态感染技术的应用程序执行保护(五 动态感染)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Golang如何对cron进行二次封装实现指定时间执行定时任务

《Golang如何对cron进行二次封装实现指定时间执行定时任务》:本文主要介绍Golang如何对cron进行二次封装实现指定时间执行定时任务问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录背景cron库下载代码示例【1】结构体定义【2】定时任务开启【3】使用示例【4】控制台输出总结背景

springboot如何通过http动态操作xxl-job任务

《springboot如何通过http动态操作xxl-job任务》:本文主要介绍springboot如何通过http动态操作xxl-job任务的问题,具有很好的参考价值,希望对大家有所帮助,如有错... 目录springboot通过http动态操作xxl-job任务一、maven依赖二、配置文件三、xxl-

Qt如何实现文本编辑器光标高亮技术

《Qt如何实现文本编辑器光标高亮技术》这篇文章主要为大家详细介绍了Qt如何实现文本编辑器光标高亮技术,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录实现代码函数作用概述代码详解 + 注释使用 QTextEdit 的高亮技术(重点)总结用到的关键技术点应用场景举例示例优化建议

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

Java中的登录技术保姆级详细教程

《Java中的登录技术保姆级详细教程》:本文主要介绍Java中登录技术保姆级详细教程的相关资料,在Java中我们可以使用各种技术和框架来实现这些功能,文中通过代码介绍的非常详细,需要的朋友可以参考... 目录1.登录思路2.登录标记1.会话技术2.会话跟踪1.Cookie技术2.Session技术3.令牌技

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

SpringBoot实现Kafka动态反序列化的完整代码

《SpringBoot实现Kafka动态反序列化的完整代码》在分布式系统中,Kafka作为高吞吐量的消息队列,常常需要处理来自不同主题(Topic)的异构数据,不同的业务场景可能要求对同一消费者组内的... 目录引言一、问题背景1.1 动态反序列化的需求1.2 常见问题二、动态反序列化的核心方案2.1 ht

golang实现动态路由的项目实践

《golang实现动态路由的项目实践》本文主要介绍了golang实现动态路由项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习... 目录一、动态路由1.结构体(数据库的定义)2.预加载preload3.添加关联的方法一、动态路由1

Web技术与Nginx网站环境部署教程

《Web技术与Nginx网站环境部署教程》:本文主要介绍Web技术与Nginx网站环境部署教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、Web基础1.域名系统DNS2.Hosts文件3.DNS4.域名注册二.网页与html1.网页概述2.HTML概述3.