zoukankan      html  css  js  c++  java
  • 摘抄Windows CE初探


    创建时间:2004-11-05 更新时间:2004-12-06
    文章属性:原创
    文章提交:san (san_at_xfocus.org)

    整理:san
    创建:2004.10.17
    更新:2004.11.09

    --[ 1. ARM简介

    从Platform Builder来看,Windows CE支持相当多CPU,但现在市场上实际销售的PDA几乎全部采用ARM芯片。ARM是一个RISC构架的32位微处理器,它一次有16个可见的寄存器:r0-r15。其中r0-r7是通用寄存器并可以做任何目的;r8-r12也是通用寄存器,但是在切换到FIQ模式的时候,使用它们的影子(shadow)寄存器;最后这三个是特殊寄存器:

       r13 (sp)     -  堆栈指针
       r14 (lr)     -  链接寄存器
       r15 (pc/psr) -  程序计数器/状态寄存器

    IDAPro和调试器里都是用别名表示。和其它RISC指令类似,ARM指令主要有分支(branch)指令、载入和存储指令和其它指令等,除了载入和存储指令,其它指令都是不能直接操作内存的,而且载入和存储指令操作的是4字节类型,那么内存地址必须要求4字节对齐,这也是RISC指令和CISC指令差异比较大的地方,在操作字符串的时候相对就比较麻烦。ARM指令一个很有趣的地方就是可以直接修改访问pc寄存器,这样如果写shellcode的话就不必象SPARC或PowerPC一样需要多条指令来定位自身。

    另外Windows CE默认使用的字节序是little-endian。

    --[ 2. Windows CE核心结构

    Windows CE是一个32位的操作系统,所以其虚拟内存的大小是4GB(2的32次方)。Windows CE把这4GB虚拟内存空间分为低地址2GB和高地址2GB。应用程序使用的地址空间是低地址2GB,高地址2GB专供Windows CE内核使用。在Windows CE 3.0源码的PRIVATE/WINCEOS/COREOS/NK/INC/nkarm.h头文件里有一些有趣的信息:

    /* High memory layout
    *
    * This structure is mapped in at the end of the 4GB virtual
    * address space.
    *
    *  0xFFFD0000 - first level page table (uncached) (2nd half is r/o)
    *  0xFFFD4000 - disabled for protection
    *  0xFFFE0000 - second level page tables (uncached)
    *  0xFFFE4000 - disabled for protection
    *  0xFFFF0000 - exception vectors
    *  0xFFFF0400 - not used (r/o)
    *  0xFFFF1000 - disabled for protection
    *  0xFFFF2000 - r/o (physical overlaps with vectors)
    *  0xFFFF2400 - Interrupt stack (1k)
    *  0xFFFF2800 - r/o (physical overlaps with Abort stack & FIQ stack)
    *  0xFFFF3000 - disabled for protection
    *  0xFFFF4000 - r/o (physical memory overlaps with vectors & intr. stack & FIQ stack)
    *  0xFFFF4900 - Abort stack (2k - 256 bytes)
    *  0xFFFF5000 - disabled for protection
    *  0xFFFF6000 - r/o (physical memory overlaps with vectors & intr. stack)
    *  0xFFFF6800 - FIQ stack (256 bytes)
    *  0xFFFF6900 - r/o (physical memory overlaps with Abort stack)
    *  0xFFFF7000 - disabled
    *  0xFFFFC000 - kernel stack
    *  0xFFFFC800 - KDataStruct
    *  0xFFFFCC00 - disabled for protection (2nd level page table for 0xFFF00000)
    */

    typedef struct ARM_HIGH {
        ulong    firstPT[4096];        // 0xFFFD0000: 1st level page table
        PAGETBL    aPT[16];            // 0xFFFD4000: 2nd level page tables
        char    reserved2[0x20000-0x4000-16*sizeof(PAGETBL)];

        char    exVectors[0x400];    // 0xFFFF0000: exception vectors
        char    reserved3[0x2400-0x400];

        char    intrStack[0x400];    // 0xFFFF2400: interrupt stack
        char    reserved4[0x4900-0x2800];

        char    abortStack[0x700];    // 0xFFFF4900: abort stack
        char    reserved5[0x6800-0x5000];

        char    fiqStack[0x100];    // 0xFFFF6800: FIQ stack
        char    reserved6[0xC000-0x6900];

        char    kStack[0x800];        // 0xFFFFC000: kernel stack
        struct KDataStruct kdata;    // 0xFFFFC800: kernel data page
    } ARM_HIGH;

    其中KDataStruct的结构非常重要而且有意思,有些类似Win32下的PEB结构,定义了系统各种重要的信息:

    struct KDataStruct {
        LPDWORD lpvTls;         /* 0x000 Current thread local storage pointer */
        HANDLE  ahSys[NUM_SYS_HANDLES]; /* 0x004 If this moves, change kapi.h */
        // NUM_SYS_HANDLES == 32 : PUBLIC/COMMON/SDK/INC/kfuncs.h
            0x004 SH_WIN32
            0x008 SH_CURTHREAD
            0x00c SH_CURPROC
            0x010 SH_KWIN32
            0x044 SH_GDI
            0x048 SH_WMGR
            0x04c SH_WNET
            0x050 SH_COMM
            0x054 SH_FILESYS_APIS
            0x058 SH_SHELL
            0x05c SH_DEVMGR_APIS
            0x060 SH_TAPI
            0x064 SH_PATCHER
            0x06c SH_SERVICES

        char    bResched;       /* 0x084 reschedule flag */
        char    cNest;          /* 0x085 kernel exception nesting */
        char    bPowerOff;      /* 0x086 TRUE during "power off" processing */
        char    bProfileOn;     /* 0x087 TRUE if profiling enabled */
        ulong   unused;         /* 0x088 unused */
        ulong   rsvd2;          /* 0x08c was DiffMSec */
        PPROCESS pCurPrc;       /* 0x090 ptr to current PROCESS struct */
        PTHREAD pCurThd;        /* 0x094 ptr to current THREAD struct */
        DWORD   dwKCRes;        /* 0x098  */
        ulong   handleBase;     /* 0x09c handle table base address */
        PSECTION aSections[64]; /* 0x0a0 section table for virutal memory */
        LPEVENT alpeIntrEvents[SYSINTR_MAX_DEVICES];/* 0x1a0 */
        LPVOID  alpvIntrData[SYSINTR_MAX_DEVICES];  /* 0x220 */
        ulong   pAPIReturn;     /* 0x2a0 direct API return address for kernel mode */
        uchar   *pMap;          /* 0x2a4 ptr to MemoryMap array */
        DWORD   dwInDebugger;   /* 0x2a8 !0 when in debugger */
        PTHREAD pCurFPUOwner;   /* 0x2ac current FPU owner */
        PPROCESS pCpuASIDPrc;   /* 0x2b0 current ASID proc */
        long    nMemForPT;      /* 0x2b4 - Memory used for PageTables */

        long    alPad[18];      /* 0x2b8 - padding */
        DWORD   aInfo[32];      /* 0x300 - misc. kernel info */
        // PUBLIC/COMMON/OAK/INC/pkfuncs.h
            0x300  KINX_PROCARRAY     address of process array
            0x304  KINX_PAGESIZE      system page size
            0x308  KINX_PFN_SHIFT     shift for page # in PTE
            0x30c  KINX_PFN_MASK      mask for page # in PTE
            0x310  KINX_PAGEFREE      # of free physical pages
            0x314  KINX_SYSPAGES      # of pages used by kernel
            0x318  KINX_KHEAP         ptr to kernel heap array
            0x31c  KINX_SECTIONS      ptr to SectionTable array
            0x320  KINX_MEMINFO       ptr to system MemoryInfo struct
            0x324  KINX_MODULES       ptr to module list
            0x328  KINX_DLL_LOW       lower bound of DLL shared space
            0x32c  KINX_NUMPAGES      total # of RAM pages
            0x330  KINX_PTOC          ptr to ROM table of contents
            0x334  KINX_KDATA_ADDR    kernel mode version of KData
            0x338  KINX_GWESHEAPINFO  Current amount of gwes heap in use
            0x33c  KINX_TIMEZONEBIAS  Fast timezone bias info
            0x340  KINX_PENDEVENTS    bit mask for pending interrupt events
            0x344  KINX_KERNRESERVE   number of kernel reserved pages
            0x348  KINX_API_MASK      bit mask for registered api sets
            0x34c  KINX_NLS_CP        hiword OEM code page, loword ANSI code page
            0x350  KINX_NLS_SYSLOC    Default System locale
            0x354  KINX_NLS_USERLOC   Default User locale
            0x358  KINX_HEAP_WASTE    Kernel heap wasted space
            0x35c  KINX_DEBUGGER      For use by debugger for protocol communication
            0x360  KINX_APISETS       APIset pointers
            0x364  KINX_MINPAGEFREE   water mark of the minimum number of free pages
            0x368  KINX_CELOGSTATUS   CeLog status flags
            0x36c  KINX_NKSECTION     Address of NKSection
            0x370  KINX_PWR_EVTS      Events to be set after power on
            0x37c  KINX_NKSIG         last entry of KINFO -- signature when NK is ready

            /* 0x380 - interlocked api code */
            /* 0x400 - end */
    }

    Win32下可以通过PEB结构定位kernel32.dll的基址,然后通过PE文件结构查找Windows API。在Windows CE下,coredll.dll的作用相当于Win32的kernel32.dll,由于KDataStruct结构开始于0xFFFFC800,偏移0x324的aInfo[KINX_MODULES]是一个指向模块链表的指针,通过这个链表能否找到coredll.dll模块呢?让我们来看一下模块的结构:

    // PRIVATE/WINCEOS/COREOS/NK/INC/kernel.h
    typedef struct Module {
        LPVOID      lpSelf;                 /* 0x00 Self pointer for validation */
        PMODULE     pMod;                   /* 0x04 Next module in chain */
        LPWSTR      lpszModName;            /* 0x08 Module name */
        DWORD       inuse;                  /* 0x0c Bit vector of use */
        DWORD       calledfunc;             /* 0x10 Called entry but not exit */
        WORD        refcnt[MAX_PROCESSES];  /* 0x14 Reference count per process*/
        LPVOID      BasePtr;                /* 0x54 Base pointer of dll load (not 0 based) */
        DWORD       DbgFlags;               /* 0x58 Debug flags */
        LPDBGPARAM  ZonePtr;                /* 0x5c Debug zone pointer */
        ulong       startip;                /* 0x60 0 based entrypoint */
        openexe_t   oe;                     /* 0x64 Pointer to executable file handle */
            typedef struct openexe_t {
                 union {
                      int hppfs;           // ppfs handle
                      HANDLE hf;           // object store handle
                      TOCentry *tocptr;    // rom entry pointer
                 };                        // 0x64
                 BYTE filetype;           // 0x68
                 BYTE bIsOID;             // 0x69
                 WORD pagemode;           // 0x6a
                 union {
                      DWORD offset;
                      DWORD dwExtRomAttrib;
                 };                       // 0x6c
                 union {
                      Name *lpName;
                      CEOID ceOid;
                 };                       // 0x70
            } openexe_t;
        e32_lite    e32;                    /* 0x74 E32 header */
        // PUBLIC/COMMON/OAK/INC/pehdr.h
          typedef struct e32_lite {           /* PE 32-bit .EXE header               */
              unsigned short  e32_objcnt;     /* 0x74 Number of memory objects            */
              BYTE            e32_cevermajor; /* 0x76 version of CE built for             */
              BYTE            e32_ceverminor; /* 0x77 version of CE built for             */
              unsigned long   e32_stackmax;   /* 0x78 Maximum stack size                  */
              unsigned long   e32_vbase;      /* 0x7c Virtual base address of module      */
              unsigned long   e32_vsize;      /* 0x80 Virtual size of the entire image    */
              unsigned long e32_sect14rva;    /* 0x84 section 14 rva */
              unsigned long e32_sect14size;   /* 0x88 section 14 size */
              struct info e32_unit[LITE_EXTRA]; /* 0x8c  Array of extra info units     */
                struct info {                       /* Extra information header block      */
                    unsigned long   rva;            /* Virtual relative address of info    */
                    unsigned long   size;           /* Size of information block           */
                }
                0x8c   EXP Export table position    
                0x94   IMP Import table position    
                0x9c   RES Resource table position  
                0xa4   EXC Exception table position
                0xac   SEC Security table position  
                0xb4   FIX Fixup table position    
          } e32_lite, *LPe32_list;

        o32_lite    *o32_ptr;               /* 0xbc  O32 chain ptr */
        DWORD       dwNoNotify;             /* 0xc0  1 bit per process, set if notifications disabled */
        WORD        wFlags;                 // 0xc4
        BYTE        bTrustLevel;            // 0xc6
        BYTE        bPadding;               // 0xc7
        PMODULE     pmodResource;           /* 0xc8 module that contains the resources */
        DWORD       rwLow;                  /* 0xcc base address of RW section for ROM DLL */
        DWORD       rwHigh;                 /* 0xd0 high address RW section for ROM DLL */
        PGPOOL_Q    pgqueue;                /* 0xcc list of the page owned by the module */
          typedef struct _PGPOOL_Q {
              WORD    idxHead;            /* head of the queue */
              WORD    idxTail;            /* tail of the queue */
          } PGPOOL_Q, *PPGPOOL_Q;
    } Module;

    模块结构偏移0x08是指向模块名字的指针,偏移0x04指向链表里的下一个模块,通过这个模块名字可以在模块链表里找到我们需要的coredll.dll。而偏移0x7c就是该模块的虚拟基址,偏移0x8c是导出表的相对地址。Windows CE使用的PE结构和Win32是一样,那么有写Win32 shellcode经验的朋友自然会想着从coredll.dll的基址按照PE结构顺藤摸瓜来搜索函数地址。但是我们用EVC实际调试时发现coredll.dll找到的基址是0x01F60000,而这个地址的内存是没有分配的(调试器里都是问号),可以访问的地址是从0x01F61000,这和IDAPro反汇编coredll.dll(从rom文件中dump出来,wince下系统在使用的文件连读的权限都没有)出的起始地址一样。Windows CE可能为了节省内存,没有加载文件最开始的0x1000字节头结构。不过没有关系,因为我们已经从模块结构得到该模块的导出表相对地址,直接从导出表就可以查找函数地址了。

    --[ 3. Windows CE演示实例

    Ratter/29A的WinCE4.Dust代码可能是为了减少体积,他给出的方法是把要搜索函数的序数索引硬编码到程序里,然后根据导出表的地址定位导出地址表,再用硬编码的序数索引来算出函数地址。这种方法让人感觉挺别扭的,虽然代码减少了,可是通用性可能会打折扣,象编写Win32 shellcode一样通过函数名来搜索的方法可能更通用一些。下面的代码就是实现这样的功能。

    ; armasm test.asm
    ; link /MACHINE:ARM /SUBSYSTEM:WINDOWSCE test.obj  
      
      CODE32

       EXPORT  WinMainCRTStartup

       AREA .text, CODE, ARM

    test_start

    ; r11 - base pointer
    test_code_start   PROC
       stmdb   sp!, {r0 - r12, lr, pc}

       bl    get_export_section
       adr   r2, mb
       bl    lookup_imports

       mov    r0, #0
       adr    r1, text
       adr    r2, text
       mov    r3, #0             ; MB_OK
       mov    lr, pc
       mov    pc, r9             ; MessageBoxW

       bl    get_export_section
       adr   r2, tp
       bl    lookup_imports

       mov   r0, #-1
       mov   r1, #0
       mov   lr, pc
       mov   pc, r9
      
       ; basic wide string compare
    wstrcmp   PROC
    wstrcmp_iterate
       ldrh    r2, [r0], #2
       ldrh    r3, [r1], #2

       cmp     r2, #0
       cmpeq   r3, #0
       moveq   pc, lr

       cmp    r2, r3
       beq    wstrcmp_iterate

       mov    pc, lr
       ENDP

    ; output:
    ;  r0 - coredll base addr
    ;  r1 - export section addr
    get_export_section   PROC
       stmdb   sp!, {r4 - r9, lr}

       ldr    r4, =0xffffc800   ; KDataStruct
       ldr    r5, =0x324        ; aInfo[KINX_MODULES]

       add    r5, r4, r5
       ldr    r5, [r5]

       ; r5 now points to first module

       mov    r6, r5
       mov    r7, #0

    iterate
       ldr    r0, [r6, #8]     ; get dll name
       adr    r1, coredll
       bl    wstrcmp        ; compare with coredll.dll

       ldreq   r7, [r6, #0x7c]    ; get dll base
       ldreq   r8, [r6, #0x8c]    ; get export section rva

       add    r9, r7, r8
       beq    got_coredllbase    ; is it what we're looking for?

       ldr    r6, [r6, #4]
       cmp    r6, #0
       cmpne   r6, r5
       bne    iterate        ; nope, go on

    got_coredllbase
       mov    r0, r7
       add    r1, r8, r7      ; yep, we've got imagebase
                       ; and export section pointer

       ldmia   sp!, {r4 - r9, pc}
       ENDP

    coredll   DCB    "c", 0x0, "o", 0x0, "r", 0x0, "e", 0x0, "d", 0x0, "l", 0x0, "l", 0x0
          DCB    ".", 0x0, "d", 0x0, "l", 0x0, "l", 0x0, 0x0, 0x0

       ; basic string compare
    bstrcmp   PROC
    bstrcmp_iterate
       ldrb    r9,  [r7], #1
       ldrb    r10, [r8], #1

       cmp     r9,  #0
       cmpeq   r10, #0
       moveq   pc, lr

       cmp    r9, r10
       beq    bstrcmp_iterate

       mov    pc, lr
       ENDP

    ; r0 - coredll base addr
    ; r1 - export section addr
    ; r2 - function name addr
    lookup_imports   PROC
       stmdb   sp!, {r4 - r6, lr}

       ldr    r4, [r1, #0x20]    ; AddressOfNames
       add    r4, r4, r0

       mov    r6, #0             ; counter
    lookup_imports_iterate
       ldr    r7, [r4], #4
       add    r7, r7, r0         ; function name ponter
       mov    r8, r2             ; find function name

       bl     bstrcmp

       addne  r6, r6, #1
       bne    lookup_imports_iterate

       ldr    r5, [r1, #0x24]    ; AddressOfNameOrdinals
       add    r5, r5, r0
       add    r6, r6, r6
       ldrh   r9, [r5, r6]       ; Ordinals
       ldr    r5, [r1, #0x1c]    ; AddressOfFunctions
       add    r5, r5, r0
       ldr    r9, [r5, r9, LSL #2] ; function address rva
       add    r9, r9, r0         ; function address

       ldmia    sp!, {r4 - r6, pc}
       ENDP

    mb   DCB     "MessageBoxW", 0x0
    tp   DCB     "TerminateProcess", 0x0,0x0,0x0,0x0
         ALIGN   4

       ; Dear User, am I allowed to spread?

    text DCB    "H", 0x0, "e", 0x0, "l", 0x0, "l", 0x0, "o", 0x0, " ", 0x0
         DCB    "W", 0x0, "i", 0x0, "n", 0x0, "C", 0x0, "E", 0x0, "!", 0x0
         DCB    0x0, 0x0, 0x0, 0x0
         ALIGN    4

       LTORG
    test_end

       ; the code after test_end doesn't get copied to victims

    WinMainCRTStartup PROC
       b     test_code_start
       ENDP

       ; first generation entry point
    host_entry
       mvn    r0, #0
       mov    pc, lr
       END

    代码还是比较傻,如果能把Win32 shellcode的hash引入可能代码会更好看一些。Ratter/29A在WinCE4.Dust虽然还只是个概念病毒,但是病毒的基本技术它都已经具备了,所以不难相信不久就会有更多的Windows CE病毒。如果作者不怀好意,用KernelIoControl把系统引导入BootLoader模式,那么对于很多非专业的用户来说无疑象遭遇CIH病毒一般可恶。

    通过上面的代码不难相信很容易就能写出Windows CE下的shellcode,Seth Fogie在最近的defcon等会议上提到Windows CE下缓冲区溢出,随着PDA网络化程度越来越高,以及和手机的结合,相信Windows CE下的缓冲区溢出不久就会流行起来。不过Windows CE下缓冲区溢出可能会遭遇几个问题:

    1. Windows CE是一个Unicode环境,它可能会把用户输入的数据转成Unicode格式。
    2. 要在Windows CE上写解码shellcode可能会有些问题,首先arm没有xor指令,另外还有可能遭遇指令缓存的问题。不是很清楚Windows CE对软中断指令swi怎么支持。
    3. 不同厂商不同版本的PDA可能存在这样那样的差异,导致攻击程序无法通用。

    不过Windows CE现在已经发展的很成熟了,可以进来看看。

    --[ 4. 参考资料:

    1. ARM ASSEMBLER
       http://www.heyrick.co.uk/assembler/index.html
    2. misc notes on the xda and windows ce
       http://www.xs4all.nl/~itsme/projects/xda/
    3. Windows CE 3.0 Source Code
       http://msdn.microsoft.com/embedded/prevver/ce3/download/source/default.aspx
    4. Details Emerge on the First Windows Mobile Virus
       http://www.informit.com/articles/article.asp?p=337071
  • 相关阅读:
    pytest.4.Fixture
    pytest.3.Assert
    pytest.2.运行多个文件
    [LeetCode 378.] Kth Smallest Element in a Sorted Matrix
    priority_queue 自定义 comparator
    原地调整法查找数组元素
    [LeetCode 436.] Find Right Interval
    [LeetCode 611.] Valid Triangle Number
    二叉树Morris遍历
    用户态IO:DPDK
  • 原文地址:https://www.cnblogs.com/nasiry/p/76465.html
Copyright © 2011-2022 走看看