这是一个能枚举当前系统进程,并且终止进程的小程序。 程序界面如下:
程序源代码下载:
君看一叶舟,出没风波里
主要实现了定时关机的功能 在NT系统下,和win98不同,需要调整本进程的权限。 主要代码如下:
void EnableDebugPriv( void ) { HANDLE hToken; LUID sedebugnameValue; TOKEN_PRIVILEGES tkp;
if ( ! OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) ) return; if ( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) ) { CloseHandle( hToken ); return; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = sedebugnameValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) ) CloseHandle( hToken ); }
本程序源代码下载:
作者:BugSearcher Introduction We normally find a lot of ways and a number of resources to use WMI or to get information from “Windows Management Instrumentation” while using Visual Basic 6 and C#, but I could not find a single resource describing the same thing in Visual C++. MSDN resources are also very limited in this context.
Code Following is the code on how to get the current processor load from a WMI class Win32_Processor defined in a .mof file. .mof files are managed object files which have a typical way of defining classes and methods.
WMI provides the COM service which is used to connect to the WMI services. The important parts of the code include:
bstrNamespace : The initialization of this variable is very tricky. The first three forward slashes //./ represent the Host Computer name from which you want to get information from. A “.” Indicates that information is to be obtained from the Same Computer on which you are working. You can give any Network name here but getting information from network depends upon your Access Rights etc. cimv2 is the namespace containing the Win32_Processor class. pIWbemLocator is the argument in which we get the Interface pointer. After that, we call the function ConnectServer of the pIWbemLocator to get a pointer to pWbemServices. WMI uses its own Query Language to get information known as WQL (the acronym for WMI Query Language). So, when calling the function ExecQuery, we have to specify the language as its first argument. Second argument is the Query itself. Last argument is important because here we get a pointer to an Enumeration object through which we can enumerate through the objects available. This enumeration is important because consider the case that we want to know the information about running processes and we are using Win32_Process class for this purpose. Then through this enumeration object, we can go through different processes one by one. By calling the Reset and Next methods of pEnumObject, we are moving through the objects. We get the pointer to an object in pClassObject. The last function through which we get the Actual value of a property is Get. We pass a BSTR to this function to get the value in a Variant. CoInitialize(NULL);
//Security needs to be initialized in XP first
if(CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0 ) != S_OK) return;
IWbemLocator pIWbemLocator = NULL; IWbemServices pWbemServices = NULL; IEnumWbemClassObject pEnumObject = NULL; BSTR bstrNamespace = (L”root\cimv2”); HRESULT hRes = CoCreateInstance ( CLSID_WbemAdministrativeLocator, NULL , CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER , IID_IUnknown , ( void ** ) & pIWbemLocator ) ; if (SUCCEEDED(hRes)) { hRes = pIWbemLocator->ConnectServer( bstrNamespace, // Namespace NULL, // Userid NULL, // PW NULL, // Locale 0, // flags NULL, // Authority NULL, // Context &pWbemServices; ); } BSTR strQuery = (L”Select from win32_Processor”); BSTR strQL = (L”WQL”); hRes = pWbemServices->ExecQuery(strQL, strQuery, WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumObject;);
ULONG uCount = 1, uReturned; IWbemClassObject * pClassObject = NULL; hRes = pEnumObject->Reset(); hRes = pEnumObject->Next(WBEM_INFINITE,uCount, &pClassObject;, &uReturned;); VARIANT v; BSTR strClassProp = SysAllocString(L”LoadPercentage”); hRes = pClassObject->Get(strClassProp, 0, &v;, 0, 0); SysFreeString(strClassProp);
_bstr_t bstrPath = &v; //Just to convert BSTR to ANSI char strPath=(char)bstrPath; if (SUCCEEDED(hRes)) MessageBox(strPath); else MessageBox(”Error in getting object”); VariantClear( &v; ); pIWbemLocator->Release(); pWbemServices->Release(); pEnumObject->Release(); pClassObject->Release(); CoUninitialize(); Conclusion This was the shortest method I was able to work out to get information from any WMI class. You can simply change the class name in the Query and Property Name while calling Get method and you will get information from all the classes supported in your OS. I tested this code in Windows 2000 Professional. I hope it will work well for Win XP but probably not in previous versions of windows because they don’t support a lot of WMI classes.
Update I am updating this Article and the code for Windows XP. As I said in the beginning, this was initially intended for Windows 2K and it was still working fine on Windows 2K machine (I have checked it on at least 10 now coz I was getting so many complaints). Well, for Windows XP there is one major change and that was suggested by igoychev. Many thanks for that. The problem with Windows XP was that it needed security to be initialized first. Plus, I have also changed those confusing slashes in bstrNamespace to some simpler ones. I am using Windows 2003 Advance Server and it is working fine on it. You need to include wbemuuid.lib in Project Settings -> Linker -> Additional Dependencies to get this code working. I have also added some error checking to avoid crashes, hope it works for you. You need to have Platform SDK installed for this code to compile. WMI SDK is not necessary once again. Sorry rbervgm, your work was great using WMI SDK but I thought better to keep it WMI SDK Clean. Hope you don’t mind. Now, I hope that downloadable project will work for most of the people. One more thing, it still works for Windows 2K machines.
在考试或者做题的时候,经常遇到这类问题,出题的老师就是爱在这点上面钻牛脚尖,那就是自增自减操作的组合运算,我个人认为,考这样的题,没多大意思,因为各个编译器不同,出来的结果也不同,而且这样有一种误导,而失去了语言学习的本质,真正的程序员,也不会写出这样的代码,除非有神经病,呵呵。但是我们还是得面对……悲哀啊。下面我用汇编来分析一下这个例子:
1 | #include "stdio.h" |
我在VC++.NET下编译通过,结果为:n=9,i=3 让我用STUDIO2003.NET的调试器来分析一下: 相关反汇编代码如下:(各语句后面有我的注释)
1 | int i=0,n = 0; |
从以上反汇编过程可以看出,n=9,i=3,输出后也如此。输出的汇编代码就不贴了。
不同的编译器输出的结果可能不同,我想,可能反汇编出来的代码也不一样,所以结果自然也不一样了。
从这里可以看出,通过反汇编的代码来分析,思路会清晰很多。studio2003.net的编译器,对于(++i)+(++i)+(++i);这种运算,是先算i,也即将三个++i先算出来,结果等于3,然后才算括号外面的加法,结果当然是n=3+3+3=9。
这是一个有趣的GDI图形测试程序,其主要功能就是获取在桌面上绘图,结果是……你的桌面会一团遭,像病毒一样,看了你会吓一跳,但是有惊无险 :)
一下程序在VC++.NET下编译通过。
#include “stdafx.h”
#include “stdlib.h”
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBox(NULL,”Written by Cooldog. :)”,”hehe…”,MB_OK); HDC myDC; //获取屏幕分辨率 RECT ScrRect; GetWindowRect(GetDesktopWindow(), &ScrRect); myDC=GetDC((HWND)0); //获取设备环境 for(long i=0;i<100000L;i++) { int x, y, width, height; x=(rand()%ScrRect.right)/2-(width=rand()%3); //随机产生坐标 y=rand()%ScrRect.bottom-(height=rand()%3); BitBlt(myDC, x, y, x+width, y+height, myDC, x+rand()%3-1, y-rand()%2, SRCCOPY); //绘图 } ReleaseDC((HWND)0, myDC); InvalidateRect(0, NULL, TRUE); return 0; }
本文一介绍用修改API头五个字节的方法在Win2k下的使用。利用Win2k为我们提供了一个强大的内存Api操作函数—VirtualProtectEx,WriteProcessMemeory,ReadProcessMemeory,有了它们我们就能在内存中动态修改代码了,其原型为: BOOL VirtualProtectEx( HANDLE hProcess, // 要修改内存的进程句柄 LPVOID lpAddress, // 要修改内存的起始地址 DWORD dwSize, // 修改内存的字节 DWORD flNewProtect, // 修改后的内存属性 PDWORD lpflOldProtect // 修改前的内存属性的地址 ); BOOL WriteProcessMemory( HANDLE hProcess, // 要写进程的句柄 LPVOID lpBaseAddress, // 写内存的起始地址 LPVOID lpBuffer, // 写入数据的地址 DWORD nSize, // 要写的字节数 LPDWORD lpNumberOfBytesWritten // 实际写入的子节数 ); BOOL ReadProcessMemory( HANDLE hProcess, // 要读进程的句柄 LPCVOID lpBaseAddress, // 读内存的起始地址 LPVOID lpBuffer, // 读入数据的地址 DWORD nSize, // 要读入的字节数 LPDWORD lpNumberOfBytesRead // 实际读入的子节数 ); 具体的参数请参看MSDN帮助。在Win2k下因为Dll和所属进程在同一地址空间,这点又和Win9x/me存在所有进程存在共享的地址空间不同, 因此,必须通过钩子函数和远程注入进程的方法,这样,便能挂接所有进程的API了。现以一个简单采用钩子函数对MessageBoxA进行拦截例子来说明: 其中Dll文件为: HHOOK g_hHook; HINSTANCE g_hinstDll; FARPROC pfMessageBoxA; int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption,UINT uType); BYTE OldMessageBoxACode[5],NewMessageBoxACode[5]; HMODULE hModule ; DWORD dwIdOld,dwIdNew; BOOL bHook=false; void HookOn(); void HookOff(); BOOL init(); LRESULT WINAPI MousHook(int nCode,WPARAM wParam,LPARAM lParam); BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: if(!init()) { MessageBoxA(NULL,”Init”,”ERROR”,MB_OK); return(false); } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: if(bHook) UnintallHook(); break; } return TRUE; } LRESULT WINAPI Hook(int nCode,WPARAM wParam,LPARAM lParam)//空的钩子函数 {
return(CallNextHookEx(g_hHook,nCode,wParam,lParam)); } HOOKAPI2_API BOOL InstallHook()//输出安装空的钩子函数 { g_hinstDll=LoadLibrary(“HookApi2.dll”); g_hHook=SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)Hook,g_hinstDll,0); if (!g_hHook) { MessageBoxA(NULL,”SET ERROR”,”ERROR”,MB_OK); return(false); }
return(true); } HOOKAPI2_API BOOL UninstallHook()//输出御在钩子函数 {
return(UnhookWindowsHookEx(g_hHook)); }
BOOL init()//初始化得到MessageBoxA的地址,并生成Jmp XXX(MyMessageBoxA)的跳转指令 { hModule=LoadLibrary(“user32.dll”); pfMessageBoxA=GetProcAddress(hModule,”MessageBoxA”); if(pfMessageBoxA==NULL) return false; _asm { lea edi,OldMessageBoxACode mov esi,pfMessageBoxA cld movsd movsb } NewMessageBoxACode[0]=0xe9;//jmp MyMessageBoxA的相对地址的指令 _asm { lea eax,MyMessageBoxA mov ebx,pfMessageBoxA sub eax,ebx sub eax,5 mov dword ptr [NewMessageBoxACode+1],eax } dwIdNew=GetCurrentProcessId(); //得到所属进程的ID dwIdOld=dwIdNew; HookOn();//开始拦截 return(true); }
int WINAPI MyMessageBoxA(HWND hWnd, LPCTSTR lpText,LPCTSTR lpCaption, UINT uType )//首先关闭拦截,然后才能调用被拦截的Api 函数 { int nReturn=0; HookOff(); nReturn=MessageBoxA(hWnd,”MessageBox已经被截获,呵呵!”,lpCaption,uType); HookOn(); return(nReturn); } void HookOn() { HANDLE hProc; dwIdOld=dwIdNew; hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld);//得到所属进程的句柄 VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld);//修改所属进程中MessageBoxA的前5个字节的属性为可写 WriteProcessMemory(hProc,pfMessageBoxA,NewMessageBoxACode,5,0);//将所属进程中MessageBoxA的前5个字节改为JMP 到MyMessageBoxA VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld);//修改所属进程中MessageBoxA的前5个字节的属性为原来的属性 bHook=true; } void HookOff()//将所属进程中JMP MyMessageBoxA的代码改为Jmp MessageBoxA { HANDLE hProc; dwIdOld=dwIdNew; hProc=OpenProcess(PROCESS_ALL_ACCESS,0,dwIdOld); VirtualProtectEx(hProc,pfMessageBoxA,5,PAGE_READWRITE,&dwIdOld); WriteProcessMemory(hProc,pfMessageBoxA,OldMessageBoxACode,5,0); VirtualProtectEx(hProc,pfMessageBoxA,5,dwIdOld,&dwIdOld); bHook=false; } //测试文件: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
if(!InstallHook()) { MessageBoxA(NULL,”Hook Error!”,”Hook”,MB_OK); return 1; } MessageBoxA(NULL,”TEST”,”TEST”,MB_OK);//可以看见TEST变成我们截获后的字符串了。而且是所有进程都被截获。 if(!UninstallHook()) { MessageBoxA(NULL,”Uninstall Error!”,”Hook”,MB_OK); return 1; } return 0; }
以上程序在WIN2K,VC7.0下调试并通过
图片:
挂接前,直接调用MessageBox函数,效果如下:
APIHOOK挂接程序界面:
MessageBox被截获后的效果:(在不同进程实现)
在这里,我重新编制了一个简单的程序进行测试,直接调用MessageBox函数,出现以下画面,表明系统所有进程的MessageBox都被挂接成功。