sandboxie没有生态,除了大佬多数和我一样是新手。
开源后业余时间断续学习,啃别人代码,理解他人思路属实不易,源码笔记有点乱序,后经过整理才有这篇文章,分享学习过程中遇到问题和思路。
本篇文章并不是轻量级沙盘实现原理,关注者可以直接略过该文。
共37页,部分内容可能不严谨,但是会随着学习的深入不断完善和改正错误。
预计分为4篇:
1. 进程篇
2. 监控篇
3. toB(正在研究的阶段)
4. 原理机制
Ps:多少参杂了一些逆向,LPC通信Handler处理用windbg附加SbieSvc服务,利用Start.exe挂调试,IDA定位分析/源码辅助学习,调试过程有些问题拿捏不准,感谢前辈的帮助。
SandBoxie循序渐进耳(进程篇)
序
关于沙箱,工作之外断断续续学习走过不少坑,去年部署Cuckoo用来个人学习优化Monitor。也尝试过开发轻量级沙盘,逆向Sandboxie连皮毛都没学到,幸好今年SandBoxie开源给学习者带来了更多的思路。
作者推荐了代码审计重点,个人喜欢从界面开始熟悉,交互是响应各功能模块第一步,相对比较友好,较快的理解软件流程的来龙去脉。
推荐一个活跃的sandboxie生态论坛分支,也许你会找到想要的答案:
https://www.wilderssecurity.com/forums/sandboxing-virtualization.98/
SandBoxie:
源码编译:
1. 如果系统已有wdk7600环境,vs2015配置7600,请参考下述文章:
https://www.cnblogs.com/iBinary/p/8290595.html
2. 解决error LNK2001: 无法解析的外部符号 _memcmp/_wcsnicmp等被引用原因:这个过程浪费了大量的业余时间,引入各类函数lib无济于事,修复skd环境,大量的obj报错最终参考msdn文档解决,在Sandboxie的论坛上也发现了解决方案。
https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=vs-2019
https://community.sophos.com/products/sandboxie/f/forum/119641/important-sandboxie-open-source-code-is-available-for-download
3. 解决MinFilter编译被引用,属性-->链接器-->输入-->附加依赖项目:fltMgr.lib
4. 编译成功,不错的开端(vs2017同样编译成功,v141)。
安装沙盘:
这个步骤该阶段不太重要,不影响源码学习。Install/ReadMe按照步骤来做,NSIS已经更新到了v3,按照步骤安装和替换文件。Sandbox.sln同级目录下创建tools/iconv,解压拷贝,这些工作一次性便适用,不需要重复工。
DavidXanatos推出了SandBoxie-Plush,使用了QT,如果不加入其它操作系统分析引擎(可移植),Win下面向界面便捷开发更倾向于使用Libuidk实现(基于MFC商用框架已开源-国外人可能也不知道),容易重构。
https://github.com/sandboxie-plus/Sandboxie
源码学习:
可以下载发行版Sandboxie安装到主机,如果熟悉MFC这将是好的开端,能快速定位代码,控件响应和功能模块,代码重在理解和应用,汲取自己需要的知识。
创建沙盘(r3)
Create New SandBox找到对应的类
1. 输入沙盘名称做规则检测,如下所示:
// 1. 长度小于等于32
if (len <= 32) {
// 2. 字符串是否合规
for (; i < len; ++i) {
if (name[i] >= L'0' && name[i] <= L'9')
continue;
if (name[i] >= L'A' && name[i] <= L'Z')
continue;
if (name[i] >= L'a' && name[i] <= L'z')
continue;
if (name[i] == L'_')
continue;
break;
}
}
// 3. 如果违规失败
if (i == 0 || i != len)
errmsg = MSG_3667;
2.调用SbieApi_IsBoxEnabled()判断沙盘名称是否已创建,发送API_SBIEDRV_CTLCODEDLL查询,封装了两个重要的参数:
1. API_IS_BOX_ENABLED(Driver API Codes)
2. Name_Address
3. Drv驱动 --> api.c(370行开始),对分发进行校验和处理:
3. 校验API_SBIEDRV_CTLCODEDLL
4. 分发api,回调查询code-->API_IS_BOX_ENABLED
__try {
ProbeForRead(
buf, sizeof(ULONG64) * API_NUM_ARGS, sizeof(ULONG64));
// 1. 拷贝r3传递过来的数据user_args
memzero(user_args, sizeof(ULONG64) * API_NUM_ARGS);
memcpy(user_args, buf, buf_len);
func_code = (ULONG)user_args[0];
// 2. 这里对DerviceCode做校验
if (func_code > API_FIRST && func_code < API_LAST)
// 3. 获取功能函数指针
func_ptr = Api_Functions[func_code - API_FIRST - 1];
if (func_ptr) {
// 4. 返回执行状态
status = func_ptr(proc, user_args);
} else
status = STATUS_INVALID_DEVICE_REQUEST;
} __except (EXCEPTION_EXECUTE_HANDLER) {
Api_functions通过SetFunction初始化(conf_user.c-->106行),初始化分发函数.
Api_SetFunction(API_IS_BOX_ENABLED, Conf_Api_IsBoxEnabled)回调函数处理,如果不存在添加到链中,返回状态码标识是否成功,详细请见代码。
沙盘进程运行:
1. 选中沙盘点击运行,也可以直接拖到Dlg中,如下所示:
2. 思路有很多种,最简单直接的办法搜索函数,这里拖拽文件响应使用OnDropFiles,该工程也只有这一处拖拽使用,如下所示:
void CMyFrame::OnDropFiles(HDROP hDrop)
{
WCHAR path[512];
// 1. 响应拖拽消息
if (! DragQueryFile(hDrop, 0, path, sizeof(path) / sizeof(WCHAR) - 1))
path[0] = L'\0';
if (path[0]) {
CString QuotedPath = L"\"" + CString(path) + L"\"";
// 2. 进入处理函数
CMyApp::RunStartExe(QuotedPath, CString());
}
}
void CMyApp::RunStartExe(
const CString &cmd, const CString &box, BOOL wait, BOOL inherit)
{
// 3. 再次封装调用
Common_RunStartExe(cmd, box, wait, inherit);
}
3. 沙盘如何将可执行文件加载,这是一个漫长的过程,RunStartExe.cpp --> 35行Common_RunStartExe,如下所示:
//---------------------------------------------------------------------------
// Common_RunStartExe
//---------------------------------------------------------------------------
// 1. 拼接指令 "/box:__ask__ fileName
wcscpy(cmdline, L"/box:");
if (box.IsEmpty())
wcscat(cmdline, L"__ask__");
else
wcscat(cmdline, box);
wcscat(cmdline, L" ");
wcscat(cmdline, cmd);
// 2. 重点:SbieDll_RunFromHome Start.exe加载执行文件
if (! SbieDll_RunFromHome(START_EXE, cmdline, &si, &pi));
4. SbieDll_RunFromHome被定义在了proc.c进程模块,最终会通过CreateProcessW来启动文件,如下所示:
// 1. 拼接执行命令格式
if (si->lpReserved) {
inherit = TRUE;
si->lpReserved = NULL;
} else
inherit = FALSE;
if (si->dwXCountChars == tzuk && si->dwYCountChars == tzuk) {
creation_flags = ABOVE_NORMAL_PRIORITY_CLASS;
si->dwXCountChars = si->dwYCountChars = 0;
} else
creation_flags = 0;
memzero(pi, sizeof(PROCESS_INFORMATION));
// 2. 创建进程
ok = CreateProcess(NULL, path, NULL, NULL, inherit, creation_flags,
NULL, NULL, si, pi)
参数:"E:\Program Files\Sandboxie\Start.exe" /box:__ask__ "C:\Users\Administrator\Downloads\powershell.txt"
5. Start.exe,可以选择x64跟踪,也可以选择参数带入源码,解析格式box和_,查询进程信息,box:__ask__,如下所示:
if (_wcsnicmp(cmd, L"box:", 4) == 0) {
if (*tmp == L'-' &&
(! SbieApi_QueryProcessInfo(
(HANDLE)(ULONG_PTR)GetCurrentProcessId(), 0))) {
针对一些指令会进行处理,Parse_Command_Line()返回执行文件路径。
Validate_Box_Name选择使用沙盘,代码如下:
if (! disable_force_on_this_program) {
// 1. 判断标识
if (_wcsicmp(BoxName, L"__ask__") == 0 ||
_wcsicmp(BoxName, L"current") == 0) {
if (auto_select_default_box) {
wcscpy(BoxName, L"DefaultBox");
if (SbieApi_IsBoxEnabled(BoxName) != STATUS_SUCCESS)
auto_select_default_box = FALSE;
}
if (! auto_select_default_box)
// 2. DoBoxDialog选择使用沙盘,记录沙盘名称
wcscpy(BoxName, DoBoxDialog());
if (! BoxName[0]) {
if (disable_force_on_this_program) {
// might be set by DoBoxDialog
return FALSE;
}
return die(EXIT_FAILURE);
}
}
// 3. 判断沙盘是否开启,获取当前状态
if (SbieApi_IsBoxEnabled(BoxName) != STATUS_SUCCESS) {
if (run_silent)
ExitProcess(ERROR_UNKNOWN_PROPERTY);
SetLastError(0);
Show_Error(SbieDll_FormatMessage1(MSG_3204, BoxName));
return die(EXIT_FAILURE);
}
}
6. 通过return die(RestartInSandbox())完成进程启动,RestartInSandbox函数:
dir = (WCHAR *)MyHeapAlloc(1024 * sizeof(WCHAR));
GetCurrentDirectory(1020, dir);
cmd = (WCHAR *)MyHeapAlloc(16384 * sizeof(WCHAR));
cmd[0] = L'\"';
GetModuleFileName(NULL, cmd + 1, MAX_PATH);
ptr = cmd + wcslen(cmd);
wcscpy(ptr, L"\" /env:00000000_" SBIE L"_CURRENT_DIRECTORY=\"");
wcscat(ptr, dir);
wcscat(ptr, L"\" /env:=Refresh ");
ptr += wcslen(ptr);
if (run_elevated_2) {
wcscpy(ptr, L"/elevate ");
ptr = cmd + wcslen(cmd);
}
if (wait_for_process) {
wcscpy(ptr, L"/wait ");
ptr = cmd + wcslen(cmd);
}
wcscpy(ptr, ChildCmdLine);
7. 调用Callsvc.c(dll模块)处理req,req结构体下文会介绍,数据如下:
rpl = (
PROCESS_RUN_SANDBOXED_RPL
*)SbieDll_CallServer(&req->h);
SandBoxie-LPC通信
LPC参考:https://bbs.pediy.com/thread-144492.htm
8. 本地通信模块需要熟悉LPC,推荐如下:
https://bbs.pediy.com/thread-144492.htm
https://bbs.pediy.com/thread-162365.htm
Client-Server调试方式,每个人都有自己的学习习惯,文章使用的第二种方式:
1. vs执行start源码,vs执行SbieSvc源码,编译好全部工程,输出目录同一个文件夹下,安装驱动,然后进行调试。(未尝试,理论可行)
2. vs执行start源码,windbg附加SbieSvc服务,IDA+源码辅助学习。
Client:
SbieDll_RunSandboxed()
负责tagPROCESS_RUN_SANDBOXED_REQ结构数据填充,前8bit数据MSG_HEADER结构体。
// msgid是0x1205,沙盘运行进程
req->h.msgid = MSGID_PROCESS_RUN_SANDBOXED;
wcscpy(req->boxname, box_name);
req->si_flags = si->dwFlags;
req->si_show_window = si->wShowWindow;
req->creation_flags = creation_flags;
ptr = (WCHAR *)((ULONG_PTR)req + sizeof(PROCESS_RUN_SANDBOXED_REQ));
// 拷贝执行的命令
req->cmd_ofs = (ULONG)((ULONG_PTR)ptr - (ULONG_PTR)req);
req->cmd_len = cmd_len;
if (cmd_len) {
wmemcpy(ptr, cmd, cmd_len);
ptr += cmd_len;
}
*ptr = L'\0';
++ptr;
// 拷贝dir
req->dir_ofs = (ULONG)((ULONG_PTR)ptr - (ULONG_PTR)req);
req->dir_len = dir_len;
if (dir_len) {
wmemcpy(ptr, dir, dir_len);
ptr += dir_len;
}
*ptr = L'\0';
++ptr;
// 拷贝环境变量(字符巨多)
req->env_ofs = (ULONG)((ULONG_PTR)ptr - (ULONG_PTR)req);
req->env_len = env_len;
if (env_len) {
wmemcpy(ptr, env, env_len);
ptr += env_len;
}
rpl = (PROCESS_RUN_SANDBOXED_RPL *)SbieDll_CallServer(&req->h);
tagPROCESS_RUN_SANDBOXED_REQ结构,后面尾随的是消息块包括执行cmd和dir,env环境,内存数据如下所示:
SbieDll_CallServer()
负责将客户端结构数据分发至服务端处理。
_FX MSG_HEADER *SbieDll_CallServer(MSG_HEADER *req)
{
static volatile ULONG last_sequence = 0;
UCHAR curr_sequence;
THREAD_DATA *data = Dll_GetTlsData(NULL);
UCHAR spaceReq[MAX_PORTMSG_LENGTH], spaceRpl[MAX_PORTMSG_LENGTH];
NTSTATUS status;
PORT_MESSAGE *msg;
UCHAR *buf, *msg_data;
ULONG buf_len, send_len;
MSG_HEADER *rpl;
if (! data->PortHandle) {
BOOLEAN Silent = (req->msgid == MSGID_SBIE_INI_GET_VERSION ||
req->msgid == MSGID_SBIE_INI_GET_USER ||
req->msgid == MSGID_PROCESS_CHECK_INIT_COMPLETE);
// 1. 连接\\RPC Control\\SbieSvcPort
if (! SbieDll_ConnectPort(Silent))
return NULL;
}
// 连接成功,循环Dispatch
curr_sequence = (UCHAR) InterlockedIncrement(&last_sequence);
buf = (UCHAR *)req;
buf_len = req->length;
while (buf_len) {
msg = (PORT_MESSAGE *)spaceReq;
memzero(msg, data->SizeofPortMsg);
msg_data = (UCHAR *)msg + data->SizeofPortMsg;
if (buf_len > data->MaxDataLen)
send_len = data->MaxDataLen;
else
send_len = buf_len;
msg->u1.s1.DataLength = (USHORT)send_len;
msg->u1.s1.TotalLength = (USHORT)(data->SizeofPortMsg + send_len);
memcpy(msg_data, buf, send_len);
if (buf == (UCHAR *)req) {
msg_data[3] = curr_sequence;
}
buf += send_len;
buf_len -= send_len;
// 发送至server,等待回复
status = NtRequestWaitReplyPort(data->PortHandle,
(PORT_MESSAGE *)spaceReq, (PORT_MESSAGE *)spaceRpl);
if (! NT_SUCCESS(status))
break;
msg = (PORT_MESSAGE *)spaceRpl;
if (buf_len && msg->u1.s1.DataLength) {
SbieApi_Log(2203, L"early reply");
return NULL;
}
}
Service:
Sandboxie客户端循环发送MSG消息到服Svc.Pipserver.cpp(SbieSvcPort),服务端由NtReplyWaitReceivePort循环等待客户端数据,源码中封装如下:
NtReplyWaitReceivePort():
void PipeServer::Thread()
{
NTSTATUS status;
UCHAR space[MAX_PORTMSG_LENGTH], spaceReply[MAX_PORTMSG_LENGTH];
PORT_MESSAGE *msg = (PORT_MESSAGE *)space;
HANDLE hReplyPort;
PORT_MESSAGE *ReplyMsg;
hReplyPort = m_hServerPort;
ReplyMsg = NULL;
while (1) {
if (ReplyMsg) {
memcpy(spaceReply, ReplyMsg, ReplyMsg->u1.s1.TotalLength);
ReplyMsg = (PORT_MESSAGE *)spaceReply;
}
status = NtReplyWaitReceivePort(hReplyPort, NULL, ReplyMsg, msg);
if (! m_hServerPort) // service is shutting down
break;
if (ReplyMsg) {
hReplyPort = m_hServerPort;
ReplyMsg = NULL;
if (! NT_SUCCESS(status))
continue; // ignore errors on client port
} else if (! NT_SUCCESS(status)) {
if (status == STATUS_UNSUCCESSFUL) {
// can be considered a warning rather than an error
continue;
}
break; // abort on errors on server port
}
if (msg->u2.s2.Type == LPC_CONNECTION_REQUEST) {
PortConnect(msg);
} else if (msg->u2.s2.Type == LPC_REQUEST) {
// 查找客户端client
CLIENT_THREAD *client = (CLIENT_THREAD *)PortFindClient(msg);
if (! client)
continue;
if (! client->replying)
PortRequest(client->hPort, msg, client);
msg->u2.ZeroInit = 0;
if (client->replying)
PortReply(msg, client);
else {
msg->u1.s1.DataLength = (USHORT) 0;
msg->u1.s1.TotalLength = sizeof(PORT_MESSAGE);
}
hReplyPort = client->hPort;
ReplyMsg = msg;
client->in_use = FALSE;
} else if (msg->u2.s2.Type == LPC_PORT_CLOSED ||
msg->u2.s2.Type == LPC_CLIENT_DIED) {
PortDisconnect(msg);
}
}
}
构造PORT_MESSAGE数据包,data->MaxDataLen数值是288bit,TotalLength总大小是328bit,包含结构体本身大小,LPC传输超过256bit进行内存共享传输。
msg = (PORT_MESSAGE *)spaceReq;
memzero(msg, data->SizeofPortMsg);
msg_data = (UCHAR *)msg + data->SizeofPortMsg;
if (buf_len > data->MaxDataLen)
send_len = data->MaxDataLen;
else
send_len = buf_len;
msg->u1.s1.DataLength = (USHORT)send_len;
msg->u1.s1.TotalLength = (USHORT)(data->SizeofPortMsg + send_len);
memcpy(msg_data, buf, send_len);
.......
// buf_len <= 0时候意味着客户端传输完成
buf += send_len;
buf_len -= send_len;
windbg下断SbieSvc.exe-->NtReplyWaitReceivePort,客户端发送MSG至服务端消息队列,也可以直接审计源码,如下所示:
Sbiesvc.exe拖入ida,定位代码段:
LPC处理LPC_CONNECTION_REQUEST,LPC_REQUEST,LPC_PORT_CLOSED,LPC_CLIENT_DIED类型。
客户端client->replying标志,客户端在循环传输过程中调用PortRequest。
if (! client->replying)
PortRequest(client->hPort, msg, client);
PortRequest():
if (! client->buf_hdr) {
ULONG *msg_Data = (ULONG *)msg->Data;
ULONG msgid = msg_Data[1];
client->sequence = ((UCHAR *)msg_Data)[3];
((UCHAR *)msg_Data)[3] = 0;
buf_len = msg_Data[0];
if (msgid && buf_len &&
buf_len < MAX_REQUEST_LENGTH &&
buf_len >= sizeof(MSG_HEADER) &&
buf_len >= msg->u1.s1.DataLength) {
// 申请内存
client->buf_hdr = AllocMsg(buf_len);
client->buf_ptr = (UCHAR *)client->buf_hdr;
}
if (! client->buf_hdr) {
client->sequence = 0;
goto finish;
}
// 拷贝客户端发来的消息
memcpy(client->buf_ptr, msg->Data, msg->u1.s1.DataLength);
client->buf_ptr += msg->u1.s1.DataLength;
buf_len += msg->u1.s1.DataLength;
// MSG_HEADER msgid
client->buf_hdr = (MSG_HEADER *)buf_ptr;
client->buf_ptr = (UCHAR *)buf_ptr
client->replying = TRUE;
// 判断是否接收数据完毕
if (buf_len < client->buf_hdr->length)
return;
// 接收完毕,LPC handler功能分发处理
buf_ptr = CallTarget(client->buf_hdr, PortHandle, msg);
CallTarget():
负责LPC Handler函数分发,LPC Handler根据Msgid执行功能函数。
定位分发函数,断点0x1205观察,通过搜索定位函数代码,如下所示:
*ProcessServer::Handler()
IDA-Windbg对比源码无误,下断g运行,如下所示:
运行后会触发LPC回调分发,MSGID_PROCESS_RUN_SANDBOXED调用号断下1205,ProcessServer.cpp(line:428),启动新进程主模块。
*ProcessServer::RunSandboxedHandler()
MSG_HEADER *ProcessServer::RunSandboxedHandler(MSG_HEADER *msg)
获取cmd,dir,env数据
WCHAR *cmd = RunSandboxedCopyString(&req->h, req->cmd_ofs, req->cmd_len);
WCHAR *dir = RunSandboxedCopyString(&req->h, req->dir_ofs, req->dir_len);
WCHAR *env = RunSandboxedCopyString(&req->h, req->env_ofs, req->env_len);
*cmd参数如下:
如果调用方是沙盒,pid号将确定API_START_PROCESS调用的BoxNameOrModel Pid参数,如果是沙箱外部的呼叫者则指定了一个框名。
SbieApi_QueryProcessInfo()
复制和初始化,调用Ioctl来进行数据初始化
__declspec(align(8)) ULONG64 ResultValue;
__declspec(align(8)) ULONG64 parms[API_NUM_ARGS];
API_QUERY_PROCESS_INFO_ARGS *args = (API_QUERY_PROCESS_INFO_ARGS *)parms;
memzero(parms, sizeof(parms));
args->func_code = API_QUERY_PROCESS_INFO;
args->process_id.val64 = (ULONG64)(ULONG_PTR)ProcessId;
args->info_type.val64 = (ULONG64)(ULONG_PTR)info_type;
args->info_data.val64 = (ULONG64)(ULONG_PTR)&ResultValue;
SbieApi_Ioctl()
1. 初始化 "\\Device\\" SANDBOXIE L"DriverApi"对象
2. NtOpenFile-->DriverApi
3. 发送API_SBIEDRV_CTLCODE控制码
RunSandboxedGetToken()
如果指定了SYSTEM/THREAD权限,将会对应的权限处理
ok = OpenProcessToken(
CallerProcessHandle, TOKEN_RIGHTS, &OldTokenHandle);
打开权限,建一个新的访问主令牌来复制一个已经存在的标记
ok = DuplicateTokenEx(OldTokenHandle, TOKEN_RIGHTS, NULL,
SecurityIdentification, TokenPrimary,
&NewTokenHandle);
获取SessionId会话,设置
ULONG SessionId = PipeServer::GetCallerSessionId();
ok = SetTokenInformation(NewTokenHandle, TokenSessionId,
&SessionId, sizeof(ULONG));
通知驱动程序并启动新进程,然后将句柄复制:
if (RunSandboxedStartProcess(
PrimaryTokenHandle, BoxNameOrModelPid, CallerPid,
cmd, dir, env, &req->creation_flags, &si, &pi)) {
if (RunSandboxedDupAndCloseHandles(
CallerProcessHandle, req->creation_flags,
&pi, &piReply)) {
err = 0;
lvl = 0;
} else {
err = GetLastError();
lvl = 0x55;
}
} else {
err = GetLastError();
lvl = 0x44;
}
RunSandboxedStartProcess():
设置相关的会话权限和si结构体拷贝,利用CreateProCessAsUser启动特定的子进程
// create new process
ok = CreateProcessAsUser(
PrimaryTokenHandle, NULL, cmd, NULL, NULL, FALSE,
crflags2, env, dir, si, pi);
如果启动成功,需要通知驱动,明确进程pid
LONG rc = SbieApi_CallTwo(API_START_PROCESS,
BoxNameOrModelPid, pi->dwProcessId);
执行RunSandBoxedStartProcess之后并非直接进程启动,还需要历经一些分发函数处理.
Msgid = Rax = 0x1e01 = MSGID_QUEUE_CREATE
*QueueServer::Handler()
*QueueServer::CreateHandler()
请求检测是否再沙盘内,如果成功查找请求对象。
QUEUE_OBJ *QueueObj = (QUEUE_OBJ *)FindQueueObj(QueueName);
if (QueueObj) {
status = STATUS_OBJECT_NAME_COLLISION;
goto finish;
}
获取进程时间相关信息。
PortReply():
负责拷贝Msg消息,代码如下所示:
msg->u2.ZeroInit = 0;
if (client->replying)
PortReply(msg, client);
else {
msg->u1.s1.DataLength = (USHORT) 0;
msg->u1.s1.TotalLength = sizeof(PORT_MESSAGE);
}
PortDisconnect():
关闭释放连接
NotifyTargets():
这部分笔记只是一些msgid调用记录通信完成后关闭,回调处理一些msgid=xxff的消息,windbg调用流程简单梳理了一下关闭连接后调用的回调函数顺序如下。
TARGET *target = (TARGET *)List_Head(&m_targets);
while (target) {
msg.msgid = target->serverId | 0xFF;
(*target->handler)(target->context, &msg);
target = (TARGET *)List_Next(target);
}
*ProcessServer::Handler()
第一次循环IRP Handler进入到Handler函数,msgids.h定义消息宏如下:
eax = 0x12ff,Handler函数没有对该消息号有处理,线性执行代码返回。
#define MSGID_PROCESS 0x1200
#define MSGID_PROCESS_CHECK_INIT_COMPLETE 0x1201
#define MSGID_PROCESS_GET_WORK_DEPRECATED 0x1202
#define MSGID_PROCESS_KILL_ONE 0x1203
#define MSGID_PROCESS_KILL_ALL 0x1204
#define MSGID_PROCESS_RUN_SANDBOXED 0x1205
#define MSGID_PROCESS_SET_DEVICE_MAP 0x1206
#define MSGID_PROCESS_OPEN_DEVICE_MAP 0x1207
*SbieIniServer::Handler()
第二次循环eax =0x18ff,进入到SbieIniServer回调中,Windbg偏移+IDA定位对比源码无误,如下所示:
SbieIniServer *pThis = (SbieIniServer *)_this;
EnterCriticalSection(&pThis->m_critsec);
pThis->m_text = NULL;
pThis->m_text_base = NULL;
pThis->m_text_max_len = 0;
pThis->m_insertbom = FALSE;
// 内部调用Handler2,用来请求判断,处理获取数据和验证参数。
MSG_HEADER *rpl = pThis->Handler2(msg);
同样eax=0x18ff不在Handler处理范围之内,线性执行,回到主循环。
if (pThis->m_text)
HeapFree(GetProcessHeap(), 0, pThis->m_text_base);
pThis->m_text = NULL;
pThis->m_text_base = NULL;
pThis->m_text_max_len = 0;
LeaveCriticalSection(&pThis->m_critsec);
*ServiceServer::Handler()
处理宏消息如下:
#define MSGID_SERVICE 0x1300
#define MSGID_SERVICE_START 0x1301
#define MSGID_SERVICE_QUERY 0x1302
#define MSGID_SERVICE_LIST 0x1303
#define MSGID_SERVICE_RUN 0x1304
#define MSGID_SERVICE_UAC 0x1305
Eax= 0x13ff,仍然只是进入了ServiceServer回调,调用了TlsGetValue()函数,不做宏处理分发,用到时候再来分析该函数。
*PStoreServer::Handler()
Eax=0x11ff,不做分发处理
*TerminalServer::Handler()
#define MSGID_TERMINAL 0x1400
#define MSGID_TERMINAL_QUERY_INFO 0x1411
#define MSGID_TERMINAL_CHECK_TYPE 0x1412
#define MSGID_TERMINAL_GET_NAME 0x1413
#define MSGID_TERMINAL_GET_PROPERTY 0x1414
#define MSGID_TERMINAL_DISCONNECT 0x1415
Eax= 0x14ff,不做分发处理.
*NamedPipeServer::Handler()
#define MSGID_NAMED_PIPE 0x1500
#define MSGID_NAMED_PIPE_OPEN 0x1501
#define MSGID_NAMED_PIPE_CLOSE 0x1502
#define MSGID_NAMED_PIPE_SET 0x1503
#define MSGID_NAMED_PIPE_READ 0x1504
#define MSGID_NAMED_PIPE_WRITE 0x1505
#define MSGID_NAMED_PIPE_LPC_CONNECT 0x1506
#define MSGID_NAMED_PIPE_LPC_REQUEST 0x1507
#define MSGID_NAMED_PIPE_ALPC_REQUEST 0x1508
#define MSGID_NAMED_PIPE_NOTIFICATION 0x15FF
Eax= 15ff,分发处理
NamedPipeServer *pThis = (NamedPipeServer *)_this;
HANDLE idProcess = (HANDLE)(ULONG_PTR)PipeServer::GetCallerProcessId();
if (msg->msgid == MSGID_NAMED_PIPE_NOTIFICATION) {
pThis->NotifyHandler(idProcess);
return NULL;
}
*FileServer::Handler()
#define MSGID_FILE 0x1700
#define MSGID_FILE_SET_ATTRIBUTES 0x1701
#define MSGID_FILE_SET_SHORT_NAME 0x1702
#define MSGID_FILE_LOAD_KEY 0x1703
#define MSGID_FILE_GET_ALL_HANDLES 0x1704
#define MSGID_FILE_IMPERSONATION_REQUESTS 0x1780
#define MSGID_FILE_SET_REPARSE_POINT 0x1781
#define MSGID_FILE_OPEN_WOW64_KEY 0x1782
#define MSGID_FILE_CHECK_KEY_EXISTS 0x1783
FileServer *pThis = (FileServer *)_this;
HANDLE idProcess = (HANDLE)(ULONG_PTR)PipeServer::GetCallerProcessId();
if (0 != SbieApi_QueryProcess(idProcess, NULL, NULL, NULL, NULL))
return SHORT_REPLY(STATUS_ACCESS_DENIED);
SbieApi_QueryProcess(){
return SbieApi_QueryProcessEx2(
ProcessId, 96, out_box_name_wchar34, out_image_name_wchar96,
out_sid_wchar96, out_session_id, NULL);
}
SbieApi_QueryProcessEx2负责参数的校验和数据配置
NTSTATUS status;
// 定义结构
__declspec(align(8)) UNICODE_STRING64 BoxName;
__declspec(align(8)) UNICODE_STRING64 ImageName;
__declspec(align(8)) UNICODE_STRING64 SidString;
__declspec(align(8)) ULONG64 parms[API_NUM_ARGS];
API_QUERY_PROCESS_ARGS *args = (API_QUERY_PROCESS_ARGS *)parms;
memzero(parms, sizeof(parms));
args->func_code = API_QUERY_PROCESS;
args->process_id.val64 = (ULONG64)(ULONG_PTR)ProcessId;
status = SbieApi_Ioctl(parms);重置参数设置。
*ComServer::Handler()
Eax= 0x1bff,分发处理:
ComServer *pThis = (ComServer *)_this;
HANDLE idProcess = (HANDLE)(ULONG_PTR)PipeServer::GetCallerProcessId();
if (msg->msgid == MSGID_COM_NOTIFICATION) {
pThis->NotifyAllSlaves(idProcess);
return NULL;
}
NotifyAllSlaves()函数用于创建一个线程发送同通知
ULONG64 *ThreadData = (ULONG64 *)
HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG64) * 2);
if (! ThreadData)
return;
ThreadData[0] = (ULONG64)this;
ThreadData[1] = (ULONG64)(ULONG_PTR)idProcess;
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)NotifyAllSlaves2,
ThreadData, 0, &idThread);
NotifyAllSlaves2()线程回调,ThreadData堆内存
ComServer *pThis = (ComServer *)ThreadData[0];
ULONG_PTR idProcess = (ULONG)ThreadData[1];
HeapFree(GetProcessHeap(), 0, ThreadData);
pThis->NotifyAllSlaves3((HANDLE)idProcess);
NotifyAllSlaves3()这一块理解有些偏差,后续深入后再填充
*IpHlpServer::Handler()
#define MSGID_IPHLP 0x1C00
#define MSGID_IPHLP_CREATE_FILE 0x1C01
#define MSGID_IPHLP_CLOSE_HANDLE 0x1C02
#define MSGID_IPHLP_SEND_ECHO 0x1C03
#define MSGID_IPHLP_NOTIFICATION 0x1CFF
Eax=0x1cff,分发处理
IpHlpServer *pThis = (IpHlpServer *)_this;
HANDLE idProcess = (HANDLE)(ULONG_PTR)PipeServer::GetCallerProcessId();
if (msg->msgid == MSGID_IPHLP_NOTIFICATION) {
pThis->NotifyHandler(idProcess);
return NULL;
}
NotifyHandler()
m_ProxyHandle->ReleaseProcess(idProcess);
if (proxy->refcount == 0) {
m_close_callback(m_context_for_callback, &proxy->data);
List_Remove(&m_list, proxy);
HeapFree(m_heap, 0, proxy);
}
再次循环仍会进入lpHipServer:Handler(),eax = 0x1cff
loc_140014400
.text:0000000140014400 cmp dword ptr [rdx+4], 1A01h ;
cmp dword ptr [rdx+4],1A01h ds:0000005f`387ffb84=00001aff
.text:0000000140014407 jz sub_140014650
.text:000000014001440D xor eax, eax
.text:000000014001440F retn
#define MSGID_NETAPI 0x1A00
#define MSGID_NETAPI_USE_ADD 0x1A01
//#define MSGID_NETAPI_WKSTA_GET_INFO 0x1A01
//#define MSGID_NETAPI_SERVER_GET_INFO 0x1A02
清除eax标志
*QueueServer::Handler()
#define MSGID_QUEUE 0x1E00
#define MSGID_QUEUE_CREATE 0x1E01
#define MSGID_QUEUE_GETREQ 0x1E02
#define MSGID_QUEUE_PUTRPL 0x1E03
#define MSGID_QUEUE_PUTREQ 0x1E04
#define MSGID_QUEUE_GETRPL 0x1E05
#define MSGID_QUEUE_NOTIFICATION 0x1EFF
Eax=0x1eff,处理分发
QueueServer *pThis = (QueueServer *)_this;
HANDLE idProcess = (HANDLE)(ULONG_PTR)PipeServer::GetCallerProcessId();
if (msg->msgid == MSGID_QUEUE_NOTIFICATION) {
pThis->NotifyHandler(idProcess);
return NULL;
}
QueueServer::NotifyHandler()删除进程对象
*EpMapperServer::Handler()
#define MSGID_EPMAPPER 0x1F00
#define MSGID_EPMAPPER_GET_PORT_NAME 0x1F01
EpMapperServer *pThis = (EpMapperServer *)_this;
if (msg->msgid == MSGID_EPMAPPER_GET_PORT_NAME)
return pThis->EpmapperGetPortNameHandler(msg);
return NULL;
总结:
windows/linux/安卓/iot沙箱思维是相通的,当然监控元数据都不一样,检测矩阵不一样。国内外关于演讲沙箱设计都会经过多个维度升华,如应用层到内核层监,内核到虚拟化,针对恶意行为设置关卡层层把控,添加漏洞元数据等。在企业中常见如沙箱-att&ck落地,也可以是攻防拦截,如elf/pe注入,窗口/进程/线程等隐藏手法,ProcessHollwing,安卓边信道劫持等不乏多个维度。
上述观点存在很多偏差,仅个人的一些理解。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-7-16 15:03
被一半人生编辑
,原因: