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
  • 相关阅读:
    递归函数及Java范例
    笔记本的硬盘坏了
    “References to generic type List should be parameterized”
    配置管理软件(configuration management software)介绍
    WinCE文件目录定制及内存调整
    使用Silverlight for Embedded开发绚丽的界面(3)
    wince国际化语言支持
    Eclipse IDE for Java EE Developers 与Eclipse Classic 区别
    WinCE Heartbeat Message的实现
    使用Silverlight for Embedded开发绚丽的界面(2)
  • 原文地址:https://www.cnblogs.com/nasiry/p/76465.html
Copyright © 2011-2022 走看看