在《Windows CE初探》一文中已经介绍了KDataStruct的结构,这是一个非常重要的数据结构,可以从用户态的应用程序访问。其开始地址是固定的PUserKData(在SDK中定义:Windows CE Tools\wce420\POCKET PC 2003\Include\Armv4\kfuncs.h),对于ARM处理器是0xFFFFC800,而其它处理器是0x00005800。偏移KINFO_OFFSET是UserKInfo数组,里面保存了重要的系统数据,比如模块链表、内核堆、APIset pointers表(SystemAPISets)。《Windows CE初探》一文中通过模块链表最终来搜索API在coredll中的地址,本文我们将讨论一下UserKInfo[KINX_APISETS]处的APIset pointers表。
Windows CE的API机制使用了PSLs(protected server libraries),是一种客户端/服务端模式。PSLs象DLL一样处理导出服务,服务的导出通过注册APIset。
/** * Data structures and functions for handle manipulations */
typedef struct cinfo { char acName[4]; /* 00: object type ID string */ uchar disp; /* 04: type of dispatch */ uchar type; /* 05: api handle type */ ushort cMethods; /* 06: # of methods in dispatch table */ const PFNVOID *ppfnMethods;/* 08: ptr to array of methods (in server address space) */ const DWORD *pdwSig; /* 0C: ptr to array of method signatures */ PPROCESS pServer; /* 10: ptr to server process */ } CINFO; /* cinfo */ typedef CINFO *PCINFO;
//pointer to struct Process declared in Kernel.h. typedef void * PPROCESS; //I will not bother redeclaring this large structure. //I will only define offsets to 2 fields used in DumpApis(): #define PROCESS_NUM_OFFSET 0 //process number (index of the slot) #define PROCESS_NAME_OFFSET 0x20 //pointer to the process name
//Also declare structure CINFO, which holds an information //about an API (originally declared in //PRIVATE\WINCEOS\COREOS\NK\INC\Kernel.h). typedef struct cinfo { char acName[4]; /* 00: object type ID string */ uchar disp; /* 04: type of dispatch */ uchar type; /* 05: api handle type */ ushort cMethods; /* 06: # of methods in dispatch table */ const PFNVOID *ppfnMethods;/* 08: ptr to array of methods (in server address space) */ const DWORD *pdwSig; /* 0C: ptr to array of method signatures */ PPROCESS pServer; /* 10: ptr to server process */ } CINFO; /* cinfo */
#define NUM_SYSTEM_SETS 32
/*------------------------------------------------------------------- FUNCTION: ProcessAddress PURPOSE: returns an address of memory slot for the given process index. PARAMETERS: BYTE p_byProcNum - process number (slot index) between 0 and 31 RETURNS: Address of the memory slot. -------------------------------------------------------------------*/ inline DWORD ProcessAddress(BYTE p_byProcNum) { return 0x02000000 * (p_byProcNum+1); }
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { FILE *fp; DWORD l_dwOldPermissions = 0;
//If this API is served by an application - get it's //address, if it is served by the kernel - use address 0 DWORD l_dwBaseAddress = 0; if(l_pServer) { l_dwBaseAddress = ProcessAddress (*(l_pServer + PROCESS_NUM_OFFSET)); }
//Add the base address to the method and signature //tables pointers PFNVOID * l_ppMethods = (PFNVOID *)l_pSet->ppfnMethods; if(l_ppMethods && (DWORD)l_ppMethods < 0x2000000) { l_ppMethods = (PFNVOID *) ((DWORD)l_ppMethods + l_dwBaseAddress); }
#define CURTLSPTR_OFFSET 0x000 #define UTlsPtr() (*(LPDWORD *)(PUserKData+CURTLSPTR_OFFSET)) #define PRETLS_THRDINFO -5 // current thread's information (bit fields, only bit 0 used for now)
#define UTLS_INKMODE 0x00000001 // bit 1 set if in kmode
回到coredll.dll中SetCleanRebootFlag的实现,这时可以知道判断lpvTls偏移-0x14的内容是为了检查当前是否内核模式。由于Pocket PC ROM编译时使用了Enable Full Kernel Mode选项,所以程序都是以内核模式运行。于是接着调试时可以看到取0x1FC6760的内容,取出来后,R0的值时0x8004B138,这个值正好是DumpApis程序输出的第一个APIset的ppfnMethods。接下来执行:
系统调用地址相对固定,可以通过索引算出它的trap地址,而且搜索coredll.dll里API地址的方法在用户态是无法实现的,因为模块链表是在内核空间,用户态无法访问。下面就是用系统调用实现的简单shellcode,它的作用是软重启系统,我想对于smartphone的系统应该也是可用(smartphone的ROM在编译时没有用Enable Full Kernel Mode选项)。