首页
社区
课程
招聘
[原创]Sandboxie循序渐进耳之进程篇
2020-7-15 18:18 23075

[原创]Sandboxie循序渐进耳之进程篇

2020-7-15 18:18
23075

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 被一半人生编辑 ,原因:
收藏
点赞14
打赏
分享
最新回复 (25)
雪    币: 1400
活跃值: (176)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
阿尔斯兰 2020-7-15 23:19
2
0
表哥,word2016提示文件无法打开,内容有问题
雪    币: 5291
活跃值: (11715)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
一半人生 5 2020-7-16 06:45
3
0
阿尔斯兰 表哥,word2016提示文件无法打开,内容有问题
我尝试了一下,文档正常,手机也可以查看
雪    币: 407
活跃值: (1572)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Thead 2020-7-16 09:14
4
0

雪    币: 9652
活跃值: (3810)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Willarcap 2020-7-16 09:20
5
0
建议转PDF,里面加一些声明什么的,做好知识产权保护,直接放docx容易被人拿去做盗版。
雪    币: 21437
活跃值: (983)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
suiyingjie 2020-7-16 09:23
6
0
修改下后缀名,.docx去掉x,即改为.doc再尝试打开试试
雪    币: 22979
活跃值: (3327)
能力值: (RANK:648 )
在线值:
发帖
回帖
粉丝
KevinsBobo 8 2020-7-16 10:13
7
0
楼主您好,您的文章质量较高,但是放在附件中会有两个问题:一是楼上的朋友们说的直接放docx容易被人拿去做盗版,不利于知识产权保护;二是无法被搜索引擎搜到,不利于知识的传播与学习。
有一个快速发帖的方法:将word文档另存为html,然后全部复制粘贴到编辑框中。
雪    币: 1341
活跃值: (3617)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
黑手鱼 3 2020-7-16 14:13
8
0
suiyingjie 修改下后缀名,.docx去掉x,即改为.doc再尝试打开试试
太棒了,一直打不开刚好解决
雪    币: 5291
活跃值: (11715)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
一半人生 5 2020-7-16 14:29
9
0
KevinsBobo 楼主您好,您的文章质量较高,但是放在附件中会有两个问题:一是楼上的朋友们说的直接放docx容易被人拿去做盗版,不利于知识产权保护;二是无法被搜索引擎搜到,不利于知识的传播与学习。 有一个快速发帖的方 ...
思考不周,感谢版主,虽然排版有些难堪已更新
雪    币: 5291
活跃值: (11715)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
一半人生 5 2020-7-16 14:30
10
0
Willarcap 建议转PDF,里面加一些声明什么的,做好知识产权保护,直接放docx容易被人拿去做盗版。
感谢
雪    币: 1773
活跃值: (1718)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
曹操abc 2020-7-16 14:43
11
0
感谢分享!!!!!!
雪    币: 40
活跃值: (696)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
killerfive 2020-7-16 15:00
12
0
你好,请问vs2017用的什么版本wdk
雪    币: 17842
活跃值: (59828)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
Editor 2020-7-16 15:22
13
0
感谢分享~
雪    币: 5291
活跃值: (11715)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
一半人生 5 2020-7-16 18:55
14
0
killerfive 你好,请问vs2017用的什么版本wdk
vs2017(141) + wdk7600
雪    币: 1182
活跃值: (2892)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
皮皮虾啊 2020-7-16 19:04
15
0
表哥太强了,make一下  = =|
雪    币: 7757
活跃值: (2226)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
option 2020-7-18 04:24
16
0
请教一下,ida怎么得到switch语句的
雪    币: 310
活跃值: (1912)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 2020-7-20 10:41
17
0
mark
雪    币: 12
活跃值: (696)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
MagicFuzzX 2020-7-31 18:25
18
0
lpc我记得不支持win10的那些app
雪    币: 0
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
walker苏 2020-8-8 17:52
19
0
感谢大佬分享!希望大佬继续更新。我现在也要研究这东西,希望可以相互交流
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_阿哲_537 2020-12-9 15:53
20
0
大佬我想问一下,你有遇到过报错SBIE1402和SBIE1409错误吗?  我这边是提示templates.ini文件和sandboxie.ini文件找不到Nt Status Codes是C0000034 Object not found,但是我在其他目录下确实有这两个ini文件。非常感谢!
雪    币: 239
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
lyanl 2020-12-15 09:26
21
0

感谢分享,非常不错,谢谢 !

最后于 2020-12-25 09:26 被lyanl编辑 ,原因:
雪    币: 3035
活跃值: (364)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mb_D34purZj 2020-12-20 23:01
22
0
大佬膜拜
雪    币: 160
活跃值: (341)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
气旋 2021-9-22 10:08
23
0
你好,最近我也在研究sandboxie,请问要在沙盘中程序是在什么时候真正执行的呢,它的内存又是如何布局管理的?
雪    币: 204
活跃值: (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
liuxianyue 2022-5-31 21:19
24
0
大佬膜拜
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_mahojwrm 2023-5-30 17:24
25
0
大佬膜拜 
游客
登录 | 注册 方可回帖
返回