<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          Windows CE API機(jī)制初探

          作者: 時(shí)間:2011-02-28 來(lái)源:網(wǎng)絡(luò) 收藏
          創(chuàng)建時(shí)間:2005-07-08
          文章屬性:原創(chuàng)
          文章提交:san (san_at_xfocus.org)

          Windows CE API機(jī)制初探

          整理:san
          創(chuàng)建:2005.07.06
          更新:2005.07.07

          --[ 目錄

          1 - Windows CE架構(gòu)

          2 - 列出所有系統(tǒng)API

          3 - Windows CE的系統(tǒng)調(diào)用

          4 - coredll.dll對(duì)API的包裹

          5 - 用系統(tǒng)調(diào)用實(shí)現(xiàn)shellcode

          6 - 小結(jié)

          7 - 感謝

          8 - 參考資料


          --[ 1 - Windows CE架構(gòu)

          在《Windows CE初探》一文中已經(jīng)介紹了KDataStruct的結(jié)構(gòu),這是一個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu),可以從用戶態(tài)的應(yīng)用程序訪問(wèn)。其開(kāi)始地址是固定的PUserKData(在SDK中定義:Windows CE Toolswce420POCKET PC 2003IncludeArmv4kfuncs.h),對(duì)于ARM處理器是0xFFFFC800,而其它處理器是0x00005800。偏移KINFO_OFFSET是UserKInfo數(shù)組,里面保存了重要的系統(tǒng)數(shù)據(jù),比如模塊鏈表、內(nèi)核堆、APIset pointers表(SystemAPISets)。《Windows CE初探》一文中通過(guò)模塊鏈表最終來(lái)搜索API在coredll中的地址,本文我們將討論一下UserKInfo[KINX_APISETS]處的APIset pointers表。

          Windows CE的API機(jī)制使用了PSLs(protected server libraries),是一種客戶端/服務(wù)端模式。PSLs象DLL一樣處理導(dǎo)出服務(wù),服務(wù)的導(dǎo)出通過(guò)注冊(cè)APIset。

          有兩種類型的APIset,分別是固有的和基于句柄的。固有的API sets注冊(cè)在全局表SystemAPISets中,可以以API句柄索引和方法索引的組合來(lái)調(diào)用他們的方法?;诰浔腁PI和內(nèi)核對(duì)象相關(guān),如文件、互斥體、事件等。這些API的方法可以用一個(gè)對(duì)象的句柄和方法索引來(lái)調(diào)用。

          kfuncs.h中定義了固有APIset的句柄索引,如:SH_WIN32、SH_GDI、SH_WMGR等。基于句柄的API索引定義在PUBLICCOMMONOAKINCpsyscall.h中,如:HT_EVENT、HT_APISET、HT_SOCKET等。

          SystemAPISets共有32個(gè)CINFO結(jié)構(gòu)的APIset,通過(guò)遍歷SystemAPISets成員,可以列出系統(tǒng)所有API。其中CINFO的結(jié)構(gòu)在PRIVATEWINCEOSCOREOSNKINCkernel.h中定義:

          /**
          * 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;


          --[ 2 - 列出所有系統(tǒng)API

          Dmitri Leman在他的cespy中有個(gè)DumpApis函數(shù),略加修改后如下:

          / DumpApis.cpp
          /

          #include "stdafx.h"

          extern "C" DWORD __stdcall SetProcPermissions(DWORD);

          #define KINFO_OFFSET 0x300
          #define KINX_API_MASK 18
          #define KINX_APISETS 24

          #define UserKInfo ((long *)(PUserKData KINFO_OFFSET))

          /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
          /PRIVATEWINCEOSCOREOSNKINCKernel.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 ( (fp = fopen("\apis.txt", "w")) == NULL )
          {
          return 1;
          }

          fprintf(fp, "Dump APIs:n");

          __try
          {
          /Get access to memory slots of other processes
          l_dwOldPermissions = SetProcPermissions(-1);

          CINFO ** l_pSystemAPISets = (CINFO **)(UserKInfo[KINX_APISETS]);

          for(int i = 0; i NUM_SYSTEM_SETS; i )
          {
          CINFO * l_pSet = l_pSystemAPISets[i];
          if(!l_pSet)
          {
          continue;
          }
          LPBYTE l_pServer = (LPBYTE)l_pSet->pServer;
          fprintf(fp,
          "APIset: X acName: %.4s disp: %d type: %d cMethods: %d "
          "ppfnMethods: X pdwSig: X pServer: X %lsn",
          i,
          l_pSet->acName,
          l_pSet->disp,
          l_pSet->type,
          l_pSet->cMethods,
          l_pSet->ppfnMethods,
          l_pSet->pdwSig,
          l_pServer,
          l_pServer? (*(LPTSTR*)
          (l_pServer PROCESS_NAME_OFFSET)) : _T("") );

          /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);
          }

          DWORD * l_pdwMethodSignatures = (DWORD *)l_pSet->pdwSig;
          if(l_pdwMethodSignatures
          (DWORD)l_pdwMethodSignatures 0x2000000)
          {
          l_pdwMethodSignatures = (DWORD *)
          ((DWORD)l_pdwMethodSignatures l_dwBaseAddress);
          }

          if(l_ppMethods)
          {
          for(int j = 0; j l_pSet->cMethods; j )
          {
          PFNVOID l_pMethod = l_ppMethods?
          l_ppMethods[j] : 0;
          if(l_pMethod (DWORD)l_pMethod 0x2000000)
          {
          l_pMethod = (PFNVOID)
          ((DWORD)l_pMethod l_dwBaseAddress);
          }
          DWORD l_dwSign = l_pdwMethodSignatures?
          l_pdwMethodSignatures[j] : 0;
          fprintf(fp,
          " meth #%3i: X sign Xn",
          j,
          l_pMethod,
          l_dwSign);
          }
          }
          }/for(int i = 0; i NUM_SYSTEM_SETS; i )
          }
          __except(1)
          {
          fprintf(fp, "Exception in DumpApisn");
          }

          if(l_dwOldPermissions)
          {
          SetProcPermissions(l_dwOldPermissions);
          }
          fclose(fp);

          return 0;
          }

          來(lái)看一下此程序輸出的片斷:

          APIset: 00 acName: Wn32 disp: 3 type: 0 cMethods: 185 ppfnMethods: 8004B138 pdwSig: 00000000 pServer: 00000000
          meth # 0: 8006C83C sign 00000000
          meth # 1: 8006C844 sign 00000000
          meth # 2: 800804C4 sign 00000000
          meth # 3: 8006BF20 sign 00000000
          meth # 4: 8006BF94 sign 00000000
          meth # 5: 8006BFEC sign 00000000
          meth # 6: 8006C0A0 sign 00000000
          meth # 7: 8008383C sign 00000000
          meth # 8: 80068FC8 sign 00000000
          meth # 9: 800694B0 sign 00000000
          meth # 10: 8006968C sign 00000000
          ...

          這是最開(kāi)始的一個(gè)APIset,它的ppfnMethods是0x8004B138,cMethods是185,根據(jù)這兩個(gè)數(shù)據(jù)得到185個(gè)地址,這些地址實(shí)際上就是內(nèi)核系統(tǒng)調(diào)用的實(shí)現(xiàn)地址。它們的索引相對(duì)PRIVATEWINCEOSCOREOSNKKERNELkwin32.h里的Win32Methods數(shù)組:

          const PFNVOID Win32Methods[] = {
          (PFNVOID)SC_Nop,
          (PFNVOID)SC_NotSupported,
          (PFNVOID)SC_CreateAPISet, / 2
          (PFNVOID)EXT_VirtualAlloc, / 3
          (PFNVOID)EXT_VirtualFree, / 4
          (PFNVOID)EXT_VirtualProtect, / 5
          (PFNVOID)EXT_VirtualQuery, / 6
          (PFNVOID)SC_VirtualCopy, / 7
          (PFNVOID)SC_LoadLibraryW, / 8
          (PFNVOID)SC_FreeLibrary, / 9
          (PFNVOID)SC_GetProcAddressW, / 10
          ...
          (PFNVOID)SC_InterruptMask, / 184
          };


          --[ 3 - Windows CE的系統(tǒng)調(diào)用

          Windows CE沒(méi)有使用ARM處理器的SWI指令來(lái)實(shí)現(xiàn)系統(tǒng)調(diào)用,SWI指令在Windows CE里是空的,就簡(jiǎn)單的執(zhí)行了"movs pc,lr"(詳見(jiàn)armtrap.s關(guān)于SWIHandler的實(shí)現(xiàn))。Windows CE的系統(tǒng)調(diào)用使用了0xf0000000 - 0xf0010000的地址,當(dāng)系統(tǒng)執(zhí)行這些地址的時(shí)候?qū)?huì)觸發(fā)異常,產(chǎn)生一個(gè)PrefetchAbort的trap。在PrefetchAbort的實(shí)現(xiàn)里(詳見(jiàn)armtrap.s)首先會(huì)檢查異常地址是否在系統(tǒng)調(diào)用trap區(qū),如果不是,那么執(zhí)行ProcessPrefAbort,否則執(zhí)行ObjectCall查找API地址來(lái)分派。

          通過(guò)APIset和其API的索引可以算出系統(tǒng)調(diào)用地址,其公式是:0xf0010000-(256*apiset apinr)*4。比如對(duì)于SC_CreateAPISet的系統(tǒng)調(diào)用可以這樣算出來(lái):0xf0010000-(256*0 2)*4=0xF000FFF8。


          --[ 4 - coredll.dll對(duì)API的包裹

          選擇一個(gè)沒(méi)有參數(shù)的SetCleanRebootFlag()進(jìn)行分析,IDAPro對(duì)其的反匯編如下:

          .text:01F74F70 EXPORT SetCleanRebootFlag
          .text:01F74F70 SetCleanRebootFlag
          .text:01F74F70 STMFD SP!, {R4,R5,LR}
          .text:01F74F74 LDR R5, =0xFFFFC800
          .text:01F74F78 LDR R4, =unk_1FC6760
          .text:01F74F7C LDR R0, [R5] ; (2FF00-0x14) -> 1
          .text:01F74F80 LDR R1, [R0,#-0x14]
          .text:01F74F84 TST R1, #1
          .text:01F74F88 LDRNE R0, [R4] ; 8004B138 ppfnMethods
          .text:01F74F8C CMPNE R0, #0
          .text:01F74F90 LDRNE R1, [R0,#0x134]
          .text:01F74F94 LDREQ R1, =0xF000FECC
          .text:01F74F98 MOV LR, PC
          .text:01F74F9C MOV PC, R1 ; 80062AAC SC_SetCleanRebootFlag
          .text:01F74FA0 LDR R3, [R5]
          .text:01F74FA4 LDR R0, [R3,#-0x14]
          .text:01F74FA8 TST R0, #1
          .text:01F74FAC LDRNE R0, [R4] ; 8004B138 ppfnMethods
          .text:01F74FB0 CMPNE R0, #0
          .text:01F74FB4 LDRNE R0, [R0,#0x25C]
          .text:01F74FB8 MOVNE LR, PC ; 800810EC SC_KillThreadIfNeeded
          .text:01F74FBC MOVNE PC, R0
          .text:01F74FC0 LDMFD SP!, {R4,R5,PC}
          .text:01F74FC0 ; End of function SetCleanRebootFlag

          寫一個(gè)包含SetCleanRebootFlag()函數(shù)的小程序用EVC進(jìn)行跟蹤調(diào)試,按F11進(jìn)入該函數(shù)以后,程序首先取KDataStruct的lpvTls成員,然后取lpvTls偏移-0x14的內(nèi)容,測(cè)試該內(nèi)容是否是1。

          得先來(lái)了解一下lpvTls偏移-0x14的數(shù)據(jù)是什么。先看PUBLICCOMMONOAKINCpkfuncs.h里的幾個(gè)定義:

          #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

          看來(lái)lpvTls偏移-0x14保存的是當(dāng)前線程信息,只有第0比特被使用。再來(lái)看PRIVATEWINCEOSCOREOSNKKERNELARMmdram.c里的MDCreateMainThread2函數(shù):

          ...
          if (kmode || bAllKMode) {
          pTh->ctx.Psr = KERNEL_MODE;
          KTHRDINFO (pTh) |= UTLS_INKMODE;
          } else {
          pTh->ctx.Psr = USER_MODE;
          KTHRDINFO (pTh) = ~UTLS_INKMODE;
          }
          ...

          KTHRDINFO (pTh)在PRIVATEWINCEOSCOREOSNKINCkernel.h里定義:

          #define KTHRDINFO(pth) ((pth)->tlsPtr[PRETLS_THRDINFO])

          它就是lpvTls偏移-0x14。也就是說(shuō)系統(tǒng)在創(chuàng)建主線程的時(shí)候,根據(jù)程序當(dāng)前的模式來(lái)設(shè)置KTHRDINFO的值,如果是內(nèi)核模式,那么是1,否則是0。

          回到coredll.dll中SetCleanRebootFlag的實(shí)現(xiàn),這時(shí)可以知道判斷l(xiāng)pvTls偏移-0x14的內(nèi)容是為了檢查當(dāng)前是否內(nèi)核模式。由于Pocket PC ROM編譯時(shí)使用了Enable Full Kernel Mode選項(xiàng),所以程序都是以內(nèi)核模式運(yùn)行。于是接著調(diào)試時(shí)可以看到取0x1FC6760的內(nèi)容,取出來(lái)后,R0的值時(shí)0x8004B138,這個(gè)值正好是DumpApis程序輸出的第一個(gè)APIset的ppfnMethods。接下來(lái)執(zhí)行:

          .text:01F74F90 LDRNE R1, [R0,#0x134]
          .text:01F74F94 LDREQ R1, =0xF000FECC

          由于程序是內(nèi)核模式,所以前一條指令成功取出值,后一條無(wú)效。這時(shí)R1的值是0x80062AAC,和DumpApis程序輸出的一個(gè)地址匹配,根據(jù)索引,發(fā)現(xiàn)這個(gè)地址是SC_SetCleanRebootFlag在內(nèi)核中的實(shí)現(xiàn)。其實(shí)索引也可以根據(jù)這條指令的偏移來(lái)?。?x134/4=0x4D(77),根據(jù)kwin32.h里Win32Methods的索引直接就對(duì)應(yīng)出SC_SetCleanRebootFlag。內(nèi)核模式的話,后面還會(huì)執(zhí)行SC_KillThreadIfNeeded。

          如果是用戶模式的話,系統(tǒng)會(huì)執(zhí)行0xF000FECC這個(gè)地址,這顯然是一個(gè)系統(tǒng)調(diào)用trap地址。根據(jù)上面的公式算出索引值:(0xf0010000-0xF000FECC)/4=0x4D(77),根據(jù)kwin32.h里Win32Methods的索引也對(duì)應(yīng)出這是SC_SetCleanRebootFlag。

          通過(guò)分析coredll.dll對(duì)API包裹的實(shí)現(xiàn),可以發(fā)現(xiàn)Windows CE在調(diào)用一部分API的時(shí)候會(huì)先判斷程序是否處于內(nèi)核模式,如果是,那么不用系統(tǒng)調(diào)用方式,直接奔內(nèi)核實(shí)現(xiàn)地址去了,否則就老老實(shí)實(shí)的用系統(tǒng)調(diào)用地址。


          --[ 5 - 用系統(tǒng)調(diào)用實(shí)現(xiàn)shellcode

          系統(tǒng)調(diào)用地址相對(duì)固定,可以通過(guò)索引算出它的trap地址,而且搜索coredll.dll里API地址的方法在用戶態(tài)是無(wú)法實(shí)現(xiàn)的,因?yàn)槟K鏈表是在內(nèi)核空間,用戶態(tài)無(wú)法訪問(wèn)。下面就是用系統(tǒng)調(diào)用實(shí)現(xiàn)的簡(jiǎn)單shellcode,它的作用是軟重啟系統(tǒng),我想對(duì)于smartphone的系統(tǒng)應(yīng)該也是可用(smartphone的ROM在編譯時(shí)沒(méi)有用Enable Full Kernel Mode選項(xiàng))。

          #include "stdafx.h"

          int shellcode[] =
          {
          0xE59F0014, / ldr r0, [pc, #20]
          0xE59F4014, / ldr r4, [pc, #20]
          0xE3A01000, / mov r1, #0
          0xE3A02000, / mov r2, #0
          0xE3A03000, / mov r3, #0
          0xE1A0E00F, / mov lr, pc
          0xE1A0F004, / mov pc, r4
          0x0101003C, / IOCTL_HAL_REBOOT
          0xF000FE74, / trap address of KernelIoControl
          };

          int WINAPI WinMain( HINSTANCE hInstance,
          HINSTANCE hPrevInstance,
          LPTSTR lpCmdLine,
          int nCmdShow)
          {
          ((void (*)(void)) shellcode)();

          return 0;
          }


          --[ 6 - 小結(jié)

          通過(guò)本文可以了解到Windows CE API機(jī)制的大概輪廓,對(duì)于系統(tǒng)調(diào)用的具體流程,也就是trap后的具體流程還不是很清晰,本文也就一塊破磚頭,希望能砸到幾個(gè)人,可以一起討論;)
          文中如有錯(cuò)誤還望不吝賜教,希望Xcon'05見(jiàn)。


          --[ 7 - 感謝

          非常感謝Nasiry對(duì)我的幫助,在他的幫助下才得以完成此文。


          --[ 8 - 參考資料

          [1] Spy: A Windows CE API Interceptor by Dmitri Leman
          Dr. Dobb's Journal October 2003
          [2] misc notes on the xda and windows ce
          http:/www.xs4all.nl/~itsme/projects/xda/
          [3] windowsCE異常和中斷服務(wù)程序初探 by Nasiry
          http:/www.cnblogs.com/nasiry/archive/2004/12/27/82476.html
          http:/www.cnblogs.com/nasiry/archive/2005/01/06/87381.html
          [4] Windows CE 4.2 Source Code
          linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)


          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();