在windows程序中嵌入Lua脚本引擎--编写自己的Lua库
? ? ? ? 在《在windows程序中嵌入Lua腳本引擎--建立一個簡易的“云命令”執行的系統》一文中,我提到了使用Lua的ffi庫,可以讓我們像寫C代碼一樣寫lua程序。這是個非常令我們這些C程序員激動的事。但是我們使用ffi庫寫出來的程序往往比較大,因為我們可能要聲明一些API的原型和結構體。比如我們看luajit的wiki中關于使用ffi調用kernel32的一個例子。(轉載請指明出于breaksoftware的csdn博客)如果我們要使用
BOOL HeapWalk(HANDLE hHeap, PROCESS_HEAP_ENTRY * lpEntry);
? ? ? ? 則在Lua中要做如下聲明
ffi.cdef[[
typedef struct _PROCESS_HEAP_ENTRY {PVOID lpData;DWORD cbData;BYTE cbOverhead;BYTE iRegionIndex;WORD wFlags;union {struct {HANDLE hMem;DWORD dwReserved[ 3 ];} Block;struct {DWORD dwCommittedSize;DWORD dwUnCommittedSize;LPVOID lpFirstBlock;LPVOID lpLastBlock;} Region;} DUMMYUNIONNAME;
} PROCESS_HEAP_ENTRY, *LPPROCESS_HEAP_ENTRY, *PPROCESS_HEAP_ENTRY;BOOL HeapWalk(HANDLE hHeap, PROCESS_HEAP_ENTRY * lpEntry);
]]
? ? ? ? ?即告訴Lua,我們要調用的函數的名字和函數使用的結構體的原型。
? ? ? ? ?看到這,可能會讓想使用ffi庫的朋友打起了退堂鼓。那我們如何才可以比較簡潔的調用這個函數呢?那就是:編寫我們自己的Lua庫"fl"。
? ? ? ? ?我們可以參考luajit中os庫的聲明方式。
? ? ? ? 1 lualib.h中新增我們庫的名字"fl",并聲明注冊我們庫的函數luaopen_fl
……
#define LUA_FFILIBNAME "ffi"
#define LUA_FLLIBNAME "fl"
……
LUALIB_API int luaopen_ffi(lua_State *L);
LUALIB_API int luaopen_fl(lua_State *L);
? ? ? ? 2 在lib_init.c中,將我們的庫名字和打開庫的名字banding
……{ LUA_JITLIBNAME, luaopen_jit },{ LUA_FLLIBNAME, luaopen_fl },{ NULL, NULL }
? ? ? ? 3 在Lualib目錄下新建一個lib_fl.c文件
? ? ? ? 實現最基本的函數和結構。主要是實現注冊我們庫的luaopen_fl函數,和函數名和函數地址綁定結構體數組uaL_Reg_fl_lib
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_lib.h"
static const luaL_Reg fl_lib[] = {{ NULL, NULL }
};LUALIB_API int luaopen_fl(lua_State *L)
{luaL_register(L, LUA_FLLIBNAME, fl_lib);return 1;
}
? ? ? ? 4 將lib_fl.c加入《在windows程序中嵌入Lua腳本引擎--使用VS IDE編譯Luajit腳本引擎》中介紹的Lua工程。
? ? ? ? 5 修改《在windows程序中嵌入Lua腳本引擎--使用VS IDE編譯Luajit腳本引擎》中介紹的Buildvm工程的生成后事件。在事件中,將lib_fl.c加入ALL_LIB中
@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c lib_fl.c
? ? ? ? 6 在ljamalg.c中新增#include "lib_fl.c"
? ? ? ? 如此我們便新增了一個名字叫fl的庫,我們可以??
fl = require "fl"
fl.……
? ? ? ? 這樣調用它。
? ? ? ? 現在我們要擴充我的庫:
? ? ? ?A 在fl庫中新增一個獲取系統版本信息的函數
LJLIB_CF(fl_GetSystemVersion)
{OSVERSIONINFOA osver;ZeroMemory(&osver, sizeof(OSVERSIONINFOA));osver.dwOSVersionInfoSize = sizeof(osver);if ( GetVersionExA(&osver) ){lua_pushnumber(L, osver.dwMajorVersion);lua_pushnumber(L, osver.dwMinorVersion);lua_pushnumber(L, osver.dwBuildNumber);lua_pushnumber(L, osver.dwPlatformId);lua_pushstring(L, osver.szCSDVersion);return 5;}else{return 0;}
}
? ? ? ?并在fl_lib數組中新增名字和該函數的地址的綁定
static const luaL_Reg fl_lib[] = {{ "GetSystemVersion", lj_cf_fl_GetSystemVersion },{ NULL, NULL }
};
? ? ? ? ?這樣我們編譯出來的luajit便可以使用簡單的方法調用獲取系統版本了。
? ? ? ? B 在fl庫中一個獲取系統中所有進程的函數
? ? ? ? 為了讓我們的這個例子盡可能復雜,我不準備使用快照的方法去獲取進程信息。而是使用Windows未公開的函數NtQuerySystemInformation。我在之前的《使用APIHOOK實現進程隱藏》中介紹過該方法。
#include <windows.h>#define MAXLOOPCOUNT 5
#define MAXPROCSSNUM 1024#define STATUS_SUCCESS 0x00000000
#define SystemProcessInformation 5
#define STATUS_SUCCESS 0x00000000
#define STATUS_INFO_LENGTH_MISMATCH ((ULONG)0xC0000004L)typedef DWORD (WINAPI * Fun_NtQuerySystemInformation)( DWORD, PVOID, DWORD, PDWORD );typedef struct _UNICODE_STRING {USHORT Length;USHORT MaximumLength;PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;typedef struct _SYSTEM_PROCESS_INFORMATION
{DWORD dwNextEntryOffset; // 下段結構對象的偏移DWORD dwNumberOfThreads; // 線程數LARGE_INTEGER qSpareLi1; LARGE_INTEGER qSpareLi2;LARGE_INTEGER qSpareLi3;LARGE_INTEGER qCreateTime; // 創建時間LARGE_INTEGER qUserTime; // 用戶態時間LARGE_INTEGER qKernelTime; // 內核態時間UNICODE_STRING ImageName; // 文件名(非路徑)int nBasePriority; // 基本優先級DWORD dwProcessId; // 進程IDDWORD dwInheritedFromUniqueProcessId; // 父進程IDDWORD dwHandleCount; // 句柄數DWORD dwSessionId;ULONG dwSpareUl3;SIZE_T tPeakVirtualSize;SIZE_T tVirtualSize;DWORD dwPageFaultCount;DWORD dwPeakWorkingSetSize;DWORD dwWorkingSetSize;SIZE_T tQuotaPeakPagedPoolUsage;SIZE_T tQuotaPagedPoolUsage;SIZE_T tQuotaPeakNonPagedPoolUsage;SIZE_T tQuotaNonPagedPoolUsage;SIZE_T tPagefileUsage;SIZE_T tPeakPagefileUsage;SIZE_T tPrivatePageCount;LARGE_INTEGER qReadOperationCount;LARGE_INTEGER qWriteOperationCount;LARGE_INTEGER qOtherOperationCount;LARGE_INTEGER qReadTransferCount;LARGE_INTEGER qWriteTransferCount;LARGE_INTEGER qOtherTransferCount;
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;BOOL GetNtQuerySystemInfoBuffer( IN DWORD SystemInformationClass, void** lpBuffer )
{BOOL bSuccess = FALSE;HMODULE hNtDll = NULL;Fun_NtQuerySystemInformation NtQuerySystemInformation = NULL;ULONG cbBuffer = 0x0001;ULONG ulNeedBytes = cbBuffer;int nloopcount = 0;ULONG nstatus = 0;void* pBuffer = NULL;*lpBuffer = NULL;hNtDll = GetModuleHandle( L"ntdll.dll" );do {if( NULL == hNtDll ) {// 加載ntdll失敗,返回FALSEbreak;}NtQuerySystemInformation = ( Fun_NtQuerySystemInformation ) GetProcAddress( hNtDll, "NtQuerySystemInformation" );if ( NULL == NtQuerySystemInformation ) {// 獲取導出函數NtQuerySystemInformation失敗,返回FALSEbreak;}// 預分配的空間大小,先分配一個小空間,理論上使用這個空間去獲取信息是失敗的cbBuffer = 0x0001;ulNeedBytes = cbBuffer;// 分配內存用于保存進程信息,只分配不釋放,由外部釋放*lpBuffer = malloc( cbBuffer );nloopcount = 0;do {// 為了防止無限循環,做個最大循環限制if ( nloopcount > MAXLOOPCOUNT ) {break;}nloopcount++;if( NULL == *lpBuffer ) {// 分配內存失敗,返回FALSEbreak;}// 理論上,第一次執行這個函數會失敗的,因為分配的空間太小nstatus = NtQuerySystemInformation( SystemInformationClass, *lpBuffer,cbBuffer,&ulNeedBytes );if ( STATUS_INFO_LENGTH_MISMATCH == nstatus ) {// 理論上,第一次執行NtQuerySystemInformation后會進入這里進行內存的再次擴容cbBuffer = ulNeedBytes;// 重新分配內存用于保存進程信息,只分配不釋放,由外部釋放pBuffer = realloc( *lpBuffer, cbBuffer );if (pBuffer != NULL)*lpBuffer = pBuffer;continue;}else if ( STATUS_SUCCESS == nstatus ) {// 成功bSuccess = TRUE;break;}else {// 非內存大小分配不夠導致的錯誤bSuccess = FALSE;break;}} while(1);} while( 0 );if ( NULL != hNtDll ) {FreeLibrary( hNtDll );hNtDll = NULL;}if ( FALSE == bSuccess ) {// 如果獲取信息失敗,則釋放分配的內存if ( NULL != *lpBuffer ) {free( *lpBuffer );*lpBuffer = NULL;}}return bSuccess;
}
? ? ? ? 看到如上結構體,要是在lua中用ffi去聲明,豈不是很崩潰!我們再看下填充數據的輔助函數
BOOL GetProcessFullInfo(lua_State *L)
{BOOL bSuccess = FALSE;PSYSTEM_PROCESS_INFORMATION pInfo = NULL;int nloopcount = 0;do {LPVOID pBuffer = NULL;if ( FALSE == GetNtQuerySystemInfoBuffer( SystemProcessInformation, &pBuffer) ) {// 獲取失敗,直接返回,不用釋放pBuffer,因為GetZwQuerySystemInfoBuffer內部就釋放了break;}if ( NULL == pBuffer ) {break;}pInfo = ( PSYSTEM_PROCESS_INFORMATION ) pBuffer;nloopcount = 0;lua_newtable( L );while( NULL != pInfo ) {// 為了防止無限循環,做個最大進程數限制if ( nloopcount > MAXPROCSSNUM ) {break;}nloopcount++;if( 0 == pInfo->dwNextEntryOffset ) {// 找到末尾最后一個了,就退出循環break;}else {lua_pushinteger( L, pInfo->dwProcessId);//lua_newtable( L );lua_pushinteger( L, pInfo->dwInheritedFromUniqueProcessId );lua_pushlstring( L, (char*)pInfo->ImageName.Buffer, pInfo->ImageName.Length );lua_settable( L, -3 );//lua_settable( L, -3 );// 利用偏移,找到下一個結構對象pInfo = ( PSYSTEM_PROCESS_INFORMATION)( ( (PUCHAR) pInfo ) + pInfo->dwNextEntryOffset );}}if ( NULL != pBuffer ) {free( pBuffer );pBuffer = NULL;}bSuccess = TRUE;} while( 0 );return bSuccess;
}
? ? ? ? 該函數生成一個如同
struct PINFO{
DWORD dwPPID; // 父進程ID
wstring wstrProcessName; // 本進程名字
};struct PINFOEX{
DWORD dwPID; // 本進程ID
PINFO Info;
}list<PINFOEX> PINFOLIST;
? ? ? ? 的結果。
? ? ? ? 如果我們執行如此簡短的Lua腳本
fl = require "fl"
allprocessinfo = fl.GetAllProcess()
for PID,item in pairs(allprocessinfo) dofor PPID,PNAME in pairs(item) doprint(PPID,PNAME)end
end
? ? ? ? 將把我們系統中進程信息打印出來。
? ? ? ? 介紹Luajit嵌入win32程序的文章講完了。
? ? ? ? 最后,該工程源碼都在以下鏈接:http://pan.baidu.com/s/1geQ10R5 密碼:pknq
總結
以上是生活随笔為你收集整理的在windows程序中嵌入Lua脚本引擎--编写自己的Lua库的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在windows程序中嵌入Lua脚本引擎
- 下一篇: WMI技术介绍和应用——WMI概述