|
在網上找到了一個這樣的例子,是Larry Bank寫的。貼出來以供大家學習。讓大家了解ARM匯編指令,并用vs2005進行調試跟蹤。
工程是pocket pc 2003的工程,主要包含兩個文件:main.c, armtest.asm。
對armtest.asm需要自定義編譯選項,
命令行為 armasm -g armtest.asm
輸出為 armtest.obj
源碼如下:
main.c 源碼
// Windows CE Sample Application
// Demonstrations how to use ARM assembly language within a C program
// Written by Larry Bank 3/25/2007
#include <windows.h>
int iGlobal;
int ARMTEST1(int, int, int , int);
/****************************************************************************
* FUNCTION : WinMain(HANDLE, HANDLE, LPSTR, int) *
*
PURPOSE : Program
entrypoint
*
****************************************************************************/
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
int iResult;
TCHAR szTemp[256];
iGlobal = 5;
iResult = ARMTEST1(1,2,3,4);
wsprintf(szTemp, L"Result = %d", iResult);
MessageBox(HWND_DESKTOP, szTemp, L"ASM Result", MB_OK);
return 0;
} /* WinMain() */
armtest.asm 源碼:
; TITLE("Sample App")
;++
AREA sample, CODE, READONLY ; name this block of code
EXPORT ARMTEST1
IMPORT iGlobal
; Called from C as int ARMTEST1(int, int, int, int);
; The first 4 parameters are passed in r0-r3, more parameters would be passed on the stack
ARMTEST1 proc
add r0,r0,r1 ; add all of the inputs together
add r0,r0,r2
add r0,r0,r3 ldr r1,=iGlobal ; get the value of our global variable
ldr r1,[r1] ; dereference the pointer (I know there's a pipe stall here)
add r0,r0,r1 ; we're not concerned with performance in this example
mov pc,lr ; return to C with the value in R0
endp
LTORG ; allow room for the address constant of our global (loaded relative to PC)
END
hook其他進程的API
今天終于有了一個小小的進步就算是自己的努力來完成的,沒想到HOOK其他進程的API原來這樣的簡單。其實就是兩個關鍵的技術(HOOK-API和遠程線程注入)。
HOOK是一種WINDOWS下存在很久的技術了。 HOOK一般分兩種 1。HOOK MESSAGE 2。HOOK API 本問討論的是HOOK API。(如果你是HOOK高手就不要看了) 在最初學HOOK-API的時候通常都是通過"覆蓋地址"和"修改IAT"的方法。 通過這兩種技術,我們基本都可以實現對本進程的API函數進行HOOK了。但是在高興之余會有點遺憾, 怎么才能HOOK其他進程的API函數呢?怎么才能對一個API函數進行全局的HOOK呢? 下面是我的一個簡單的“HOOK其他進程API函數”的實現。(對另一進程的MessageBoxA這個函數進行HOOK)
里面的應用了兩個技術 1。遠程線程注入 2。修改IAT,HOOK-API
好了貼出代碼如下: 一共是3個文件 install.c 注入程序 fundll.cpp DLL程序 test.cpp 測試程序
//-------------------------install.c-------------------------- // //write by Gxter //install.c
#include "windows.h" #include "tlhelp32.h"
#pragma comment(lib,"th32.lib")
const char *pkill="fundll.dll"; //DLL文件的路徑
//這個路徑很有意思,這個路徑是相對于目標進程的,而不是自身進程。 //所以要嘛寫成絕對路徑,要嘛寫成相對于目標進程的相對路徑。 //如果寫成相對于自身的路徑就要麻煩了,本程序就找不到DLL文件了。
char *prosess="test.exe"; //要注入的進程名(目標進程名)
int main() { HANDLE hSnap; HANDLE hkernel32; //被注入進程的句柄 PROCESSENTRY32 pe; BOOL bNext; HANDLE hToken; TOKEN_PRIVILEGES tp; LUID Luid; LPVOID p; FARPROC pfn;
if (!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) { return 1; }
if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&Luid)) { return 1; }
tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tp.Privileges[0].Luid = Luid;
if (!AdjustTokenPrivileges(hToken,0,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL)) { return 1; }
pe.dwSize = sizeof(pe); hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); bNext=Process32First(hSnap, &pe); while(bNext) { if(!stricmp(pe.szExeFile,prosess)) //--->> { hkernel32=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_WRITE|PROCESS_VM_OPERATION,1,pe.th32ProcessID); break; } bNext=Process32Next(hSnap, &pe); }
CloseHandle(hSnap);
p=VirtualAllocEx(hkernel32,NULL,strlen(pkill),MEM_COMMIT,PAGE_READWRITE); WriteProcessMemory(hkernel32,p,pkill,strlen(pkill),NULL); pfn=GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA"); CreateRemoteThread(hkernel32,NULL,0,pfn,p,NULL,0);
return 0; }
//----------------------fundll.cpp----------------------------- // //write by Gxter // //fundll.cpp
#include "windows.h" #include "process.h" #include "tlhelp32.h" #include "stdio.h"
#pragma comment(lib,"th32.lib")
PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNTHeaders; PIMAGE_OPTIONAL_HEADER pOptHeader; PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; PIMAGE_THUNK_DATA pThunkData; PIMAGE_IMPORT_BY_NAME pImportByName; HMODULE hMod;
// 定義MessageBoxA函數原型 typedef int (WINAPI *PFNMESSAGEBOX)(HWND, LPCSTR, LPCSTR, UINT uType); int WINAPI MessageBoxProxy(IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType);
int * addr = (int *)MessageBoxA; //保存函數的入口地址 int * myaddr = (int *)MessageBoxProxy;
void ThreadProc(void *param);//線程函數
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { if(fdwReason==DLL_PROCESS_ATTACH) _beginthread(ThreadProc,0,NULL);
return TRUE; }
//結束進程的函數
void ThreadProc(void *param) { //------------hook api---------------- hMod = GetModuleHandle(NULL);
pDosHeader = (PIMAGE_DOS_HEADER)hMod; pNTHeaders = (PIMAGE_NT_HEADERS)((BYTE *)hMod + pDosHeader->e_lfanew); pOptHeader = (PIMAGE_OPTIONAL_HEADER)&(pNTHeaders->OptionalHeader);
pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)hMod + pOptHeader->DataDirectory[1].VirtualAddress);
while(pImportDescriptor->FirstThunk) { char * dllname = (char *)((BYTE *)hMod + pImportDescriptor->Name);
pThunkData = (PIMAGE_THUNK_DATA)((BYTE *)hMod + pImportDescriptor->OriginalFirstThunk);
int no = 1; while(pThunkData->u1.Function) { char * funname = (char *)((BYTE *)hMod + (DWORD)pThunkData->u1.AddressOfData + 2); PDWORD lpAddr = (DWORD *)((BYTE *)hMod + (DWORD)pImportDescriptor->FirstThunk) +(no-1); //修改內存的部分 if((*lpAddr) == (int)addr) { //修改內存頁的屬性 DWORD dwOLD; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(lpAddr,&mbi,sizeof(mbi)); VirtualProtect(lpAddr,sizeof(DWORD),PAGE_READWRITE,&dwOLD); WriteProcessMemory(GetCurrentProcess(), lpAddr, &myaddr, sizeof(DWORD), NULL); //恢復內存頁的屬性 VirtualProtect(lpAddr,sizeof(DWORD),dwOLD,0); } //--------- no++; pThunkData++; }
pImportDescriptor++; } //-------------------HOOK END----------------- }
//new messagebox function int WINAPI MessageBoxProxy(IN HWND hWnd, IN LPCSTR lpText, IN LPCSTR lpCaption, IN UINT uType) { return ((PFNMESSAGEBOX)addr)(NULL, "gxter_test", "gxter_title", 0); //這個地方可以寫出對這個API函數的處理代碼 }
//----------------------------test.cpp------------------------------------ // //write by Gxter //test.cpp
#include "stdio.h" #include "windows.h"
int main() { printf("test---\n"); while(1) { getchar(); MessageBoxA(NULL, "原函數", "09HookDemo", 0); } return 0; }
//---------------------------THE--END--------------------------------------
測試過程:先運行TEST不要關閉(建立目標),再運行install(進行注入)。但要注意FUNDLL和TEST文件位置。
上面的代碼進本上就實現了對其他進程的API進行HOOK了。 但還有一個問題就是對“API函數進行全局的HOOK”。我的想法就是注入所有進程就可以實現了。 只要簡單的改一下上面的代碼就可以實現了。 這好象有點像SetWindowsHookEx這個函數的的實現過程。 以上就是我想法了,如有錯誤還請斧正。
Gxter.安康 200501022
--[ 目錄
1 - Windows CE架構
2 - 列出所有系統API
3 - Windows CE的系統調用
4 - coredll.dll對API的包裹
5 - 用系統調用實現shellcode
6 - 小結
7 - 感謝
8 - 參考資料
--[ 1 - Windows CE架構
在
《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。
有
兩種類型的APIset,分別是固有的和基于句柄的。固有的API
sets注冊在全局表SystemAPISets中,可以以API句柄索引和方法索引的組合來調用他們的方法。基于句柄的API和內核對象相關,如文件、
互斥體、事件等。這些API的方法可以用一個對象的句柄和方法索引來調用。
kfuncs.h中定義了固有APIset的句柄索引,
如:SH_WIN32、SH_GDI、SH_WMGR等。基于句柄的API索引定義在PUBLIC\COMMON\OAK\INC\psyscall.h
中,如:HT_EVENT、HT_APISET、HT_SOCKET等。
SystemAPISets共有32個CINFO結構的APIset,通過遍歷SystemAPISets成員,可以列出系統所有API。其中CINFO的結構在PRIVATE\WINCEOS\COREOS\NK\INC\kernel.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 - 列出所有系統API
Dmitri Leman在他的cespy中有個DumpApis函數,略加修改后如下:
// 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 //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 ( (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: %02X acName: %.4s disp: %d type: %d cMethods: %d " "ppfnMethods: %08X pdwSig: %08X pServer: %08X %ls\n", 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: %08X sign %08X\n", j, l_pMethod, l_dwSign); } } }//for(int i = 0; i < NUM_SYSTEM_SETS; i++) } __except(1) { fprintf(fp, "Exception in DumpApis\n"); }
if(l_dwOldPermissions) { SetProcPermissions(l_dwOldPermissions); } fclose(fp);
return 0; }
來看一下此程序輸出的片斷:
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 ...
這
是最開始的一個APIset,它的ppfnMethods是0x8004B138,cMethods是185,根據這兩個數據得到185個地址,這些地址
實際上就是內核系統調用的實現地址。它們的索引相對PRIVATE\WINCEOS\COREOS\NK\KERNEL\kwin32.h里的
Win32Methods數組:
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的系統調用
Windows
CE沒有使用ARM處理器的SWI指令來實現系統調用,SWI指令在Windows CE里是空的,就簡單的執行了"movs
pc,lr"(詳見armtrap.s關于SWIHandler的實現)。Windows CE的系統調用使用了0xf0000000 -
0xf0010000的地址,當系統執行這些地址的時候將會觸發異常,產生一個PrefetchAbort的trap。在PrefetchAbort的實
現里(詳見armtrap.s)首先會檢查異常地址是否在系統調用trap區,如果不是,那么執行ProcessPrefAbort,否則執行
ObjectCall查找API地址來分派。
通過APIset和其API的索引可以算出系統調用地址,其公式
是:0xf0010000-(256*apiset+apinr)*4。比如對于SC_CreateAPISet的系統調用可以這樣算出
來:0xf0010000-(256*0+2)*4=0xF000FFF8。
--[ 4 - coredll.dll對API的包裹
選擇一個沒有參數的SetCleanRebootFlag()進行分析,IDAPro對其的反匯編如下:
.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
寫一個包含SetCleanRebootFlag()函數的小程序用EVC進行跟蹤調試,按F11進入該函數以后,程序首先取KDataStruct的lpvTls成員,然后取lpvTls偏移-0x14的內容,測試該內容是否是1。
得先來了解一下lpvTls偏移-0x14的數據是什么。先看PUBLIC\COMMON\OAK\INC\pkfuncs.h里的幾個定義:
#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
看來lpvTls偏移-0x14保存的是當前線程信息,只有第0比特被使用。再來看PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\mdram.c里的MDCreateMainThread2函數:
... if (kmode || bAllKMode) { pTh->ctx.Psr = KERNEL_MODE; KTHRDINFO (pTh) |= UTLS_INKMODE; } else { pTh->ctx.Psr = USER_MODE; KTHRDINFO (pTh) &= ~UTLS_INKMODE; } ...
KTHRDINFO (pTh)在PRIVATE\WINCEOS\COREOS\NK\INC\kernel.h里定義:
#define KTHRDINFO(pth) ((pth)->tlsPtr[PRETLS_THRDINFO])
它就是lpvTls偏移-0x14。也就是說系統在創建主線程的時候,根據程序當前的模式來設置KTHRDINFO的值,如果是內核模式,那么是1,否則是0。
回
到coredll.dll中SetCleanRebootFlag的實現,這時可以知道判斷lpvTls偏移-0x14的內容是為了檢查當前是否內核模
式。由于Pocket PC ROM編譯時使用了Enable Full Kernel
Mode選項,所以程序都是以內核模式運行。于是接著調試時可以看到取0x1FC6760的內容,取出來后,R0的值時0x8004B138,這個值正好
是DumpApis程序輸出的第一個APIset的ppfnMethods。接下來執行:
.text:01F74F90 LDRNE R1, [R0,#0x134] .text:01F74F94 LDREQ R1, =0xF000FECC
由
于程序是內核模式,所以前一條指令成功取出值,后一條無效。這時R1的值是0x80062AAC,和DumpApis程序輸出的一個地址匹配,根據索引,
發現這個地址是SC_SetCleanRebootFlag在內核中的實現。其實索引也可以根據這條指令的偏移來取:0x134/4=0x4D(77),
根據kwin32.h里Win32Methods的索引直接就對應出SC_SetCleanRebootFlag。內核模式的話,后面還會執行
SC_KillThreadIfNeeded。
如果是用戶模式的話,系統會執行0xF000FECC這個地址,這顯然是一個系統調用
trap地址。根據上面的公式算出索引值:(0xf0010000-0xF000FECC)/4=0x4D(77),根據kwin32.h里
Win32Methods的索引也對應出這是SC_SetCleanRebootFlag。
通過分析coredll.dll對API包裹的實現,可以發現Windows CE在調用一部分API的時候會先判斷程序是否處于內核模式,如果是,那么不用系統調用方式,直接奔內核實現地址去了,否則就老老實實的用系統調用地址。
--[ 5 - 用系統調用實現shellcode
系
統調用地址相對固定,可以通過索引算出它的trap地址,而且搜索coredll.dll里API地址的方法在用戶態是無法實現的,因為模塊鏈表是在內核
空間,用戶態無法訪問。下面就是用系統調用實現的簡單shellcode,它的作用是軟重啟系統,我想對于smartphone的系統應該也是可用
(smartphone的ROM在編譯時沒有用Enable Full Kernel Mode選項)。
#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 - 小結
通過本文可以了解到Windows CE API機制的大概輪廓,對于系統調用的具體流程,也就是trap后的具體流程還不是很清晰,本文也就一塊破磚頭,希望能砸到幾個人,可以一起討論;) 文中如有錯誤還望不吝賜教,希望Xcon''05見。
--[ 7 - 感謝
非常感謝Nasiry對我的幫助,在他的幫助下才得以完成此文。
近日,由于程序設計需要,我對WincowsCE 的 內存布局進行了研究,由于發現國內在這方面的文檔資料較少,于是在研究告一段落之際,形成這篇示例文檔,以望拋磚引玉,得到別的高手的指正。 一、程序實現的先決條件 由于windows系統的窗體消息總是投遞至一個特定進程的指定窗體消息函數中。于是在本地進程(自己的應用程序)中取得屬于其它進程的窗體的消息必須實現以下兩個部分: 1、將需要掛接窗體的代碼放到目標進程的地址空間中去。 2、執行這一段代碼,并獲得目標進程窗體的消息。 這兩步看起來很簡單,但在實現過程中就比較困難。由于Windows CE作為嵌入式移動設備 操作系統,與windows 98/2000/XP等桌面 操作系統在
內核的設計理念以及API的支持上有極大的區別。這就直接導致了常規的桌面系統利用全局鼠標鉤子注入/遠程線程注入等方法在CE中完全得不通。不過可喜的
是,微軟在開發工具中提供的remotexxx等遠程調試程序使我清楚這個目標并不是不可能的任務,微軟既然可以做到,那就是說在CE的內部一定有一套完
整的跨進程 內存訪問/代碼注入的機制。 二、程序實現的基本原理
經過兩天的google
搜索,在網上我發現了一個沒有在微軟文檔中聲明的有趣的API函數:PerformCallBack4,傳說中這個函數可以在自己的應用程序中執行指定的
進程中的一個函數,So Cool!這好象正是我所需要的東西。雖然網上也傳聞這個函數在wm5不受支持,其實經過實踐這個傳聞只是謠傳而已! PerformCallBack4函數的定義: [DllImport("coredll.dll")] public static extern uint PerformCallBack4(ref CallBackInfo CallBackInfo, IntPtr ni_pVoid1,IntPtr ni_pVoid2,IntPtr ni_pVoid3); 其中函數的參數CallBackInfo結構定義: [StructLayout(LayoutKind.Sequential)] public struct CallBackInfo { public IntPtr hProc; //遠程的目標進程 public IntPtr pfn; //指向遠程目標進程的函數地址的指針 public IntPtr pvArg0; //函數的需要的第一個參數 }//end struct 而PerformCallback4的 ni_pVoid1、ni_pVoid2、ni_pVoid3為傳遞到遠程目標進程執行函數的其它三個參數。 至于將代碼放到目標進程的 內存空間,我們可以利用CE設計上的一個特性: 1、為了節約 內存使用,CE將所有程序調用的動態鏈接庫(DLL)都映射到同一個 內存地址中。 2、CE的 內存布局中劃分有一個slot0的 內存位置,這個 內存位置是由正在執行的進程所占有的,每一個特定的時間片,只能有一個進程可以占有這個 內存空
間。在進程要求執行時,系統并不直接執行進程所處內存位置的代碼,而是將該進程的執行代碼復制到slot0的內存位置中產生一個副本執行。也就是說進程在
執行時內存將會有進程執行代碼的兩個完全一樣的版本:存在于slot0中正在執行的進程代碼和進程本身所處的內存中的代碼。 在這個特
性下,可以得到結論:如果進程A通過LoadLibrary函數裝載Test.dll,而進程B也通過LoadLibrary函數裝載同一個
Test.dll,這個Test.dll的所有函數在進程A和進程B中執行時,相對于slot0中的進程執行代碼都會得到同一地址。
3、在CE中,系統在內存中劃分出33個slot,slot0保留給正在執行的進程,然后在進程啟動時將所有的代碼放到除slot0以外的一個slot中
(這就是臭名昭著的CE系統中內存最多只能有不多于32個程序執行的限制的來由)。在進程執行時,每個應用程序的內存訪問默認只能訪問slot0內存空間
中的地址以及進程所處的slot內存空間的地址。
但為使設備驅動程序可以訪問到它們所需的其它應用程序數據,CE提供了兩個函數以打破這個限制,SetKmode和
SetProcPermission,SetKmode函數告訴系統,當前運行的進程是否需要在內核模式中執行;SetProcPermission函數
可以接受一個位掩碼,每一位代碼一個slot的訪問控制,1代表可以訪問該slot的內存內容。0表示不能訪問該slot的內存內容。這兩個函數在
msdn中有幫助文檔,可參閱msdn的文檔說明。 本文我們對實現的原理進行了剖析,在下一篇文章中我們將以一個小示例程序演示實現的全過程。
在文章《淺析Windows CE跨進程內存注入實現窗體消息掛接(上)》中,我們已經得到了這個七巧板游戲所需要的所有小板塊,剩下的事就是等待我們按一定順序將合適的板塊放到合適的位置,本章我們開始進行真刀真槍的實戰演練。
程序目標:捕獲explore窗體(也就是程序窗體的消息并輸出到WinProcInfo.txt中)
程序的執行步驟設計如下:
1、編寫一個窗體消息掛接DLL,這個DLL提供一個,函數中利用setwindowlong函數將窗體的默認消息處理過程改為這個掛接DLL中定義的一個窗體過程。
2、在C#程序中利用findwindow等API函數獲得exlore類窗體的句柄及窗體所屬的進程,并使用performcallback4在目標進程空間中執行coredll.dll的loadLibrary函數將我們寫的掛接dll放到目標進程中。
3、在C#程序中使用performcallback4在目標進程空間中執行掛接DLL提供的導出接口函數實現跨進程窗體消息截獲.
一、程序的實現如下:
在VS2005中建立一個智能設備的MFC DLL,命名為HookWindowsProcMFCDLL。
在HookWindowsProcMFCDLL.cpp中進行掛接DLL的核心編碼:
LRESULT CALLBACK fnHookWindowProc(HWND hwnd,UINT msg,WPARAM wparam, LPARAM lparam);
int __declspec(dllexport) WINAPI fnAttachWinProc(HWND ni_hAttatchWin,PVOID ,PVOID,PVOID);
int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin);
WNDPROC tpOldWindowProc;
FILE *m_pDebugOutputFile;
//將一個窗體消息處理掛接到net精簡版MessageWindow對象上的代碼 typedef struct { WNDPROC OldWinProc;//保留窗體原始消息處理過程的函數指針 HWND WindowHandle;//保存net精簡版中對應的窗口掛接的MessageWindow對象的句柄 } DEFUDT_AttachWinInfo; //end struct
CMap<HWND,HWND,DEFUDT_AttachWinInfo,DEFUDT_AttachWinInfo> m_aAttachWinInfoMap;
//對指定的窗口進程進行掛接 int __declspec(dllexport) WINAPI fnAttachWinProc(HWND ni_hAttatchWin, PVOID ni_0, PVOID ni_1, PVOID ni_2 ) { DEFUDT_AttachWinInfo tudtAttachWinInfo; m_pDebugOutputFile = fopen("\\Storage Card\\WinProcInfo.txt", "w"); WNDPROC tpOldWindowProc=(WNDPROC)::SetWindowLong(ni_hAttatchWin, GWL_WNDPROC,(LONG) fnHookWindowProc ); fprintf(m_pDebugOutputFile,"Attatch successfully! OldWindowProc: %08X\n",tpOldWindowProc); tudtAttachWinInfo.OldWinProc=tpOldWindowProc ; tudtAttachWinInfo.WindowHandle=ni_hAttatchWin; m_aAttachWinInfoMap.SetAt(ni_hAttatchWin,tudtAttachWinInfo); fclose(m_pDebugOutputFile); return 77;// (int)tpOldWindowProc ; }//end function
int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin) { DEFUDT_AttachWinInfo tudtAttachWinInfo; WNDPROC tpOldWindowProc;
//取得在ncf中消息接收窗口對應的原始消息處理函數的函數指針 m_aAttachWinInfoMap.Lookup(ni_hDetachWin,tudtAttachWinInfo) ;
//將窗體的消息處理函數設為默認的處理過程 tpOldWindowProc =(WNDPROC) SetWindowLong(ni_hDetachWin,GWL_WNDPROC , (LONG)tudtAttachWinInfo.OldWinProc);
//將掛接信息消息處理映謝類中刪除 m_aAttachWinInfoMap.RemoveKey(ni_hDetachWin);
return (int)tpOldWindowProc ;
}//end function
LRESULT CALLBACK fnHookWindowProc(HWND hwnd,UINT msg,WPARAM wparam, LPARAM lparam) { DEFUDT_AttachWinInfo tudtAttachWinInfo; m_aAttachWinInfoMap.Lookup(hwnd,tudtAttachWinInfo) ; m_pDebugOutputFile = fopen("\\Storage Card\\WinProcInfo.txt", "a"); if (m_pDebugOutputFile!=NULL) { fprintf(m_pDebugOutputFile,"HWND: %08X Msg: %08X Wparam %08X Lparam %08X \n", hwnd,msg,wparam,lparam);
}//EHD IF
fclose(m_pDebugOutputFile); //tudtAttachWin=maatt LRESULT tobjResult= ::CallWindowProc(tudtAttachWinInfo.OldWinProc ,hwnd,msg,wparam,lparam); return tobjResult; }//end function
|
而在C#的主程序中,我們使用這個DLL掛接explore類的程序窗體,以下給出掛接部分的代碼:
int m_hTargetWindow;//要掛接的目標窗體句柄 IntPtr m_hTargetProcess;//目標窗體所屬的進程 IntPtr m_hModule; //掛接DLL的句柄
private void Form1_Load(object sender, EventArgs e) { IntPtr tpTemp = IntPtr.Zero, tpTempa = IntPtr.Zero; uint tuntApiRet;
m_hTargetWindow = (int)clsCECoreAPI.FindWindow("Explore", null );//資源管理器 0x0013e800;
//掛接指定的進程窗體消息 IntPtr thCurrentProcess = clsCECoreAPI.GetCurrentProcess(); m_hTargetProcess=IntPtr.Zero ;// (IntPtr) (unchecked((int)0xedd84e4a)); tuntApiRet= clsCECoreAPI.GetWindowThreadProcessId(new IntPtr(unchecked((int) m_hTargetWindow)), ref m_hTargetProcess);
string tstrArgument; tstrArgument = "\\Program Files\\processinject\\HookWindowsProcMFCDLL.dll";// HookWindowsProcMFCDLL.dll"; IntPtr tpArg0;
int tintOriginalKMode = clsCECoreAPI.SetKMode(1); int tintOriginalProcPermission = (int)clsCECoreAPI.SetProcPermissions(0xffffffff);
IntPtr tpFuncProc = clsCECoreAPI.GetProcAddress(clsCECoreAPI.GetModuleHandle("coredll.dll"), "LoadLibraryW");
CallBackInfo tudtCALLBACKINFO;
tpArg0 = clsCECoreAPI.MapPtrToProcess(tstrArgument, thCurrentProcess);
tudtCALLBACKINFO.hProc = m_hTargetProcess;// Proc; tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess); tudtCALLBACKINFO.pvArg0 = tpArg0; m_hModule =new IntPtr(unchecked( (int) clsCECoreAPI.PerformCallBack4(ref tudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero ))); //clsCECoreAPI.Sleep(1000);
IntPtr thModule = clsCECoreAPI.LoadLibrary("HookWindowsProcMFCDLL.dll"); tpFuncProc = clsCECoreAPI.GetProcAddress(thModule, "fnAttachWinProc");
tpArg0 = (IntPtr) m_hTargetWindow;// clsCECoreAPI.MapPtrToProcess(ref thTargetWindow, thCurrentProcess);
tudtCALLBACKINFO.hProc = m_hTargetProcess; tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess); tudtCALLBACKINFO.pvArg0 = tpArg0 ; tuntApiRet = clsCECoreAPI.PerformCallBack4(ref tudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero ); //clsCECoreAPI.Sleep(5000); }
[DllImport("HookWindowsProcMFCDLL.dll")] public static extern int fnAttachWinProc(IntPtr ni_hAttatchWin);
[DllImport("HookWindowsProcMFCDLL.dll")] public static extern int fnDetachWinMsgProc(IntPtr ni_hDetachWin);
|
取消掛接的代碼根據上述代碼很容易就可以建立,不再細敘。
注:clsCECoreAPI的函數全是封裝的標準CE API,由于這些API在msdn 中都有詳細的文檔注釋,因篇幅所限,不再將代碼一一列舉.
在執行這個程序時,將模擬器的共享路徑設為PC機的桌面,這樣模擬器的storage card目錄就等同桌面了,點模擬器的開始菜單,選程序,你就可以看到explore窗體的消息都輸出到桌面的WinProcInfo.txt文件中了,運行結果如下:
目前本程序只在PPC2003/wm5 for PPC測試通過,由于smartphone系統在編譯時使用了和ppc系統不同的機制,內存運作不明,本程序在smartphone上無法正確運行,有好的建議的話請指教一二,謝謝.
可以使用下面的應用程序代碼測試這個driver,使用evc編譯。
#include <windows.h>
#include<Windev.h>
#include <stdio.h>
#include "objbase.h"
#include "initguid.h"
#include "foo.h"
//char data1[10];
int WinMain(void)
{
HANDLE hnd;
COPY_STRUCT cs[1];
int i;
//static char data1[10];
auto char data1[10];
auto char data2[10];
static char* p1,*p2;
//cs.pBuffer1 = (char *)malloc(10);
//cs.pBuffer2 = (char*)malloc(10);
//cs.nLen = 10;
p1 = (char *)LocalAlloc(LPTR,10);
p2 = (char *)malloc(10);
//cs[0].pBuffer1 = (char *)malloc(10);
//cs[0].pBuffer2 = (char*)malloc(10);
cs[0].pBuffer1 = &data1[0];
cs[0].pBuffer2 = &data2[0];
cs[0].nLen = 10;
memset(cs[0].pBuffer1,'a',10);
hnd = CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);
if(hnd==NULL)
{
printf("Open device falied!\n");
return;
}
DeviceIoControl(hnd,IOCTL_FOO_XER,&cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);
//for(i=0;i<9;i++)
//{
//printf(" %c",*(cs.pBuffer2++));
//}
printf("\n");
CloseHandle(hnd);
// free(cs[0].pBuffer1);
// free(cs[0].pBuffer2);
}
可以通過evc的單步調試看結果。好了一切都完成了,我們來看看系統是怎么工作的吧,從應用程序開始,
CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);
會調用到
FOO_Open(DWORD dwContext, DWORD AccessCode, DWORD ShareMode)
而FOO_DEV_NAME名字定義在foo.h里面。
#define FOO_DEV_NAME L"Foo1:"
注意后面是 1 ,這個是和注冊表的這一項匹配的
"Index"=dword:1
當調用CreateFile發生了什么,slot之間的轉換,一系列系統操作后,調用到我們自己的driver函數FOO_Open,在這個函數里我們返回了一個句柄,它可以用來存儲我們的自己driver的信息。在其它I/O操作中可以使用。
Driver什么時候加載的?在注冊表里,device manager會一個個的加載,會調用到FOO_Init函數。這個函數返回一個指針,在調用FOO_Open又傳回來了,這樣我們就可以實現初始化一些自己driver的東西。
接著一個重要的函數,
DeviceIoControl(hnd,IOCTL_FOO_XER,&cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);
調用到
FOO_IOControl
走到這里
case IOCTL_FOO_XER:
if((pInBuf==NULL))
{
SetLastError(ERROR_INVALID_PARAMETER);
break;
}
pcs = (COPY_STRUCT*)pInBuf;
__try{
pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess());
pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess());
DEBUG_OUT(1, (TEXT("+FOO_IOControl(0x%x,0x%x)\r\n"),pcs->pBuffer1,pcs->pBuffer2));
memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen);
bResult = TRUE;
}
__except(EXCEPTION_EXECUTE_HANDLER){
DEBUG_OUT(1,(TEXT("Exception:FOO_IOCTL\r\n")));
break;
}
break;
default:
break;
這里又很多東西要研究,
從應用程序傳來的參數有, control code,IOCTL_FOO_XER和一個重要的輸入參數&cs[0],它是一個指針。cs 是一個結構體,定義在FOO.H
typedef struct {
char* pBuffer1;
char* pBuffer2;
int nLen;
}COPY_STRUCT;
而且這個結構體里有兩個指針。
DeviceIoControl 傳過來的指針可以用嗎?它包含的兩個指針可以直接用嗎?
按照PB連接幫助文檔看,
The operating system (OS ) manages pointers passed directly as parameters. Drivers must map all pointers contained in structures. DeviceIoControl buffers are often structures that contain data, some of which might be pointers.
You
can map a pointer contained in a structure by calling MapPtrToProcess,
setting the first parameter to the pointer, and then setting the second
parameter to GetCallerProcess.
cs指針已經映射好了,但是它指向的結構里的指針我們需要自己使用MapPtrToProcess函數映射。
這也就是:
pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess());
pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess());
的由來,可是后面的代碼沒有使用pMap1,pMap2。而是直接使用:
memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen);
而且它還工作了,沒有出現exception。很奇怪。我第一次在一個家伙的代碼里看見這種情況,很吃驚,但是它工作的很好,是文檔出錯了?
我們來分析一下,看看應用程序的代碼:
COPY_STRUCT cs[1];
auto char data1[10];
auto char data2[10];
cs結構和data1,data2數組都是自動變量,存放在堆棧里。假設這個應用程序被加載到0x18000000位置的slot里,那么他們的地址都是0x18XXXXXX。不熟悉wince memory architecture的可以看看資料,了解一下slot。當調用了
DeviceIoControl,按照文檔的說法,cs指針得到了轉換,因為從應用程序的進程轉到了device.exe進程,而device進程又是當前的運行的進程,被映射到了slot0,系統負責轉換cs指針。而cs包含的pBuffer1和pBuffer2是沒有映射不能直接用的。
事實上,我們傳過來的指針根本就是不需要映射,因為他們都是0x18xxxxxx,在應用程序的slot里,所以只要device.exe有訪問應用程序的權限,就可以了。而這個權限,系統已經幫我們設置好了。
那什么情況下要自己映射呢?
如果應用程序在定義 data1和data2使用static關鍵字,或者使用LocalAlloc,HeapAlloc的時候,一定要自己映射cs里的指針。
在應用程序里這樣寫:
cs.pBuffer1 = (char *)malloc(10);
cs.pBuffer2 = (char*)malloc(10);
cs.nLen = 10;
如果不使用MapPtrToProcess完成映射,那就出現data abort exception.
為什么呢?
因為這些變量都是在堆里分配的,而當應用程序運行時,被映射到slot0,堆的地址也就是處于slot的范圍內,傳遞到device.exe后,device.exe被映射到了slot0,這個時候必須要將應用程序的指針映射回應用程序所在的slot。否則訪問的是device.exe的空間,會發生不可知道的結果。
驗證一下上面說的地址分配問題。
我們這樣定義
COPY_STRUCT cs[1];
static char data1[10]; 堆里
auto char data2[10]; 棧里
這樣賦值:
cs[0].pBuffer1 = &data1[0];
cs[0].pBuffer2 = &data2[0];
cs[0].nLen = 10;
調試信息:
cs[0].pBuffer1 = &data1[0];
180112D0 ldr r2, [pc, #0xD0]
180112D4 str r2, [sp, #0x10]
讀取&data1[0]使用的是PC作為基址,而此時的應用程序處于運行階段映射到slot0,那么pc也就在0~01ffffff范圍,我的調試結果是在0x000112D0+8,使用的是arm,流水線機制,當前指令地址+8才是pc值。
143: cs[0].pBuffer2 = &data2[0];
180112D8 add r0, sp, #0x20
180112DC str r0, [sp, #0x14]
讀取&data2[0]采用的是sp作為基址,sp在應用程序加載到slot的時候就確定了的。所以保持了在應用程序slot的值,處于0x18xxxxxx范圍。
我們看到因為wince的slot機制,我們有時候需要映射,有時候不需要。所以wince文檔說結構里的指針要映射。畢竟你不知道應用程序怎么寫。
當然,你可以根本不映射,只要把那個結構屏蔽調,寫一個STATIC LIBRARY給用戶使用,自己保證使用正確的地址分配就可以了。上面我說的那個家伙就是這么干的。
好了,接著
調用:
CloseHandle(hnd);
程序結束了,完成了一次簡單的拷貝。
這個框架完成了,driver的基本接口設計,強調了內存指針的使用問題。但是相對于一個真正的driver,還缺少點東西,就是訪問硬件的方法。下面繼續討論如何訪問硬件。
函數式編程 haskell F# 自動化測試管理 tcl/expect tcl/tk tcl 工具命令語言 嵌入式腳本
lua語言 Google Hacking 的實現以及應用
winver---------檢查windows版本 wmimgmt.msc----打開windows管理體系結構(wmi) wupdmgr--------windows更新程序 wscript--------windows腳本宿主設置 write----------寫字板 winmsd---------系統信息 wiaacmgr-------掃描儀和照相機向導 winchat--------xp自帶局域網聊天 mem.exe--------顯示內存使用情況 msconfig.exe---系統配置實用程序 mplayer2-------簡易widnows media player mspaint--------畫圖板 mstsc----------遠程桌面連接 mplayer2-------媒體播放機 magnify--------放大鏡實用程序 mmc------------打開控制臺 mobsync--------同步命令
--------------------------------------------------------------------------------
dxdiag---------檢查directx信息 drwtsn32------ 系統醫生 devmgmt.msc--- 設備管理器 dfrg.msc-------磁盤碎片整理程序 diskmgmt.msc---磁盤管理實用程序 dcomcnfg-------打開系統組件服務 ddeshare-------打開dde共享設置 dvdplay--------dvd播放器
--------------------------------------------------------------------------------
net stop messenger-----停止信使服務 net start messenger----開始信使服務 notepad--------打開記事本 nslookup-------網絡管理的工具向導 ntbackup-------系統備份和還原 narrator-------屏幕“講述人” ntmsmgr.msc----移動存儲管理器 ntmsoprq.msc---移動存儲管理員操作請求 netstat -an----(tc)命令檢查接口
--------------------------------------------------------------------------------
syncapp--------創建一個公文包 sysedit--------系統配置編輯器 sigverif-------文件簽名驗證程序 sndrec32-------錄音機 shrpubw--------創建共享文件夾 secpol.msc-----本地安全策略 syskey---------系統加密,一旦加密就不能解開,保護windows xp系統的雙重密碼 services.msc---本地服務設置 sndvol32-------音量控制程序 sfc.exe--------系統文件檢查器 sfc /scannow---windows文件保護
--------------------------------------------------------------------------------
tsshutdn-------60秒倒計時關機命令 tourstart------xp簡介(安裝完成后出現的漫游xp程序) taskmgr--------任務管理器
--------------------------------------------------------------------------------
eventvwr-------事件查看器 eudcedit-------造字程序 explorer-------打開資源管理器
--------------------------------------------------------------------------------
packager-------對象包裝程序 perfmon.msc----計算機性能監測程序 progman--------程序管理器
--------------------------------------------------------------------------------
regedit.exe----注冊表 rsop.msc-------組策略結果集 regedt32-------注冊表編輯器 rononce -p ----15秒關機 regsvr32 /u *.dll----停止dll文件運行 regsvr32 /u zipfldr.dll------取消zip支持
--------------------------------------------------------------------------------
cmd.exe--------cmd命令提示符 chkdsk.exe-----chkdsk磁盤檢查 certmgr.msc----證書管理實用程序 calc-----------啟動計算器 charmap--------啟動字符映射表 cliconfg-------sql server 客戶端網絡實用程序 clipbrd--------剪貼板查看器 conf-----------啟動netmeeting compmgmt.msc---計算機管理 cleanmgr-------垃圾整理 ciadv.msc------索引服務程序
--------------------------------------------------------------------------------
osk------------打開屏幕鍵盤 odbcad32-------odbc數據源管理器 oobe/msoobe /a----檢查xp是否激活 lusrmgr.msc----本機用戶和組 logoff---------注銷命令
--------------------------------------------------------------------------------
iexpress-------木馬捆綁工具,系統自帶
--------------------------------------------------------------------------------
nslookup-------ip地址偵測器
--------------------------------------------------------------------------------
fsmgmt.msc-----共享文件夾管理器
--------------------------------------------------------------------------------
utilman--------輔助工具管理器
--------------------------------------------------------------------------------
gpedit.msc-----組策略
第1步: 從官方主頁www.lua.org下載Lua源代碼,最新版本為5.1.2。 解壓之后找到“src”文件夾,這里面就是Lua了,不過還不能直接使用。
第2步: 使用任意ANSI C編譯器,在這里使用VS2005編譯LUA。具體步驟如下: 1、新建一個空的Console工程,在這里該工程名暫為“lua” 2、將src中的文件全部拷貝到該項目文件夾下 3、根據生成的文件來添加需要編譯的文件 4、使用Release配置來進行編譯
以下是各類生成文件的詳細說明:
動態庫編譯 定義def文件 或者
首先需要修改一下Lua.h頭文件。如下: /* mark for all API functions */ //#ifndef LUA_API //#define LUA_API extern //#endif #ifdef LUA502_EXPORTS // 根據自己的項目而定 #define LUA_API __declspec(dllexport) #else #define LUA_API __declspec(dllimport) #endif 然后用vc建立一個win32 dll,包含*.h和*.c文件(參考一),編譯即可。
靜態庫文件lua.lib
1、添加除了“lua.c”與“luac.c”以外的全部文件到工程 2、更改[項目屬性]->[配置屬性]->[常規]->[項目類型]為“靜態庫文件(.lib)”
解釋器lua.exe
1、添加除了“luac.c”以外的全部文件到工程 2、更改Release狀態下[項目屬性]->[配置屬性]->[常規]->[項目類型]為“應用程序(.exe)”
編譯器luac.exe
3.1、添加除了“lua.c”以外的全部文件到工程 3.2、更改Release狀態下[項目屬性]->[配置屬性]->[常規]->[項目類型]為“應用程序(.exe)” 注意:該文件生成時的名字為lua.exe,因為該項目名稱為“lua”。如果之前生成過解釋器lua.exe,則應該將解釋器移走之后再生成。
2、使用LUA
使用靜態庫lua.lib
1、新建一個空的console工程,并添加一個空的源文件 2、在該文件中添加以下代碼,并自行修改關于路徑的部分 //================================================================================================================ // Lua Test Object // C++ Source lua_test.cpp //================================================================================================================ //================================================================================================================ // Include Files //================================================================================================================ extern "C" { #include "D:\\My Documents\\Visual Studio 2005\\Projects\\lua\\lua\\lua.h" #include "D:\\My Documents\\Visual Studio 2005\\Projects\\lua\\lua\\lualib.h" #include "D:\\My Documents\\Visual Studio 2005\\Projects\\lua\\lua\\lauxlib.h" } //================================================================================================================ // Libraries //================================================================================================================ #pragma comment( lib ,"D:\\My Documents\\Visual Studio 2005\\Projects\\lua\\release\\lua.lib") //================================================================================================================ // Main Functions //================================================================================================================ int main( void) { return 1; } 3、如果編譯通過,表示靜態庫文件lua.lib配置成功。
使用解釋器lua.exe
1、設置系統環境變量 我的電腦->屬性->高級->環境變量(N )->系統變量->Path->在尾部添加 ;+lua.exe所在路徑,如 ;d:\My Documents\Visual Studio 2005\Projects\lua\release,重啟機器。 2、新建文本文件,輸入 print("Hello World"),退出將文件名改為 a.lua 3、在CMD中輸入 lua a.lua 4、如果出現字符串 Hellow World,表示解釋器lua.exe配置成功
使用編譯器luac.exe
1、設置系統環境變量 我的電腦->屬性->高級->環境變量( N )->系統變量->Path->在尾部添加 ;+lua.exe所在路徑,如 ;d:\My Documents\Visual Studio 2005\Projects\lua\release,重啟機器。 2、新建文本文件,輸入 print("Hello World"),退出將文件名改為 a.lua(可以直接使用之前的a.lua) 3、在CMD中輸入 luac a.lua 4、如果在a.lua所在的目錄下出現luac.out文件,表示解釋器luac.exe配置成功
在CPP中調用Jscript中的函數
在C++中調用Jscript的函數非常簡單,Windows提供了一個msscript.ocx的控件,利用這個控件可以直接操作Jscript: 執行一段Jscript腳本,或者調用指定的函數。我寫了一個簡單的例子:
l 導入msscript.ocx。下面這條指令會在項目目錄中生成msscript.tli和msscript.tlh兩個文件,里面有msscript.ocx中所有接口的描述和IID的定義。
#import "msscript.ocx" no_namespace
|
l 聲明一個對象。
CComPtr<IScriptControl> m_iScriptControl;
|
l 創建對象實例
if(SUCCEEDED(m_iScriptControl.CoCreateInstance(__uuidof(ScriptControl))))
|
l 設置語言等屬性。
m_iScriptControl->PutLanguage(L"JScript");
m_iScriptControl->PutAllowUI(VARIANT_FALSE);
|
l 加入Jscript代碼。
m_iScriptControl->AddCode(L"function test(str1, str2) { return str1 + \"-ok-\" + str2; }");
|
l 獲得函數,這里要說明的是GetItem的參數是1到n,而不是0到n-1。
CComPtr<IScriptProcedureCollection> aProcedureSet = m_iScriptControl->GetProcedures();
long n = aProcedureSet->GetCount();
CComPtr<IScriptProcedure> aProcedure = aProcedureSet->GetItem(_variant_t(n));
_bstr_t strFunction = aProcedure->GetName();
|
l 準備函數參數。
VARIANT va = {0};
va.vt = VT_BSTR;
n = 2;
SAFEARRAYBOUND bounds[1] = {0};
bounds[0].cElements = n;
SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, bounds);
long i = 0;
for(i = 0; i < n; i++)
{
va.bstrVal = SysAllocString(L"test");
SafeArrayPutElement(psa, &i, &va);
}
|
l 調用函數。
_variant_t Result = m_iScriptControl->Run(strFunction, &psa);
|
l 釋放參數。
for(i = 0; i < n; i++)
{
va.bstrVal = SysAllocString(L"test");
SafeArrayGetElement(psa, &i, &va);
SysFreeString(va.bstrVal);
}
SafeArrayDestroy(psa);
Windows Mobile 6為開發人員帶來的新特性 本次課程將為您介紹Windows Mobile 6提供的最新API,其中包括聲音,WISP(Windows Ink Services for Pen (WISP)),DLOCK,Home Screen和其它可供開發人員在Windows Mobile中使用的API。 Outlook DLock allows you to unlock locked attached files in Outlook XP. NETCFv2.wm.armv4i.cab ,System_SR_CHS_wm.cab
以下 API 通過 AYGShell 公開。
編程元素 說明 SHChangeNotifyDeregister 該函數禁用窗口接收文件更改通知的功能。 SHChangeNotifyFree 該函數清除文件更改通知。 SHChangeNotifyRegister 該函數登記用于接收更改通知的應用程序。 SHCloseApps 該函數嘗試為應用程序釋放內存。 SHCreateMenuBar 該函數在屏幕底部創建菜單欄。 SHCreateNewItem 該函數以編程方式創建新的項,好像該項是從全局 NEW 下拉菜單中選擇的。 SHDoneButton 該函數允許應用程序基于應用程序的狀態動態顯示或隱藏 OK 按鈕。 SHEnumPropSheetHandlers 該函數通過枚舉類項 hkey 下面的子項來支持屬性表擴展。 SHFindMenuBar 該函數用來獲得菜單欄窗口的句柄。 SHFreeContextMenuExtensions 該函數釋放為處理上下文菜單而分配的內存。 SHFullScreen 該函數用來接管某些屏幕區域。 SHGetAppKeyAssoc 該函數用于確定導航控件是否被映射到應用程序。 SHGetAutoRunPath 該函數搜索第一個存儲卡,并構造用來查找自動運行文件的路徑。 SHHandleWMActivate 該函數用來幫助管理輸入面板和您的應用程序。 SHHandleWMSettingChange 該函數用來幫助管理輸入面板和您的應用程序。 SHInitDialog 該函數調整對話框的大小,使其適合軟件輸入面板。 SHInitExtraControls 該函數調整對話框的大小,使其適合軟件輸入面板。 SHInputDialog 該函數用于處理輸入對話框。 SHInvokeContextMenuCommand 該函數調用上下文菜單中的命令。 SHLoadContextMenuExtensions 該函數從指定的上下文和類的配對所對應的注冊表中列出的處理程序加載上下文菜單擴展。 SHNotificationAdd 該函數將通知異步地添加到通知欄。 SHNotificationGetData 該函數獲得通知的數據。 SHNotificationRemove 該函數刪除通知。 SHNotificationUpdate 該函數更新掛起通知的某些方面的內容。 SHRecognizeGesture 該函數被自定義控件用來識別某些筆針的筆勢。 SHSetAppKeyWndAssoc 該函數指派某個窗口負責接收特定硬件按鍵的按鍵消息。 SHSetNavBarText 該函數設置任務欄中的標題文本。 SHSipInfo 該函數向外殼查詢與輸入面板和輸入方法有關的信息。 SHSipPreference 該函數為輸入面板請求位置。
通過provxml文件配置的,
這個文件必須要編到BIN檔里,如果修改文件名,bib也要相應修改。
platform.bib
|