在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库的声明方式。

在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);

在lib_init.c中,将我们的库名字和打开库的名字绑定

……  { LUA_JITLIBNAME,luaopen_jit },  { LUA_FLLIBNAME, luaopen_fl },  { NULL,NULL }

在Lualib目录下新建一个lib_fl.c文件

实现最基本的函数和结构。主要是实现注册我们库的luaopen_fl函数,以及函数名和函数地址绑定结构体数组luaL_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;}

将lib_fl.c加入《在Windows程序中嵌入Lua脚本引擎–使用VS IDE编译Luajit脚本引擎》中介绍的Lua工程。

修改《在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

在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便可以使用简单的方法调用获取系统版本了。

豆包AI编程 豆包AI编程

豆包推出的AI编程助手

豆包AI编程 483 查看详情 豆包AI编程

在windows程序中嵌入Lua脚本引擎--编写自己的Lua库

B. 在fl库中新增一个获取系统中所有进程的函数

为了让我们的这个例子尽可能复杂,我不准备使用快照的方法去获取进程信息。而是使用Windows未公开的函数NtQuerySystemInformation。我在之前的《使用APIHOOK实现进程隐藏》中介绍过该方法。

#include #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;                        // 进程ID    DWORD           dwInheritedFromUniqueProcessId;     // 父进程ID    DWORD           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失败,返回FALSE            break;        }        NtQuerySystemInformation =             ( Fun_NtQuerySystemInformation ) GetProcAddress( hNtDll, "NtQuerySystemInformation" );        if ( NULL == NtQuerySystemInformation ) {            // 获取导出函数NtQuerySystemInformation失败,返回FALSE            break;        }        // 预分配的空间大小,先分配一个小空间,理论上使用这个空间去获取信息是失败的        cbBuffer = 0x0001;        ulNeedBytes = cbBuffer;        // 分配内存用于保存进程信息,只分配不释放,由外部释放        *lpBuffer = malloc( cbBuffer );        nloopcount = 0;        do {            // 为了防止无限循环,做个最大循环限制            if ( nloopcount > MAXLOOPCOUNT ) {                break;            }            nloopcount++;            if( NULL == *lpBuffer ) {                // 分配内存失败,返回FALSE                break;            }            // 理论上,第一次执行这个函数会失败的,因为分配的空间太小            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; // 父进程IDwstring wstrProcessName; // 本进程名字};struct PINFOEX{DWORD dwPID;  // 本进程IDPINFO Info;}list PINFOLIST;

的结果。

如果我们执行如此简短的Lua脚本

fl = require "fl"allprocessinfo = fl.GetAllProcess()for PID,item in pairs(allprocessinfo) dofor PPID,PNAME in pairs(item) do  print(PPID,PNAME)endend

将把我们系统中进程信息打印出来。

在windows程序中嵌入Lua脚本引擎--编写自己的Lua库

介绍Luajit嵌入Win32程序的文章讲完了。

最后,该工程源码都在以下链接:https://www.php.cn/link/3244ef4c2aeb17c03511c93cb43caef0 密码:pknq

以上就是在windows程序中嵌入Lua脚本引擎–编写自己的Lua库的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/458506.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
新技术推出,IDEA研究院发布T-Rex模型,让用户能够直接在图像上选择“Prompt”提示
上一篇 2025年11月8日 02:32:50
VSCode如何提升代码审查效率?CodeReview插件简化代码评论流程
下一篇 2025年11月8日 02:32:54

相关推荐

  • 抖音电脑版支持什么系统_抖音电脑版系统兼容性说明

    抖音电脑版支持什么系统_抖音电脑版系统兼容性说明抖音电脑版支持什么系统_抖音电脑版系统兼容性说明抖音电脑版支持什么系统_抖音电脑版系统兼容性说明抖音电脑版支持什么系统_抖音电脑版系统兼容性说明

    抖音电脑版支持Windows 7以上64位及主流macOS系统,可通过官方客户端、安卓模拟器、网页端或开源工具安装使用,Linux仅限第三方方案。 如果您尝试在电脑上安装或运行抖音电脑版,但遇到无法启动或功能异常的情况,则可能是由于操作系统不符合软件的兼容性要求。以下是关于抖音电脑版所支持操作系统的…

    2026年8月26日 用户投稿
    100
  • Java中线程状态有哪些 图解线程生命周期的六种状态

    Java中线程状态有哪些 图解线程生命周期的六种状态Java中线程状态有哪些 图解线程生命周期的六种状态Java中线程状态有哪些 图解线程生命周期的六种状态Java中线程状态有哪些 图解线程生命周期的六种状态

    java线程生命周期包含六种状态,分别是new、runnable、blocked、waiting、timed_waiting和terminated。1. new表示线程被创建但尚未启动;2. runnable表示线程已就绪或正在运行;3. blocked表示线程因等待锁而阻塞;4. waiting表…

    2026年8月26日 用户投稿
    000
  • 飞龙汽车与华达科技达成战略合作,后续将推进股权合作事宜

    飞龙股份与华达科技达成战略合作,强强联手拓展新能源汽车及ai算力热管理市场! ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 4月3日,飞龙股份发布公告,宣布与华达汽车科技股份有限公司达成战略合作,共同应对新能源汽车和AI算力领域快速增长的温…

    2026年8月26日
    000
  • 电脑主机主板BIOS升级详细教程,避免升级风险保证系统兼容性

    电脑主机主板BIOS升级详细教程,避免升级风险保证系统兼容性电脑主机主板BIOS升级详细教程,避免升级风险保证系统兼容性电脑主机主板BIOS升级详细教程,避免升级风险保证系统兼容性电脑主机主板BIOS升级详细教程,避免升级风险保证系统兼容性

    bios升级需谨慎操作,核心步骤包括确认主板型号、下载匹配文件、准备u盘、进入bios界面、使用刷新工具、稳定供电完成升级、升级后重置设置。务必确保每一步准确无误,避免断电或文件错误导致主板损坏。 电脑主板的BIOS升级,说到底就是给主板换个新版本的“固件”,就像给手机系统打补丁一样。这个操作本身不…

    2026年8月26日 用户投稿
    200
  • CSRF(跨站请求伪造)防护的实现原理

    csrf防护通过验证请求的真实性来实现,主要方法包括使用csrf token和samesite cookie。1. csrf token方法:在用户登录后生成唯一token,嵌入表单中,服务器验证token有效性。2. samesite cookie方法:设置cookie的samesite属性为st…

    2026年8月26日
    000
  • Java中Faker的作用 解析虚拟数据

    Java中Faker的作用 解析虚拟数据Java中Faker的作用 解析虚拟数据Java中Faker的作用 解析虚拟数据Java中Faker的作用 解析虚拟数据

    faker在java中用于生成虚拟数据。它能模拟个人信息、公司信息、银行信息、互联网信息等多种类型数据,如姓名、地址、电话、邮箱等,并支持自定义规则。使用时需在项目中添加对应maven或gradle依赖,其优势包括简化测试准备、生成逼真数据、支持多语言,但存在随机性高、数据质量不稳定、性能影响等局限…

    2026年8月26日 用户投稿
    000
  • 告别手动造车:pelmered/fake-car如何解决Faker无法生成车辆数据的难题

    在项目开发过程中,我们经常需要模拟各种数据,而 Faker 是一个非常流行的 PHP 库,用于生成各种类型的虚假数据,例如姓名、地址、电话号码等等。但是,Faker 默认情况下并不支持生成车辆相关的数据,这给需要模拟车辆数据的开发者带来了不便。幸运的是,pelmered/fake-car 这个库填补…

    用户投稿 2026年8月26日
    000
  • 协程ORM(如Hyperf/Database)的使用

    如何使用hyperf/database进行协程orm操作?首先,使用基本查询获取用户记录;其次,进行关联查询和预加载;然后,使用事务管理避免死锁;最后,使用chunk()方法分批处理数据。通过这些步骤,可以充分发挥协程orm在提高并发性能和优化查询效率方面的优势。 在现代的PHP开发中,协程(Cor…

    2026年8月26日
    000
  • Win7如何使用自动关机命令?

    Win7如何使用自动关机命令?Win7如何使用自动关机命令?Win7如何使用自动关机命令?Win7如何使用自动关机命令?

    尽管我们并不总是需要用到自动开机的功能,但在某些情况下,它确实非常实用。因此,今天我们就来一起学习一下如何在windows 7系统中通过运行自动关机命令来设置定时关机。 在日常使用电脑的过程中,我们常常会在电脑上下载视频、软件或修复系统。如果一直守在旁边等待,这不仅浪费时间,还会影响我们处理其他事情…

    2026年8月26日 用户投稿
    100
  • 红果漫剧观看卡顿如何解决_红果漫剧流畅观看设置技巧

    视频卡顿可先尝试切换网络并靠近路由器增强信号,再将红果漫剧画质调至标清,清除应用缓存后更新应用及系统版本,最后重启设备或关闭后台程序以释放内存。 如果您在观看红果漫剧时遇到视频卡顿、加载缓慢或频繁缓冲的情况,这通常与网络连接、设备性能或应用设置有关。以下是解决此问题的步骤: 一、检查并优化网络环境 …

    2026年8月26日
    000
  • win8怎么查看电脑主板型号_win8查看主板型号命令与技巧

    1、使用msinfo32可查看系统型号和BIOS版本;2、通过管理员命令提示符执行wmic baseboard命令获取主板厂商、型号等详细信息;3、运行dxdiag在系统选项卡中查看BIOS信息以辅助判断主板型号。 如果您需要了解计算机的硬件信息,特别是主板型号,这对于驱动程序安装、系统升级或故障排…

    2026年8月26日
    000
  • edge浏览器快速登录指南 浏览器edge官方入口登录地址

    访问 edge.microsoft.com,登录微软账户即可跨设备同步收藏夹、密码和设置;使用 Bing 版 Microsoft Edge 可体验 AI 浏览、个性化新闻和奖励功能,下载地址为 microsoft.com/edge。 Access Microsoft Edge at edge.mic…

    2026年8月26日
    100
  • Java中如何实现动画 详解定时刷新机制

    Java中如何实现动画 详解定时刷新机制Java中如何实现动画 详解定时刷新机制Java中如何实现动画 详解定时刷新机制Java中如何实现动画 详解定时刷新机制

    java中实现动画的核心机制是定时刷新画面,利用javax.swing.timer在gui程序中安全更新界面。具体步骤为:选择jpanel等组件重写paintcomponent()绘制帧内容;创建timer设置延迟时间和actionlistener;在actionperformed()中更新动画状态…

    2026年8月26日 用户投稿
    100
  • TeamBay科技款Pro重磅升级!

    TeamBay科技款Pro重磅升级!TeamBay科技款Pro重磅升级!TeamBay科技款Pro重磅升级!TeamBay科技款Pro重磅升级!

    teambay科技款pro迎来重磅升级!65-115英寸全场景适配,全面重塑智能会议新体验。 还在因投屏延迟、远程画面模糊、书写卡顿等问题影响会议效率?TeamBay科技款Pro智能会议平板以“芯片级跃迁+全维度进化”强势登场,涵盖65/75/86/98/110/115英寸全尺寸产品矩阵,无论是10…

    2026年8月26日 用户投稿
    200
  • VSCode跳转后怎么返回_VSCode代码跳转后快速返回原位置教程

    使用“Go Back”命令(快捷键Alt+左箭头或Ctrl+-)可快速返回跳转前的位置,VSCode通过导航历史栈记录每次跳转,支持精确到行的回溯,结合面包屑、大纲视图和符号搜索等功能,大幅提升代码浏览效率。 在VSCode里,如果你不小心跳到了某个定义或引用,想要快速回到你刚才阅读的位置,最直接的…

    2026年8月26日
    000
  • ​​如何在电脑上定闹钟?Win/Mac闹钟设置教程​​

    在windows上设置闹钟需打开“闹钟和时钟”应用,点击“+”添加,设定时间、重复、声音、名称后保存;2. 在mac上可使用“日历”创建事件并设置提醒时间与方式,或使用“提醒事项”设定时间与重复,也可安装第三方闹钟应用;3. 电脑闹钟依赖设备开机且不休眠,而手机闹钟更便携独立;4. 闹钟不响常见原因…

    2026年8月26日
    000
  • 抖音短剧怎么屏蔽广高?抖音短剧

    随着抖音短剧的火爆,越来越多的人沉迷于其中。广告的频繁出现却让很多人感到烦恼。今天,就让我来给大家分享一下如何屏蔽抖音短剧中的广告,让你轻松告别广高烦恼。 一、了解抖音广告类型 在解决这个问题之前,我们先来了解一下抖音短剧中的广告类型。主要有以下几种: 1. 开屏广告:打开抖音短剧时,首先出现的全屏…

    2026年8月26日
    000
  • java中mapping是什么 对象关系映射的基本概念

    java中的mapping指的是对象关系映射(orm),它将java类映射到数据库表。1)使用orm工具如hibernate或jpa,可以将java类映射到数据库表,简化数据访问。2)orm减少了sql编写时间,提高了代码可维护性,但可能引入性能问题。3)使用orm时需理解其工作原理,考虑使用原生s…

    2026年8月26日
    000
  • Listen1如何联系技术支持_Listen1联系技术支持的详细指南

    答案是通过GitHub提交Issue、加入官方QQ群或发送邮件联系开发者。用户可依次选择这三种方式获取技术支持,需提供详细问题描述与环境信息以获得帮助。 如果您在使用Listen1时遇到功能异常或无法解决问题的情况,可能需要直接联系其技术支持团队以获得帮助。以下是几种可行的联系方式和操作步骤: 一、…

    2026年8月26日
    000
  • OlympicArena— 上海交大联合 AI Lab 等推出的多学科认知推理基准测试框架

    olympicarena:一个多学科认知推理基准测试框架 OlympicArena是由上海交通大学、上海AI Lab、苏州大学和上海交通大学生成式人工智能实验室(GAIR Lab)联合打造的多学科认知推理能力评估平台。该框架包含11,163道源自国际奥林匹克竞赛的双语题目,涵盖数学、物理、化学、生物…

    2026年8月26日
    000

发表回复

登录后才能评论
关注微信