??xml version="1.0" encoding="utf-8" standalone="yes"?> E序目标Q捕获exploreH体Q也是E序H体的消息ƈ输出到WinProcInfo.txt中) E序的执行步骤设计如下: 1、编写一个窗体消息挂接DLLQ这个DLL提供一个,函数中利用setwindowlong函数窗体的默认消息处理q程改ؓq个挂接DLL中定义的一个窗体过E?/p>
2、在C#E序中利用findwindow{API函数获得exlorecȝ体的句柄及窗体所属的q程Qƈ使用performcallback4在目标进E空间中执行coredll.dll的loadLibrary函数我们写的挂接dll攑ֈ目标q程中?/p>
3、在C#E序中用performcallback4在目标进E空间中执行挂接DLL提供的导出接口函数实现跨q程H体消息截获. 一、程序的实现如下Q?/strong> 在VS2005中徏立一个智能设备的MFC DLLQ命名ؓHookWindowsProcMFCDLL?/p>
在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对象上的代码 CMap<HWND,HWND,DEFUDT_AttachWinInfo,DEFUDT_AttachWinInfo> m_aAttachWinInfoMap; //Ҏ定的H口q程q行挂接 int __declspec(dllexport) WINAPI fnDetachWinMsgProc(HWND ni_hDetachWin) //取得在ncf中消息接收窗口对应的原始消息处理函数的函数指?br> m_aAttachWinInfoMap.Lookup(ni_hDetachWin,tudtAttachWinInfo) ; //窗体的消息处理函数设ؓ默认的处理过E?br> tpOldWindowProc =(WNDPROC) SetWindowLong(ni_hDetachWin,GWL_WNDPROC , (LONG)tudtAttachWinInfo.OldWinProc); //挂接信息消息处理映谢类中删?br> m_aAttachWinInfoMap.RemoveKey(ni_hDetachWin); return (int)tpOldWindowProc ; }//end function }//EHD IF fclose(m_pDebugOutputFile); 而在C#的主E序中,我们使用q个DLL挂接explorecȝE序H体Q以下给出挂接部分的代码Q?/p>
int m_hTargetWindow;//要挂接的目标H体句柄 private void Form1_Load(object sender, EventArgs e) m_hTargetWindow = (int)clsCECoreAPI.FindWindow("Explore", null );//资源理?0x0013e800; //挂接指定的进E窗体消?br> IntPtr thCurrentProcess = clsCECoreAPI.GetCurrentProcess(); string tstrArgument; int tintOriginalKMode = clsCECoreAPI.SetKMode(1); IntPtr tpFuncProc = clsCECoreAPI.GetProcAddress(clsCECoreAPI.GetModuleHandle("coredll.dll"), "LoadLibraryW"); CallBackInfo tudtCALLBACKINFO; tpArg0 = clsCECoreAPI.MapPtrToProcess(tstrArgument, thCurrentProcess); tudtCALLBACKINFO.hProc = m_hTargetProcess;// Proc; IntPtr thModule = clsCECoreAPI.LoadLibrary("HookWindowsProcMFCDLL.dll"); tpArg0 = (IntPtr) m_hTargetWindow;// clsCECoreAPI.MapPtrToProcess(ref thTargetWindow, thCurrentProcess); tudtCALLBACKINFO.hProc = m_hTargetProcess; [DllImport("HookWindowsProcMFCDLL.dll")] [DllImport("HookWindowsProcMFCDLL.dll")] 取消挂接的代码根据上qC码很Ҏ可以徏立,不再l叙?/p>
注:clsCECoreAPI的函数全是封装的标准CE APIQ由于这些API在msdn 中都有详l的文注释Q因幅所限,不再代码一一列D. 在执行这个程序时Q将模拟器的׃n路径设ؓPC机的桌面Q这h拟器的storage card目录q同桌面了Q点模拟器的开始菜单,选程序,你就可以看到exploreH体的消息都输出到桌面的WinProcInfo.txt文g中了Q运行结果如下:概要
如果您需要共享仅部分 DLL 数据QMicrosoft 创徏一个新的节和而共享它? 如果您想׃n的所?DLL 静态数据,非常重要做两件事情:
只有静态数据被׃n?用对作ؓ GlobalAlloc() ?malloc() q样?API / 函数的调用动态分配的内存是仍然特定于调用q程?
•
W一ơ,DLL 必须使用 C q行时的 DLL 版本 Q例?Crtdll.lib ?Msvcrt.libQ?请参阅您的品文更多有关?C q行?DLL 中?br>
注意Q?
Crtdll.lib 不再 SDKQ从 Windows NT 3.51 开始的一部分?上次发布q?4 ?1995 q上?MSDN 3.5
SDK?Win 32 现在要求用户指定的由他们自己的编译器 vender 提供?C q行?LIBs 他们自己的版本?
•
W二个,您需要指?data ?bss ׃n? 通常Q这?def 文g?SECTIONS"部分中?例如Q?
SECTIONS
如果您要使用 Visual C++ 32-bit EditionQ您必须指定此用链接器上的部分开兟?例如Q?
.bss READ WRITE SHARED
.data READ WRITE SHARED
link -section:.data,rws -section:.bss,rws
pȝ试图加蝲每个q程中相同的地址处共享的内存块?但是Q如果块不能加载到相同的内存地址Q系l将׃n的分区映到一个不同的内存地址?仍在׃n内存?h意该׃n节内部指针无效在q种情况下ƈ不能攑օ享各节中? 更多信息
您要同时指定.data ?bss 为共享,因ؓ它们每个保存不同cd的数据?.data 部分包含初始化的数据Q?bss 部分保存未初始化的数据?
for sharing in DLL all data one reason is to have in between Win32 DLL
(running on Windows NT) and Win32s consistent behavior (running on
Windows 3.1). when running on Win32sQ?2-bit DLL shares among all of
that use DLL processes its data?
h意不需要共享所有数?Win 32 ?Win32s 之间的行为完全相同?DLL 可用于将变量存储为实例数据在 Win 32 U程本地存储 (TLS)?br>
for additional informationQplease see following article in Microsoft Knowledge Base:
q篇文章中的信息适用?
•
Microsoft Win32 Application Programming Interface 当用?/td>
Microsoft Windows NT 4.0
Microsoft Windows NT 3.51 Service Pack 5
Microsoft Windows NT 4.0
Microsoft Windows 95
Microsoft Windows 98 Standard Edition
the operating system: Microsoft Windows 2000
the operating system: Microsoft Windows XP
关键字:
kbmt kbdll kbhowto kbipc kbkernbase KB109619 KbMtzh
]]>
1 - Windows CE架构
2 - 列出所有系lAPI
3 - Windows CE的系l调?br>
4 - coredll.dll对API的包?br>
5 - 用系l调用实现shellcode
6 - 结
7 - 感谢
8 - 参考资?br>
--[ 1 - Windows CE架构
?
《Windows
CE初探》一文中已经介绍了KDataStruct的结构,q是一个非帔R要的数据l构Q可以从用户态的应用E序讉K。其开始地址是固定的
PUserKDataQ在SDK中定义:Windows CE Tools\wce420\POCKET PC
2003\Include\Armv4\kfuncs.hQ,对于ARM处理器是0xFFFFC800Q而其它处理器?x00005800。偏U?
KINFO_OFFSET是UserKInfo数组Q里面保存了重要的系l数据,比如模块链表、内核堆、APIset
pointers表(SystemAPISetsQ。《Windows
CE初探》一文中通过模块链表最l来搜烦API在coredll中的地址Q本文我们将讨论一下UserKInfo[KINX_APISETS]处的
APIset pointers表?br>
Windows CE的API机制使用了PSLs(protected server libraries)Q是一U客L/服务端模式。PSLs象DLL一样处理导出服务,服务的导出通过注册APIset?br>
?
两种cd的APIsetQ分别是固有的和Z句柄的。固有的API
sets注册在全局表SystemAPISets中,可以以API句柄索引和方法烦引的l合来调用他们的Ҏ。基于句柄的API和内核对象相养I如文件?
互斥体、事件等。这些API的方法可以用一个对象的句柄和方法烦引来调用?br>
kfuncs.h中定义了固有APIset的句柄烦引,
如:SH_WIN32、SH_GDI、SH_WMGR{。基于句柄的API索引定义在PUBLIC\COMMON\OAK\INC\psyscall.h
中,如:HT_EVENT、HT_APISET、HT_SOCKET{?br>
SystemAPISets共有32个CINFOl构的APIsetQ通过遍历SystemAPISets成员Q可以列出系l所有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 - 列出所有系lAPI
Dmitri Leman在他的cespy中有个DumpApis函数Q略加修改后如下Q?br>
// 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;
}
来看一下此E序输出的片断:
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
...
q?
是最开始的一个APIsetQ它的ppfnMethods?x8004B138QcMethods?85Q根据这两个数据得到185个地址Q这些地址
实际上就是内核系l调用的实现地址。它们的索引相对PRIVATE\WINCEOS\COREOS\NK\KERNEL\kwin32.h里的
Win32Methods数组Q?br>
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的系l调?br>
Windows
CE没有使用ARM处理器的SWI指o来实现系l调用,SWI指o在Windows CE里是I的Q就单的执行?movs
pc,lr"Q详见armtrap.s关于SWIHandler的实玎ͼ。Windows CE的系l调用用了0xf0000000 -
0xf0010000的地址Q当pȝ执行q些地址的时候将会触发异常,产生一个PrefetchAbort的trap。在PrefetchAbort的实
现里Q详见armtrap.sQ首先会查异常地址是否在系l调用trap区,如果不是Q那么执行ProcessPrefAbortQ否则执?
ObjectCall查找API地址来分z?br>
通过APIset和其API的烦引可以算出系l调用地址Q其公式
是:0xf0010000-(256*apiset+apinr)*4。比如对于SC_CreateAPISet的系l调用可以这L?
来:0xf0010000-(256*0+2)*4=0xF000FFF8?br>
--[ 4 - coredll.dll对API的包?br>
选择一个没有参数的SetCleanRebootFlag()q行分析QIDAPro对其的反汇编如下Q?br>
.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()函数的小E序用EVCq行跟踪调试Q按F11q入该函C后,E序首先取KDataStruct的lpvTls成员Q然后取lpvTls偏移-0x14的内容,试该内Ҏ否是1?br>
得先来了解一下lpvTls偏移-0x14的数据是什么。先看PUBLIC\COMMON\OAK\INC\pkfuncs.h里的几个定义Q?br>
#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保存的是当前U程信息Q只有第0比特被用。再来看PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\mdram.c里的MDCreateMainThread2函数Q?br>
...
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。也是说系l在创徏ȝE的时候,ҎE序当前的模式来讄KTHRDINFO的|如果是内核模式,那么?Q否则是0?br>
?
到coredll.dll中SetCleanRebootFlag的实玎ͼq时可以知道判断lpvTls偏移-0x14的内ҎZ查当前是否内核模
式。由于Pocket PC ROM~译时用了Enable Full Kernel
Mode选项Q所以程序都是以内核模式q行。于是接着调试时可以看到取0x1FC6760的内容,取出来后QR0的值时0x8004B138Q这个值正?
是DumpApisE序输出的第一个APIset的ppfnMethods。接下来执行Q?br>
.text:01F74F90 LDRNE R1, [R0,#0x134]
.text:01F74F94 LDREQ R1, =0xF000FECC
?
于程序是内核模式Q所以前一条指令成功取出|后一条无效。这时R1的值是0x80062AACQ和DumpApisE序输出的一个地址匚wQ根据烦引,
发现q个地址是SC_SetCleanRebootFlag在内怸的实现。其实烦引也可以Ҏq条指o的偏UL取:0x134/4=0x4D(77)Q?
Ҏkwin32.h里Win32Methods的烦引直接就对应出SC_SetCleanRebootFlag。内核模式的话,后面q会执行
SC_KillThreadIfNeeded?br>
如果是用h式的话,pȝ会执?xF000FECCq个地址Q这昄是一个系l调?
trap地址。根据上面的公式出索引|(0xf0010000-0xF000FECC)/4=0x4D(77)Q根据kwin32.h?
Win32Methods的烦引也对应是SC_SetCleanRebootFlag?br>
通过分析coredll.dll对API包裹的实玎ͼ可以发现Windows CE在调用一部分API的时候会先判断程序是否处于内核模式,如果是,那么不用pȝ调用方式Q直接奔内核实现地址MQ否则就老老实实的用系l调用地址?br>
--[ 5 - 用系l调用实现shellcode
p?
l调用地址相对固定Q可以通过索引出它的trap地址Q而且搜烦coredll.dll里API地址的方法在用户态是无法实现的,因ؓ模块链表是在内核
I间Q用h无法访问。下面就是用pȝ调用实现的简单shellcodeQ它的作用是软重启系l,我想对于smartphone的系l应该也是可?
Qsmartphone的ROM在编译时没有用Enable Full Kernel Mode选项Q?br>
#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机制的大概轮廓,对于pȝ调用的具体流E,也就是trap后的具体程q不是很清晰Q本文也׃块破砖头Q希望能砸到几个人,可以一赯论;Q?br>文中如有错误q望不吝赐教Q希望Xcon''05见?br>
--[ 7 - 感谢
非常感谢NasiryҎ的帮助,在他的帮助下才得以完成此文?img src ="http://www.shnenglu.com/zjj2816/aggbug/58841.html" width = "1" height = "1" />
]]>
一、程序实现的先决条g
׃windowspȝ的窗体消息L投递至一个特定进E的指定H体消息函数中。于是在本地q程Q自q应用E序Q中取得属于其它q程的窗体的消息必须实现以下两个部分Q?br>
1、将需要挂接窗体的代码攑ֈ目标q程的地址I间中去?br>
2、执行这一D代码,q获得目标进E窗体的消息?br>
q两步看h很简单,但在实现q程中就比较困难。由于Windows CE作ؓ嵌入式移动设?a target="_blank" class="channel_keylink">操作pȝQ与windows 98/2000/XP{桌?a target="_blank" class="channel_keylink">操作pȝ?
内核的设计理念以及API的支持上有极大的区别。这q接导致了常规的桌面系l利用全局鼠标钩子注入/q程U程注入{方法在CE中完全得不通。不q可喜的
是,微Y在开发工具中提供的remotexxx{远E调试程序我清楚这个目标ƈ不是不可能的dQ微软既然可以做刎ͼ那就是说在CE的内部一定有一套完
整的跨进E?a target="_blank" class="channel_keylink">内存讉K/代码注入的机制?br>
二、程序实现的基本原理
l过两天的google
搜烦Q在|上我发C一个没有在微Y文中声明的有趣的API函数QPerformCallBack4Q传说中q个函数可以在自q应用E序中执行指定的
q程中的一个函敎ͼSo Cool!q好象正是我所需要的东西。虽然网上也传闻q个函数在wm5不受支持Q其实经q实践这个传d是谣传而已Q?br>
PerformCallBack4函数的定义:
[DllImport("coredll.dll")]
public static extern uint PerformCallBack4(ref CallBackInfo CallBackInfo,
IntPtr ni_pVoid1,IntPtr ni_pVoid2,IntPtr ni_pVoid3);
其中函数的参数CallBackInfol构定义:
[StructLayout(LayoutKind.Sequential)]
public struct CallBackInfo
{
public IntPtr hProc; //q程的目标进E?br>public IntPtr pfn; //指向q程目标q程的函数地址的指?br>public IntPtr pvArg0; //函数的需要的W一个参?br>}//end struct
而PerformCallback4?ni_pVoid1、ni_pVoid2、ni_pVoid3Z递到q程目标q程执行函数的其它三个参数?br>
至于代码放到目标进E的内存I间Q我们可以利用CE设计上的一个特性:
1、ؓ了节U?a target="_blank" class="channel_keylink">内存使用QCE所有程序调用的动态链接库QDLLQ都映射到同一?a target="_blank" class="channel_keylink">内存地址中?br>
2、CE?a target="_blank" class="channel_keylink">内存布局中划分有一个slot0?a target="_blank" class="channel_keylink">内存位置Q这?a target="_blank" class="channel_keylink">内存位置是由正在执行的进E所占有的,每一个特定的旉片,只能有一个进E可以占有这?a target="_blank" class="channel_keylink">内存I?
间。在q程要求执行Ӟpȝq不直接执行q程所处内存位|的代码Q而是该q程的执行代码复制到slot0的内存位|中产生一个副本执行。也是说进E在
执行时内存将会有q程执行代码的两个完全一L版本Q存在于slot0中正在执行的q程代码和进E本w所处的内存中的代码?br>
在这个特
性下Q可以得到结论:如果q程A通过LoadLibrary函数装蝲Test.dllQ而进EB也通过LoadLibrary函数装蝲同一?
Test.dllQ这个Test.dll的所有函数在q程A和进EB中执行时Q相对于slot0中的q程执行代码都会得到同一地址?br>
3、在CE中,pȝ在内存中划分?3个slotQslot0保留l正在执行的q程Q然后在q程启动时将所有的代码攑ֈ除slot0以外的一个slot?
Q这是臭名昭著的CEpȝ中内存最多只能有不多?2个程序执行的限制的来由)。在q程执行Ӟ每个应用E序的内存访问默认只能访问slot0内存I间
中的地址以及q程所处的slot内存I间的地址?
但ؓ使设备驱动程序可以访问到它们所需的其它应用程序数据,CE提供了两个函C打破q个限制QSetKmode?
SetProcPermissionQSetKmode函数告诉pȝQ当前运行的q程是否需要在内核模式中执行;SetProcPermission函数
可以接受一个位掩码Q每一位代码一个slot的访问控Ӟ1代表可以讉K该slot的内存内宏V?表示不能讉K该slot的内存内宏V这两个函数?
msdn中有帮助文Q可参阅msdn的文档说明?br>
本文我们对实现的原理q行了剖析,在下一文章中我们以一个小CZE序演示实现的全q程?
在文章《浅析Windows CE跨进E内存注入实现窗体消息挂接(上)》中Q我们已l得Cq个七y板游戏所需要的所有小板块Q剩下的事就是等待我们按一定顺序将合适的板块攑ֈ合适的位置Q本章我们开始进行真刀真枪的实战演l?/p>
typedef struct
{
WNDPROC OldWinProc;//保留H体原始消息处理q程的函数指?br> HWND WindowHandle;//保存net_版中对应的窗口挂接的MessageWindow对象的句?br>} DEFUDT_AttachWinInfo; //end struct
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
{
DEFUDT_AttachWinInfo tudtAttachWinInfo;
WNDPROC tpOldWindowProc;
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);
//tudtAttachWin=maatt
LRESULT tobjResult= ::CallWindowProc(tudtAttachWinInfo.OldWinProc ,hwnd,msg,wparam,lparam);
return tobjResult;
}//end function
IntPtr m_hTargetProcess;//目标H体所属的q程
IntPtr m_hModule; //挂接DLL的句?/p>
{
IntPtr tpTemp = IntPtr.Zero, tpTempa = IntPtr.Zero;
uint tuntApiRet;
m_hTargetProcess=IntPtr.Zero ;// (IntPtr) (unchecked((int)0xedd84e4a));
tuntApiRet= clsCECoreAPI.GetWindowThreadProcessId(new IntPtr(unchecked((int) m_hTargetWindow)),
ref m_hTargetProcess);
tstrArgument = "\\Program Files\\processinject\\HookWindowsProcMFCDLL.dll";// HookWindowsProcMFCDLL.dll";
IntPtr tpArg0;
int tintOriginalProcPermission = (int)clsCECoreAPI.SetProcPermissions(0xffffffff);
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);
tpFuncProc = clsCECoreAPI.GetProcAddress(thModule, "fnAttachWinProc");
tudtCALLBACKINFO.pfn = clsCECoreAPI.MapPtrToProcess(tpFuncProc, m_hTargetProcess);
tudtCALLBACKINFO.pvArg0 = tpArg0 ;
tuntApiRet = clsCECoreAPI.PerformCallBack4(ref tudtCALLBACKINFO,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero );
//clsCECoreAPI.Sleep(5000);
}
public static extern int fnAttachWinProc(IntPtr ni_hAttatchWin);
public static extern int fnDetachWinMsgProc(IntPtr ni_hDetachWin);
目前本程序只在PPC2003/wm5 for PPC试通过,׃smartphonepȝ在编译时使用了和ppcpȝ不同的机Ӟ内存q作不明Q本E序在smartphone上无法正运行,有好的徏议的话请指教一?谢谢.
]]>
#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的单步调试看l果。好了一切都完成了,我们来看看系l是怎么工作的吧Q从应用E序开始,
CreateFile(FOO_DEV_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,0,0,NULL);
会调用到
FOO_Open(DWORD dwContext, DWORD AccessCode, DWORD ShareMode)
?/span>FOO_DEV_NAME名字定义?/span>foo.h里面?/span>
#define FOO_DEV_NAME L"Foo1:"
注意后面?/span> 1 ,q个是和注册表的q一匹配的
"Index"=dword:1
当调?/span>CreateFile发生了什么,slot之间的{换,一pdpȝ操作后,调用到我们自qdriver函数FOO_OpenQ在q个函数里我们返回了一个句柄,它可以用来存储我们的自己driver的信息。在其它I/O操作中可以用?/span>
Driver什么时候加载的Q在注册表里Q?/span>device manager会一个个的加载,会调用到FOO_Init函数。这个函数返回一个指针,在调?/span>FOO_Open又传回来了,q样我们可以实现初始化一些自?/span>driver的东ѝ?/span>
接着一个重要的函数Q?/font>
DeviceIoControl(hnd,IOCTL_FOO_XER,&cs[0],sizeof(COPY_STRUCT),NULL,0,NULL,NULL);
调用?/font>
FOO_IOControl
走到q里
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;
q里又很多东西要研究Q?/font>
从应用程序传来的参数有, control codeQ?/span>IOCTL_FOO_XER和一个重要的输入参数&cs[0],它是一个指针?/span>cs 是一个结构体Q定义在FOO.H
typedef struct {
char* pBuffer1;
char* pBuffer2;
int nLen;
}COPY_STRUCT;
而且q个l构体里有两个指针?/font>
DeviceIoControl 传过来的指针可以用吗Q它包含的两个指针可以直接用吗?
按照PBq接帮助文看,
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指针已经映射好了Q但是它指向的结构里的指针我们需要自׃?/span>MapPtrToProcess函数映射?span lang="EN-US">
q也是Q?span lang="EN-US">
pMap1 = MapPtrToProcess(pcs->pBuffer1,GetCallerProcess());
pMap2 = MapPtrToProcess(pcs->pBuffer2,GetCallerProcess());
的由来,可是后面的代码没有?span lang="EN-US">pMap1Q?span lang="EN-US">pMap2。而是直接使用Q?span lang="EN-US">
memcpy(pcs->pBuffer2,pcs->pBuffer1,pcs->nLen);
而且它还工作了,没有出现exception。很奇怪。我W一ơ在一个家伙的代码里看见这U情况,很吃惊,但是它工作的很好Q是文出错了?
我们来分析一下,看看应用E序的代码:
COPY_STRUCT cs[1];
auto char data1[10];
auto char data2[10];
csl构?span lang="EN-US">data1Q?span lang="EN-US">data2数组都是自动变量Q存攑֜堆栈里。假设这个应用程序被加蝲?span lang="EN-US">0x18000000位置?span lang="EN-US">slot里,那么他们的地址都是0x18XXXXXX。不熟悉wince memory architecture的可以看看资料,了解一?span lang="EN-US">slot。当调用?span lang="EN-US">
DeviceIoControlQ按照文档的说法Q?/span>cs指针得到了{换,因ؓ从应用程序的q程转到?/span>device.exeq程Q?/span>deviceq程又是当前的运行的q程Q被映射Cslot0Q系l负责{?/span>cs指针。?/span>cs包含?/span>pBuffer1?/span>pBuffer2是没有映不能直接用的?/span>
事实上,我们传过来的指针Ҏ是不需要映,因ؓ他们都是0x18xxxxxx,在应用程序的slot里,所以只?span lang="EN-US">device.exe有访问应用程序的权限Q就可以了。而这个权限,pȝ已经帮我们设|好了?span lang="EN-US">
那什么情况下要自己映呢Q?span lang="EN-US">
如果应用E序在定?span lang="EN-US"> data1?span lang="EN-US">data2使用static关键字,或者?span lang="EN-US">LocalAllocQ?span lang="EN-US">HeapAlloc的时候,一定要自己映射cs里的指针?span lang="EN-US">
在应用程序里q样写:
cs.pBuffer1 = (char *)malloc(10);
cs.pBuffer2 = (char*)malloc(10);
cs.nLen = 10;
如果不?/span>MapPtrToProcess完成映射Q那出?span lang="EN-US">data abort exception.
Z么呢Q?span lang="EN-US">
因ؓq些变量都是在堆里分配的Q而当应用E序q行Ӟ被映到slot0Q堆的地址也就是处?span lang="EN-US">slot的范围内Q传递到device.exe后,device.exe被映到?span lang="EN-US">slot0Q这个时候必要应用程序的指针映射回应用程序所在的slot。否则访问的?span lang="EN-US">device.exe的空_会发生不可知道的l果?span lang="EN-US">
验证一下上面说的地址分配问题?span lang="EN-US">
我们q样定义
COPY_STRUCT cs[1];
static char data1[10]; 堆里
auto char data2[10]; 栈里
q样赋|
cs[0].pBuffer1 = &data1[0];
cs[0].pBuffer2 = &data2[0];
cs[0].nLen = 10;
调试信息Q?span lang="EN-US">
cs[0].pBuffer1 = &data1[0];
180112D0 ldr r2, [pc, #0xD0]
180112D4 str r2, [sp, #0x10]
d&data1[0]使用的是PC作ؓ基址Q而此时的应用E序处于q行阶段映射?span lang="EN-US">slot0Q那?span lang="EN-US">pc也就?span lang="EN-US">0~01ffffff范围Q我的调试结果是?span lang="EN-US">0x000112D0+8,使用的是armQ流水线机制Q当前指令地址Q?span lang="EN-US">8才是pc倹{?span lang="EN-US">
143: cs[0].pBuffer2 = &data2[0];
180112D8 add r0, sp, #0x20
180112DC str r0, [sp, #0x14]
d&data2[0]采用的是sp作ؓ基址Q?span lang="EN-US">sp在应用程序加载到slot的时候就定了的。所以保持了在应用程?span lang="EN-US">slot的|处于0x18xxxxxx范围?span lang="EN-US">
我们看到因ؓwince?span lang="EN-US">slot机制Q我们有时候需要映,有时候不需要。所?span lang="EN-US">wince文档说结构里的指针要映射。毕竟你不知道应用程序怎么写?span lang="EN-US">
当然Q你可以Ҏ不映,只要把那个结构屏蔽调Q写一?span lang="EN-US">STATIC LIBRARYl用户用,自己保证使用正确的地址分配可以了。上面我说的那个家伙是q么q的?span lang="EN-US">
好了Q接着
调用Q?span lang="EN-US">
CloseHandle(hnd);
E序l束了,完成了一ơ简单的拯?span lang="EN-US">
每个q程都拥有自q虚拟地址I间Q那么怎样才能讉Kq个I间呢?q就需要用到Windows API函数。这些函数直接与~写E序相关Q因而更受Y件工E师的关注。有兌斚w的函数较多,q里介绍几个重要的函数?/p>
在一个程序中不能直接应用某个pȝ的设备参敎ͼ否则不利于E序的移植。因此,如果实需要用到这L讑֤参数Q则需要一个系l信息函数来获得。VC++ ~译器所提供q样的函CؓGetSystemInfo()。该函数需要一个指向SYSTEM_INFOl构的指针作为参数。其原型表示为:
l
void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
l
其中lpSystemInfoq回LPSYSTEM_INFOl构的地址Q用于装载适当的系l信息,q个l构体定义ؓQ?/p>
l
typedef struct _SYSTEM_INFO {
union {
DWORD dwOemId;
struct {
WORD wProcessorArchitecture;
WORD wReserved;
};
};
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO;
l
其中参数含义如下所q?/p>
dwOemIdQ是一个过旉项Q用于与Windows NT 3.5以及以前的版本兼宏V?/p>
wProcessorArchitectureQ指明处理的l构Q如Intel、Alpha、Intel 64位或Alpha 64位?/p>
dwPageSizeQ用于显CCPU的页面大。在x86 CPU上,q个值是4096字节。在Alpha CPU上,q个值是8192字节。在IA-64上,q个值是8192字节?/p>
lpMinimumApplicationAddressQ用于给出每个进E可用地址I间的最内存地址。在Windows 98上,q个值是0x400000Q因为每个进E的地址I间中下面的4MB是不能用的。在Windows 2K/XP上,q个值是0x10000Q因为每个进E的地址I间中开头的64KBLI闲的?/p>
lpMaximumApplicationAddressQ用于给出每个进E可用地址I间的最大内存地址。在Windows 98上,q个地址?x7FFFFFFFQ因为共享内存映文件区域和׃n操作pȝ代码包含在上面的2GB分区中。在Windows XP上,q个地址?x7FFEFFFF?/p>
dwActiveProcessorMaskQ位屏蔽Q指明哪个CPU是活动的?/p>
dwNumberOfProcessorsQ计机中CPU的数目?/p>
dwProcessorTypeQ处理器cd?/p>
dwAllocationGranularityQ保留的地址I间区域的分配粒度?/p>
wProcessorLevelQ进一步细分处理器的结构?/p>
wProcessorRevisionQ用于进一步细分处理器的别?/p>
wReservedQ保留供来使用?/p>
在以上参C只有lpMinimumApplicationAddress、lpMaximumApplicationAddress、dwPageSize和dwAllocationGranularity与内存有兟?/p>
对内存分配可以采用不同的ҎQ常用的Ҏ有:用C/C++语言的内存分配函敎ͼ例如Q用malloc() ?free()、new ?delete 函数分配和释攑֠内存Q用Windows传统的全局或者局部内存分配函敎ͼ如GlobalAlloc()和GlobalFree()Q用Win32的堆分配函数Q如HeapAlloc()和HeapFree()Q用Win32的虚拟内存分配函敎ͼ如VirtualAlloc()和VirtualFree()。注意,用不同的Ҏ分配内存后,要用相对应的函数来释放所占用的内存。这里只介绍Win32的虚拟内存分配函数?/p>
在进E创Z初ƈ被赋予地址I间Ӟ其虚拟地址I间未分配Q处于空闲状态。这时地址I间内的内存是不能用的Q必通过VirtualAlloc()函数来分配其中的各个区域Q对其进行保留。VirtualAlloc()函数原型为:
l
LPVOID VirtualAlloc(
LPVOID lpAddress,
DWORD dwSize,
DWORD flAllocationType,
DWORD flProtect
);
l
该函数用来分配一定范围的虚拟c参?指定起始地址Q参?指定分配内存的长度;参数3指定分配方式Q取值MEM_COMMINT或者MEM_RESERVEQ参?指定控制讉K本次分配的内存的标识Q取gؓPAGE_READONLY、PAGE_READWRITE或者PAGE_NOACCESS?/p>
分配完成后,卛_q程的虚拟地址I间中保留了一个区域,可以Ҏ区域中的内存q行保护权限许可范围内的讉K。当不再需要访问此地址I间区域Ӟ应释放此区域Q由VirtualFree()负责完成。其函数原型为:
l
BOOL VirtualFree(
LPVOID lpAddress,
DWORD dwSize,
DWORD dwFreeType
);
l
其中参数含义如下所q?/p>
lpAddressQ指向待释放面区域的指针。如果参数dwFreeType指定了MEM_RELEASEQ则lpAddress必须为页面区域保留由VirtualAlloc()所q回的基地址?/p>
dwSizeQ指定了要释攄地址I间区域的大,如果参数dwFreeType指定了MEM_RELEASE标志Q则dwSize讄?Q由pȝ计算在特定内存地址上的待释攑域的大小?/p>
dwFreeTypeQؓ所执行的释放操作的cdQ其可能的取gؓMEM_RELEASE和MEM_DECOMMITQ其中MEM_RELEASE标志指明要释放指定的保留面区域QMEM_DECOMMIT标志则对指定的占用页面区域进行占用的解除?/p>
如果VirtualFree()执行完成Q将回收全部范围的已分配面Q此后如再对q些已释 N面区域内存进行访问将引发内存讉K异常。释攑的页面区域可供系ll分?nbsp; 使用?/p>
Windows API函数GlobalMemoryStatus()可用于检索关于当前内存状态的动态信息。在软g的About对话框中Q通常用这个函数来获取pȝ内存的用情c其函数原型为:
l
void GlobalMemoryStatus(LPMEMORYSTATUS lpmstMemStat);
l
其中lpmstMemStatq回MEMORYSTATUSl构的地址Q这个结构体的定义ؓQ?/p>
l
typedef struct MEMORYSTATUS{
DWORD dwLength;
DWORD dwMemoryLoad;
DWORD dwTotalPhys;
DWORD dwAvailPhys;
DWORD dwTotalPageFile;
DWORD dwAvailPageFile;
DWORD dwTotalVirtual;
DWORD dwAvailVirtual;
} MEMORYSTATUS ,* LPMEMORYSTATUS;
l
其中参数含义如下所q?/p>
dwLengthQMEMORYSTATUSl构大小?/p>
dwMemoryLoadQ已使用内存所占的癑ֈ比?/p>
dwTotalPhysQ物理存储器的d节数?/p>
dwAvailPhysQ空闲物理存储器的字节数?/p>
dwTotalPageFileQ页文g包含的最大字节数?/p>
dwAvailPageFileQ用h式分ZI闲内存大小?/p>
dwTotalVirtualQ用h式分区大?/p>
dwAvailVirtualQ表C当前进E中q剩下的自由区域的d?/p>
在调用GlobalMemoryStatus()之前Q必ddwLength成员初始化ؓ用字节表C的l构的大,即一个MEMORYSTATUSl构的大。这个初始化操作使得Microsoft能够在新版本Windowspȝ中将新成员添加到q个l构中,而不会破坏现有的应用E序。当调用GlobalMemoryStatus()Ӟ它将对该l构的其余成员进行初始化q返回?/p>
如果某个应用E序在内存大?GB的计机上运行,或者合计交换文件的大小大于4GBQ那么可以用新的GlobalMemoryStatusEx()函数。其函数的原型ؓQ?/p>
l
BOOL GlobalMemoryStatusEx(MEMORYSTATUSEX &mst);
l
其中mstq回MEMORYSTATUSEXl构的填充信息,该结构体与原先的MEMORYSTATUSl构基本相同Q差别在于新l构的所有成员的大小都是64位宽Q因此它的值可以大? GB?/p>
对内存的理除了对当前内存的使用状态信息进行获取外Q还l常需要获取有兌E的虚拟地址I间的状态信息。例如,如何得到一个进E已提交的页面范_q就要用C?API函数VirtualQuery()或VirtualQueryEx()来进行查询。这两个函数的功能相|不同是VirtualQuery()只是查询本进E内存空间信息,而VirtualQueryEx()可以查询指定q程的内存空间信息。VirtualQuery()函数原型如下Q?/p>
l
DWORD VirtualQuery(
LPVOID lpAddress,
PMEMORY_BASIC_INFORMATION lpBuffer,
DWORD dwLength
);
l
VirtualQueryEx()函数原型如下Q?/p>
l
DWORD VirtualQueryEx(
HANDLE hProcess ,
LPCVOID lpAddress ,
PMEMORY_BASIC_INFORMATION lpBuffer ,
DWORD dwLength
);
l
其中参数含义如下所q?/p>
hProcessQ进E的句柄?/p>
lpAddressQ想要了解其信息的虚存地址?/p>
lpBufferQ返回MEMORY_ BASIC_INFORMATIONl构的地址?/p>
dwLengthQ返回的字节数?/p>
PWEMORY_BASIC_INFORMATION的定义如下:
l
typedef struct _MEMORY_BASIC_INFORMATION{
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
DWORD RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, * PMEMORY_BASIC_INFORMATION;
l
其中参数含义如下所q?/p>
BaseAddressQ被查询内存块的基地址?/p>
AllocationBaseQ用VirtualAlloc()分配该内存时实际分配的基地址?/p>
AllocationProtectQ分配该面Ӟ面的一些属性,如PAGE_READWRITE、PAGE_EXECUTE{(其他属性可参?Platform SDKQ?/p>
RegionSizeQ从BaseAddress开始,h相同属性的面的大?/p>
StateQ页面的状态,?U可能|MEM_COMMIT、MEM_FREE和MEM_ RESERVEQ这个参数是最重要的,从中可知指定内存面的状态?/p>
ProtectQ页面的属性,它可能的取g AllocationProtect 相同?/p>
TypeQ指明了该内存块的类型,?U可能|MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE?/p>
在进行进E挂钩时Q经常要向内存页中写入部分代码,q就需要改变内存页的保护属性。有q的是Win32提供了两个API函数VirtualProtect()和VirtualProtectEx()Q它们可以对改变内存保护。例如,在用这两个函数Ӟ可以先按PAGE_READWRITE属性来提交一个页的地址Qƈ且立卛_数据填写到该中Q然后再把该늚属性改变ؓPAGE_READONLYQ这样可以有效地保护数据不被该进E中的Q何其他线E重写。在调用q两个函C前最好先了解有关面的信息,可以通过VirtualQuery()来实现?/p>
VirtualProtect()与VirtualProtectEx()函数的区别在于VirtualProtect()只适用于本q程Q而VirtualProtectEx()可以适用于其他进E。VirtualProtect()函数原型如下Q?/p>
BOOL VirtualProtect(
PVOID pvAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD pflOldProtect
);
l
VirtualProtectEx()函数原型如下Q?/p>
l
BOOL VirtualProtectEx(
HANDLE hProcess,
PVOID pvAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD pflOldProtect
);
l
其中参数的含义如下所q?/p>
hProcessQ要修改内存的进E句柄?/p>
pvAddressQ指向内存的基地址Q它必须位于q程的用h式分ZQ?/p>
dwSizeQ用于指明想要改变保护属性的字节数?/p>
flNewProtectQ代表PAGE_*保护属性标志中的Q何一个标志,但PAGE_ WRITECOPY和PAGE_EXECUTE_WRITECOPYq两个标志除外?/p>
pflOldProtectQ是DWORD大小的地址QVirtualProtect()和VirtualProtectEx()用原先与pvAddress位置上的字节相关的保护属性填入该地址。尽许多应用程序ƈ不需要该信息Q但是必Mؓ该参C递一个有效地址Q否则该函数的运行将会失败?/p>
前面已经说明了如何获得一个进E的内存属性、如何分配内存和如何改变内存늚保护属性,其最l的目的是要对一个进E中内存内容q行d。要完成此工作,需要用C个函敎ͼReadProcessMemory() 和WriteProcessMemory()Q这两个函数非常有用。如果知道了一个进E的句柄和内存地址Q就可以用ReadProcessMemory()函数来得到该q程和该地址中的内容Q此函数的原型ؓQ?/p>
l
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);
l
其中hProcessd的进E句柄,lpBaseAddress内存的v始地址QlpBuffer入数据的地址QnSized的字节数QlpNumberOfBytesRead为实际读入的?nbsp; 节数?/p>
同样Q如果知道了一个进E的句柄和内存地址Q可以用WriteProcessMemory()函数向该q程和该地址中写入新的内容,q个函数的原型ؓQ?/p>
l
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);
l
其中参数hProcess写入的进E句柄,lpBaseAddress为写内存的v始地址QlpBuffer为写入数据的地址QnSize写入的字节数QlpNumberOfBytesWritten为实际写入的字节数?/p>
Windows CE的有些函C需要用到物理内存结构体PHYSICAL_ADDRESSQ?/span> Windows CE?/span>ceddk.h中定义了PHYSICAL_ADDRESSQ它其实?/span>LARGE_INTEGERcdQ其定义如下Q?/span>
// in ceddk.h
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
// in winnt.h
typedef union _LARGE_INTEGER{
struct{
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER;
可见Q?/span>Windows CE中用64?/span>Bit来代表物理地址Q对于大多数32位的CPU而言Q只需要把它的HighPart讄?/span>0可以了?/span>
如果要直接访问某一个地址的物理内存,Windows CE提供?/span>VirtualAlloc()?/span>VirtualCopy()函数Q?/span>VirtualAlloc负责在虚拟内存空间内保留一D虚拟内存,?/span>VirtualCopy负责把一D늉理内存和虚拟内存l定Q这P最l对物理内存的访问还是通过虚拟地址q行。它们的声明如下Q?/span>
// 甌虚拟内存
LPVOID VirtualAlloc(
LPVOID lpAddress, // 希望的虚拟内存v始地址
DWORD dwSize, // 以字节ؓ单位的大?/span>
DWORD flAllocationType, // 甌cdQ分?/span>Reserve?/span>Commit
DWORD flProtect // 讉K权限
);
// 把物理内存绑定到虚拟地址I间
BOOL VirtualCopy(
LPVOID lpvDest, // 虚拟内存的目标地址
LPVOID lpvSrc, // 物理内存地址
DWORD cbSize, // 要绑定的大小
DWORD fdwProtect // 讉K权限
);
VirtualAlloc对虚拟内存的甌分ؓ两步Q保?/span>MEM_RESERVE和提?/span>MEM_COMMIT。其?/span>MEM_RESERVE只是在进E的虚拟地址I间内保留一D,q不分配实际的物理内存,因此保留的虚拟内存ƈ不能被应用程序直接用?/span>MEM_COMMIT阶段才真正的拟内存分配物理内存?/span>
下面的代码显CZ如何使用VirtualAlloc?/span>VirtualCopy来访问物理内存。因?/span>VirtualCopy负责把一D늉理内存和虚拟内存l定Q所?/span>VirtualAlloc的时候只需要对内存保留Q没有必要提交?/span>
FpDriverGlobals =
(PDRIVER_GLOBALS) VirtualAlloc(
0,
DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
MEM_RESERVE,
PAGE_NOACCESS);
if (FpDriverGlobals == NULL) {
ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!\r\n")));
return;
}
else {
if (!VirtualCopy(
(PVOID)FpDriverGlobals,
(PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START),
DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
(PAGE_READWRITE | PAGE_NOCACHE))) {
ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!\r\n")));
return;
}
}
CEDDKq提供了函数MmMapIoSpace用来把一D늉理内存直接映到虚拟内存。此函数的原形如下:
PVOID MmMapIoSpace(
PHYSICAL_ADDRESS PhysicalAddress, // 起始物理地址
ULONG NumberOfBytes, // 要映的字节?/span>
BOOLEAN CacheEnable // 是否~存
);
其实Q?/span>MmMapIoSpace函数内部也是调用VirtualAlloc?/span>VirtualCopy函数来实现物理地址到虚拟地址的映的?/span>MmMapIoSpace函数的原代码是公开的,我们可以?/span>%_WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\CEDDK\DDK_MAP\ddk_map.c得到。从MmMapIoSpace的实现我们也可以看出VirtualAlloc?/span>VirtualCopy的用法:
PVOID MmMapIoSpace (
IN PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN CacheEnable
)
{
PVOID pVirtualAddress; ULONGLONG SourcePhys;
ULONG SourceSize; BOOL bSuccess;
SourcePhys = PhysicalAddress.QuadPart & ~(PAGE_SIZE - 1);
SourceSize = NumberOfBytes + (PhysicalAddress.LowPart & (PAGE_SIZE - 1));
pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);
if (pVirtualAddress != NULL)
{
bSuccess = VirtualCopy(
pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize,
PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));
if (bSuccess) {
(ULONG)pVirtualAddress += PhysicalAddress.LowPart & (PAGE_SIZE - 1);
}
else {
VirtualFree(pVirtualAddress, 0, MEM_RELEASE);
pVirtualAddress = NULL;
}
}
return pVirtualAddress;
}
此外Q?/span>Windows CEq供?/span>AllocPhysMem函数?/span>FreePhysMem函数Q用来申请和释放一D连l的物理内存。函数可以保证申L物理内存是连l的Q如果函数成功,会返回虚拟内存的句柄和物理内存的起始地址。这对于DMA讑֤ؓ有用。在q里׃详细介绍了,读者可以参?/span>Windows CE的联机文?/span>
在程序中赋|
my_function = 0x00;
然后调用函数Q?/p>
my_function();
E序׃跌{?x00地址开始执行,常用于BootLoaderE序中.
cM的还有直接向某个地址写入数据Q?/p>
int *my_address = 0x05555555;
*my_address = 0x22222222;
直接?x05555555地址写入数据0x22222222.
(2) W二个问题的{案也很单:如果Ud n 位,那么UM的位数要不小?0 Qƈ且一定要于 n 。这样就不会在一ơ操作中把所有数据都U走?
比如Q如果整型数据占 32 位,n 是一整型数据Q则 n << 31 ?n << 0 都合法,?n << 32 ?n << -1 都不合法?
注意即腄位填W号位,有符h数的右移也不相当与除以。ؓ了证明这一点,我们可以想一?-1 >> 1 不可能ؓ 0 ?
问题Q位D늻?
struct RPR_ATD_TLV_HEADER
{
ULONG res1:6;
ULONG type:10;
ULONG res1:6;
ULONG length:10;
};
位段l构是一U特D的l构, 在需按位讉K一个字节或字的多个位时, 位结构比按位q算W更加方ѝ?
位结构定义的一般Ş式ؓ:
struct
位结构名
{
数据cd 变量? 整型常数
;
数据cd 变量? 整型常数
;
} 位结构变?
其中: 整型常数必须是非负的整数, 范围?~15, 表示二进制位的个? 卌C有多少位?
变量名是选择? 可以不命? q样规定是ؓ了排列需要?
例如: 下面定义了一个位l构?
struct{
unsigned incon: 8; /*incon占用低字节的0~7??/font>
*/
unsigned txcolor: 4;/*txcolor占用高字节的0~3位共4?/font>
*/
unsigned bgcolor: 3;/*bgcolor占用高字节的4~6位共3?/font>
*/
unsigned blink: 1; /*blink占用高字节的W??/font>
*/
}ch;
位结构成员的讉K与结构成员的讉K相同?
例如: 讉K上例位结构中的bgcolor成员可写?
ch.bgcolor
位结构成员可以与其它l构成员一起用。按位访问与讄Q方?amp;节省
例如:
struct info{
char name[8];
int age;
struct addr address;
float pay;
unsigned state: 1;
unsigned pay: 1;
}workers;'
上例的结构定义了关于一个工从的信息。其中有两个位结构成? 每个位结构成员只有一? 因此只占一个字节但保存了两个信? 该字节中W一位表C工人的状? W二位表C工资是否已发放。由此可见用位l构可以节省存贮I间?
注意不要过值限?
问题Q字节对?
我在使用VC~程的过E中Q有一ơ调用DLL中定义的l构Ӟ发觉l构都ؕ掉了Q完全不能读取正的|后来发现q是因ؓDLL和调用程序用的字节寚w选项不同Q那么我想问一下,字节寚wI竟是怎么一回事Q?
{案与分析:
关于字节寚wQ?
1?当不同的l构使用不同的字节对齐定义时Q可能导致它们之间交互变得很困难?
2?在跨CPUq行通信Ӟ可以使用字节寚w来保证唯一性,诸如通讯协议、写驱动E序时候寄存器的结构等?
三种寚w方式Q?
1?自然寚w方式QNatural AlignmentQ:与该数据cd的大相{?
2?指定寚w方式 Q?
#pragma pack(8) //
指定Align?8Q?/font>
#pragma pack() //
恢复到原先?
3?实际寚w方式Q?
Actual Align = min ( Order Align, Natual Align )
对于复杂数据cdQ比如结构等Q:实际寚w方式是其成员最大的实际寚w方式Q?
Actual Align = max( Actual align1,2,3Q?
~译器的填充规律Q?
1?成员为成员Actual Align的整数倍,在前面加Padding?
成员Actual Align = min( l构Actual AlignQ设定对齐方?
2?l构为结构Actual Align的整数倍,在后面加Padding.
例子分析Q?
#pragma pack(8) //
指定Align?/font>
8
struct STest1
{
char ch1;
long lo1;
char ch2;
} test1;
#pragma pack()
现在
Align of STest1 = 4 , sizeof STest1 = 12 ( 4 * 3 )
test1在内存中的排列如下( FF ?padding Q:
00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --
01 FF FF FF 01 01 01 01 01 FF FF FF
ch1 -- lo1 -- ch2
#pragma pack(2) //
指定Align?/font>
2
struct STest2
{
char ch3;
STest1 test;
} test2;
#pragma pack()
现在 Align of STest1 = 2, Align of STest2 = 2 , sizeof STest2 = 14 ( 7 * 2 )
test2在内存中的排列如下:
00 -- -- -- 04 -- -- -- 08 -- -- -- 12 -- -- --
02 FF 01 FF FF FF 01 01 01 01 01 FF FF FF
ch3 ch1 -- lo1 -- ch2
注意事项Q?
1?q样一来,~译器无法ؓ特定q_做优化,如果效率非常重要Q就量不要使用#pragma packQ如果必M用,也最好仅在需要的地方q行讄?
2?需要加pack的地方一定要在定义结构的头文件中加,不要依赖命o行选项Q因为如果很多h使用该头文gQƈ不是每个人都知道应该pack。这特别表现在ؓ别h开发库文gӞ如果一个库函数使用了struct作ؓ其参敎ͼ当调用者与库文件开发者用不同的packӞ׃造成错误Q而且该类错误很不好查?
3?在VC及BC提供的头文g中,除了能正好对齐在四字节上的结构外Q都加了packQ否则我们编的WindowsE序哪一个也不会正常q行?
4??#pragma pack(n) 后一定不要include其他头文Ӟ若包含的头文件中改变了align|生非预期l果?
5?不要多h同时定义一个数据结构。这样可以保证一致的pack倹{?
问题Q按位运符
C语言和其它高U语a不同的是它完全支持按位运符。这与汇~语a的位操作有些怼?C中按位运符列出如下:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
操作W?作用
────────────────────────────
& 位逻辑?/font>
| 位逻辑?/font>
^ 位逻辑异或
- 位逻辑?/font>
>> 右移
<< 左移
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
注意Q?
1?按位q算是对字节或字中的实际位进行检、设|或UM, 它只适用于字W型和整数型变量以及它们的变? 对其它数据类型不适用?
2?关系q算和逻辑q算表达式的l果只能???而按位运的l果可以??以外的倹{要注意区别按位q算W和逻辑q算W的不同, 例如, 若x=7, 则x&&8 的gؓ?两个非零值相与仍为非?, 而x&8的gؓ0?
3?| ?||Q?amp;?amp;&Q~? 的关p?
&、| ?~ 操作W把它们的操作数当作一个ؓ序列Q按位单独进行操作。比如:10 & 12 = 8Q这是因?&"操作W把 10 ?12 当作二进制描q?1010 ?1100 Q所以只有当两个操作数的相同位同时ؓ 1 Ӟ产生的结果中相应位才?1 。同理,10 | 12 = 14 ( 1110 )Q通过补码q算Q~10 = -11 ( 11...110101 )?lt;以多ؓ一个位序列> &&、|| 和!操作W把它们的操作数当作"???Qƈ且用 0 代表"?QQ何非 0 D认ؓ??。它们返?1 代表"?Q? 代表"?Q对?&&"?||"操作W,如果左侧的操作数的值就可以军_表达式的|它们Ҏ׃去计右侧的操作数。所以,!10 ?0 Q因?10 ?0 Q?0 && 12 ?1 Q因?10 ?12 均非 0 Q?0 || 12也是 1 Q因?10 ?0 。ƈ且,在最后一个表辑ּ中,12 Ҏ没被计,在表辑ּ 10 || f( ) 中也是如此?
一Q系l环?2
二.gSOAP的简要用例?2
三.囄说明 6
四.要注意的问题 6
五.参考文?7
六.备注 7
一Q系l环?br />linux操作pȝkernel2.4.2Q安装gsoap2.6到目?usr/local/gsoap
二.gSOAP的简要用例?br />下面是一个简单的例子Q实C个加法运的WebServiceQ具体功能是cli端输入num1和num2Qserver端返回一个num1和num2相加的结果sum?br />
1Q?首先Q我们需要做的是写一个函数声明文Ӟ来定义接口函数ns__addQ文件名字ؓadd.hQ内容如下:
//gsoap ns service name: add
//gsoap ns service namespace: http://mail.263.net/add.wsdl
//gsoap ns service location: http://mail.263.net
//gsoap ns service executable: add.cgi
//gsoap ns service encoding: encoded
//gsoap ns schema namespace: urn:add
int ns__add( int num1, int num2, int* sum );
2Q?然后我们需要创建文件MakefileQ从而利用gsoapcpp2工具由add.h生成一?xml文g?c文g?h文gQ这些文件均动生成,Makefile的内容如下:
GSOAP_ROOT=/usr/local/gsoap
WSNAME=add
CC=g++ -g -DWITH_NONAMESPACES
INCLUDE=-I $(GSOAP_ROOT)
SERVER_OBJS=$(WSNAME)C.o $(WSNAME)Server.o stdsoap2.o
CLIENT_OBJS=$(GSOAP_ROOT)/env/envC.o $(WSNAME)ClientLib.o stdsoap2.o
ALL_OBJS=${WSNAME}server.o $(WSNAME)C.o $(WSNAME)Server.o ${WSNAME}test.o ${WSNAME}client.o $(WSNAME)ClientLib.o
#ȝ目标
all:server
${WSNAME}.wsdl:${WSNAME}.h
$(GSOAP_ROOT)/soapcpp2 -p$(WSNAME) -i -n -c ${WSNAME}.h
stdsoap2.o:$(GSOAP_ROOT)/stdsoap2.c
$(CC) -c $?
#~译一L成规则的.o文g
$(ALL_OBJS):%.o:%.c
$(CC) -c $? $(INCLUDE)
#~译服务器端
server:Makefile ${WSNAME}.wsdl ${WSNAME}server.o $(SERVER_OBJS)
$(CC) ${WSNAME}server.o $(SERVER_OBJS) -o ${WSNAME}server
#~译客户?br />client:Makefile ${WSNAME}.wsdl ${WSNAME}client.c ${WSNAME}test.c $(ALL_OBJS) stdsoap2.o
$(CC) ${WSNAME}test.o ${WSNAME}client.o $(CLIENT_OBJS) -o ${WSNAME}test
cl:
rm -f *.o *.xml *.a *.wsdl *.nsmap $(WSNAME)H.h $(WSNAME)C.c $(WSNAME)Server.c $(WSNAME)Client.c $(WSNAME)Stub.* $(WSNAME)$(WSNAME)Proxy.* $(WSNAME)$(WSNAME)Object.* $(WSNAME)ServerLib.c $(WSNAME)ClientLib.c $(WSNAME)server ns.xsd $(WSNAME)test
3Q我们先来做一个server端,创徏文gaddserver.c文gQ内容如下:
#include "addH.h"
#include "add.nsmap"
int main(int argc, char **argv)
{
int m, s; /* master and slave sockets */
struct soap add_soap;
soap_init(&add_soap);
soap_set_namespaces(&add_soap, add_namespaces);
if (argc < 2)
{
printf("usage: %s <server_port> \n", argv[0]);
exit(1);
}
else
{
m = soap_bind(&add_soap, NULL, atoi(argv[1]), 100);
if (m < 0)
{
soap_print_fault(&add_soap, stderr);
exit(-1);
}
fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
for ( ; ; )
{
s = soap_accept(&add_soap);
if (s < 0)
{
soap_print_fault(&add_soap, stderr);
exit(-1);
}
fprintf(stderr, "Socket connection successful: slave socket = %d\n", s);
add_serve(&add_soap);//该句说明该server的服?br />soap_end(&add_soap);
}
}
return 0;
}
//server端的实现函数与add.h中声明的函数相同Q但是多了一个当前的soapq接的参?br />int ns__add(struct soap *add_soap, int num1, int num2, int *sum)
{
*sum = num1 + num2;
return 0;
}
4Q让我们的server跑v来吧Q?br />shell>make
shell>./addserver 8888
如果l端打印出“Socket connection successful: master socket = 3”,那么你的server已经在前台runh了,应该是值得高兴?amp;#61514;?br />打开IEQ键入http://本机IP:8888Q显CXMLQ服务已l启动,l端打印出“Socket connection successful: slave socket = 4”,表示服务接收C一ơsoap的连接?br />
5Q让我们再来写个客户端(q个只是soap的客L函数装一下,具体的调用参见下面的addtest.cQ,创徏文gaddclient.cQ内容如下:
#include "addStub.h"
#include "add.nsmap"
/**
* 传入参数QserverQserver的地址
* num1,num2Q需要相加的?br />* 传出参数QsumQnum1和num2相加的结?br />* q回|0为成功,其他为失?br />*/
int add( const char* server, int num1, int num2, int *sum )
{
struct soap add_soap;
int result = 0;
soap_init(&add_soap);
soap_set_namespaces(&add_soap, add_namespaces);
//该函数是客户端调用的主要函数Q后面几个参数和add.h中声明的一P前面多了3个参敎ͼ函数名是接口函数名ns__add前面加上soap_call_
soap_call_ns__add( &add_soap, server, "", num1, num2, sum );
if(add_soap.error)
{
printf("soap error:%d,%s,%s\n", add_soap.error, *soap_faultcode(&add_soap), *soap_faultstring(&add_soap) );
result = add_soap.error;
}
soap_end(&add_soap);
soap_done(&add_soap);
return result;
}
6Q我们最l写一个可以运行的客户端调用程序,创徏文gaddtest.cQ内容如下:
#include <stdio.h>
#include <stdlib.h>
int add(const char* server, int num1, int num2, int *sum);
int main(int argc, char **argv)
{
int result = -1;
char* server="http://localhost:8888";
int num1 = 0;
int num2 = 0;
int sum = 0;
if( argc < 3 )
{
printf("usage: %s num1 num2 \n", argv[0]);
exit(0);
}
num1 = atoi(argv[1]);
num2 = atoi(argv[2]);
result = add(server, num1, num2, &sum);
if (result != 0)
{
printf("soap err,errcode = %d\n", result);
}
else
{
printf("%d+%d=%d\n", num1, num2, sum );
}
return 0;
}
7Q让我们的client端和server端通讯
shell>make client
shell>./addtest 7 8
当然Q你的server应该q在runQ这样得到输出结?+8=15Q好了,你成功完成了你的W一个C写的WebServiceQ恭喜?br />三.囄说明
四.要注意的问题
1Q?add.h文g前面的几句注释不能删除,为soapcpp2需要识别的标志
2Q?接口函数的返回值只能是intQ是soap调用的结果,一般通过soap.error来判断soap的连接情况,q个q回值没有用到?br />3Q?接口函数的最后一个参Cؓ传出参数Q如果需要传出多个参敎ͼ需要自己定义一个结构将q回封装?br />4Q??h文g中不能include别的.h文gQ可能不能生效,需要用到某些结构的时候需要在该文件中直接声明?br />5Q?如果客户端的调用不需要返回|那么最后一个参?br />五.参考文?br />1Qgsoap主页
http://gsoap2.sourceforge.net
2Q跟我一起写Makefile
http://dev.csdn.net/develop/article/20/20025.shtm
3QWeb ServicesQ?A Technical IntroductionQ机械工业出版社Q?br />六.备注
192.168.18.233?92.168.18.234?usr/local/gsoap目录下的3个需要的文g及一个env目录Q不是编译安装的Q是在别的地方编译好了直接copyq来的(实际~译l果中还有wsdl2h工具及其他一些文Ӟ但是我们的实际开发中只是用到了这3个文件及env目录Q。因为时间仓促,本hq没有时间研I编译的问题Q相关细节可以查看参考文??br />?92.168.18.233?home/weiqiong/soap/sample目录下及192.168.18.234?tmp/soap/sample目录下有本文讲到的加法运的例子?/font>
event_set_pstn_ring,
event_set_sip_ring,
event_set_alarm_ring,
event_set_ring_volume
};
typedef struct msgbuf
{
long msgtype;
unsigned long msgid;
unsigned long lp;
unsigned long wp;
}MSGBuf, *pMSGBuf;
int vvMSGSend(long thread_id, unsigned long msgid, unsigned long lp, unsigned long wp);
int vvMSGRecv(long thread_id, struct msgbuf *msg, int is_wait);
#ifndef _WINDOWS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#define MSG_FILE_NAME "/rw/" //"/mnt/"
#define MSG_FLAG (IPC_CREAT | 00666)
//| IPC_EXCL
typedef struct sendMsg
{
int sd;
void *content;
}SendMsg, *pSendMsg;
#endif
#endif //MSG_H
#include "vvmsg.h"
#include <ps_log.h>
#ifndef _WINDOWS
#include <phone_Interface.h>
#include <pthread.h>
#include <basegdi.h>
#include <keyboard.h>
//#include "hash.h"
extern pthread_t g_incomingthread;
//extern hash_table table;
#endif
int vvMSGSend(long thread_id, unsigned long msgid, unsigned long lp, unsigned long wp)
{
struct msgbuf bmsg;
#ifndef _WINDOWS
key_t key;
int msg_id;
bmsg.msgtype = thread_id;
bmsg.msgid = msgid;
bmsg.lp = lp;
bmsg.wp = wp;
if((key = ftok(MSG_FILE_NAME,'a')) == -1)
{
return -1;
}
if((msg_id = msgget(key,MSG_FLAG)) == -1)
{
return -1;
}
if (msgsnd(msg_id, &bmsg, sizeof(struct msgbuf), IPC_NOWAIT) == -1)
{
return -1;
}
#endif
return 1;
}
int vvMSGRecv(long thread_id, struct msgbuf *msg, int is_wait)
{
#ifndef _WINDOWS
key_t key;
int msg_id;
if((key = ftok(MSG_FILE_NAME,'a')) == -1)
{
printf("Recv msg error 1!\n");
return -1;
}
if((msg_id = msgget(key,MSG_FLAG)) == -1)
{
printf("Recv msg error 2!\n");
return -1;
}
if (is_wait != 1)
{
if (msgrcv(msg_id, msg, sizeof(struct msgbuf), thread_id, IPC_NOWAIT) == -1)
{
printf("Recv msg error 3!\n");
return -1;
}
}
else
{
if (msgrcv(msg_id, msg, sizeof(struct msgbuf), thread_id, 0) == -1)
{
//printf("Recv msg error 4!\n");
return -1;
}
}
#endif
return 1;
}
void *skype_thread_start(void *arg)
{
#ifndef _WINDOWS
MSGBuf msg;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);//讄U程属?br /> pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);
for (;;)
{
pthread_testcancel();//讄取消?br /> if (vvMSGRecv((long)g_incomingthread, &msg, 1) == -1)
continue;
// analysis the message
switch (msg.msgid)
{
ps_show_str(log_DEBUG, "vvmsg event!!!!!!!!!!!!!!!%d\r\n", msg.msgid);
case event_login:
{
userLogin();
}
break;
case event_logout:
{
userLogout();
}
case event_sip_clean:
{
SipClean();
}
break;
case event_sip_init_para:
{
ps_show_str(log_DEBUG, "event before################UpdateSipInitPara\r\n");
UpdateSipInitPara();
ps_show_str(log_DEBUG, "event after##################UpdateSipInitPara\r\n");
}
break;
case event_log_init_para:
{
UpdateLogInitPara();
}
break;
case event_set_dtmf_mode:
{
int i = (int)msg.lp;
ps_show_str(log_DEBUG, "event_set_dtmf_mode########################%d\r\n", i);
SetDTMFMode(i);
}
break;
case event_set_dhcp:
{
SetDHCP();
}
break;
case event_set_pppoe:
{
SetPPPOE();
}
break;
case event_pstn_call_out:
{
pstncall((char*)msg.lp);
}
break;
case event_sip_call_out:
{
sipcall((char*)msg.lp);
}
break;
case event_answer_sipcall:
{
callmgr_answercall((LINE_ID_T *)msg.lp);
}
break;
case event_release_sipcall:
{
callmgr_releasecall((LINE_ID_T *)msg.lp);
}
break;
case event_loadBMP_init:
{
CreateSysBmp();
}
break;
case event_pstn_call_in:
{
LINE_ID_T *line = (LINE_ID_T *)msg.wp;
sipcome_create(line);
}
break;
case event_sip_call_in:
{
LINE_ID_T *line = (LINE_ID_T *)msg.wp;
sipcome_create(line);
}
break;
case event_remote_establish_call:
{
LINE_ID_T *line = (LINE_ID_T *)msg.wp;
pstnchat_create(line);
if(g_Hwnd[HWND_CALLOUT]!=0)
calling_destroy(g_Hwnd[HWND_CALLOUT]);
}
break;
case event_remote_cancelcall:
{
if(g_Hwnd[HWND_CALLIN]!=0)
SendMessage(g_Hwnd[HWND_CALLIN],MSG_KEYDOWN,KEY_SW_RSK,0);
}
break;
case event_remote_release_call:
{
if(g_Hwnd[HWND_CHAT]!=0)
SendMessage(g_Hwnd[HWND_CHAT],MSG_KEYDOWN,KEY_SW_RSK,0);
}
break;
case event_login_return:
{
printf("sfds0000000000000000000000000000000dssssssss^^^^^^^^^^^^^^^^^\r\n");
if(g_Hwnd[HWND_MAINSCREEN]!=0)
{
UpdateWindow(g_Hwnd[HWND_MAINSCREEN],1);
// SetFocusChild(g_Hwnd[HWND_MAINSCREEN]);
// ShowWindow(g_Hwnd[HWND_MAINSCREEN], SW_SHOW);
}
}
break;
case event_remote_ignore:
{
if(g_Hwnd[HWND_CALLOUT]!=0)
SendMessage(g_Hwnd[HWND_CALLOUT],MSG_KEYDOWN,KEY_SW_RSK,0);
}
break;
case event_set_pstn_ring:
{
SetPstnRing((int)msg.lp);
}
break;
case event_set_sip_ring:
{
SetSipRing((int)msg.lp);
}
break;
case event_set_ring_volume:
{
SetRingVolume((int)msg.lp);
}
break;
}
}
#endif
}
附(创徏U程Q:if (pthread_create(&g_incomingthread, NULL, skype_thread_start, NULL))
return -1;
static void strtoupper(char *p)
{
int i;
int j=strlen(p);
for(i=0;i<j;i++)
{
*p=toupper(*p);
p++;
}
};
int main( void )
{
int fd;
DIR * dir;
struct dirent * ent;
char *p1;
char *p2;
char temp[255];
char lower[50];
memset(temp,0,sizeof(temp));
memset(lower,0,sizeof(lower));
if(!(dir=opendir("/opt/sip_ui/res/."))){ //目录
perror("opendir");
return;
}
errno=0;
if((fd=open("hw",O_TRUNC|O_CREAT|O_WRONLY,0644))<0){
perror("open");
exit(1);
}
while((ent=readdir(dir))){
if(strstr(ent->d_name,".bmp")){ //后缀?br /> p1=strtok(ent->d_name,".");
p2=strtok(NULL,".");
memcpy(lower,p1,strlen(p1));
lower[strlen(p1)]='\0';
strtoupper(p1);
strtoupper(p2);
strcat(p1,"_PIC");
//strcat(p1,p2);
sprintf(temp,"#define %s \"res\" PATH_SEP \"%s.\" RESFILE_EXT\n",p1,lower);
if(write(fd,temp,strlen(temp)) < 0){
perror("write");
exit(1);
}
}
errno=0;
}
if(errno)
{
perror("readdir");
return ;
}
close(fd);
closedir(dir);
return 1;
}