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

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

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

金融业开源技术 术语

金融业开源技术  术语 1  范围 本文件界定了金融业开源技术的常用术语。 本文件适用于金融业中涉及开源技术的相关标准及规范性文件制定和信息沟通等活动。

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时