Antivirus 日志
查询精神状态.jpg
https://github.com/Cisco-Talos/clamav-bytecode-compiler/blob/main/docs/user/clambc-user.pdf
文档提到,调试cbc的话可以断点在cli_vm_execute_jit函数
源码中搜到这里:
[clamav/libclamav/bytecode.c at 1d305881813ce52f1ecd5b07a10b9d64cc52f095 · Cisco-Talos/clamav (github.com)](https://github.com/Cisco-Talos/clamav/blob/1d305881813ce52f1ecd5b07a10b9d64cc52f095/libclamav/bytecode.c#L1905)
[clamav/libclamav/bytecode_nojit.c at main · Cisco-Talos/clamav (github.com)](https://github.com/Cisco-Talos/clamav/blob/main/libclamav/bytecode_nojit.c#L48)
是在 libclamav 里面的函数,ldd,然后直接复制出来
docker run -v /home/ctf/:/test/ --rm -it clamav/clamav which clamscan
docker run -v /home/ctf/:/test/ --rm -it clamav/clamav ldd /usr/bin/clamscan
/lib/ld-musl-x86_64.so.1 (0x7f453c65b000)
libclamav.so.12 => /usr/lib/libclamav.so.12 (0x7f453be19000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f453c65b000)
libclammspack.so.0 => /usr/lib/libclammspack.so.0 (0x7f453bdfe000)
libcrypto.so.3 => /usr/lib/libcrypto.so.3 (0x7f453b9b6000)
libz.so.1 => /lib/libz.so.1 (0x7f453b99c000)
libbz2.so.1 => /usr/lib/libbz2.so.1 (0x7f453b989000)
libpcre2-8.so.0 => /usr/lib/libpcre2-8.so.0 (0x7f453b8de000)
libxml2.so.2 => /usr/lib/libxml2.so.2 (0x7f453b7d0000)
libjson-c.so.5 => /usr/lib/libjson-c.so.5 (0x7f453b7c0000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7f453b79c000)
liblzma.so.5 => /usr/lib/liblzma.so.5 (0x7f453b764000)
docker run -v /home/ctf/:/test/ --rm -it clamav/clamav cp /usr/lib/libclamav.so.1 /test/
```
然后
ldd ./clamscan
linux-vdso.so.1 (0x00007ffc65da1000)
libclamav.so.12 => not found
libc.musl-x86_64.so.1 => not found
然后也复制出来
找到对应函数了,开始尝试调试
自带的clamav/clamav太精简了,运行不了ida linux_server64
在官方文档上找到了:clamav/clamav-debian
docker run \
-v /home/ctf/test/:/test/\
-p 23946:23946\
--rm -it clamav/clamav-debian\
/bin/bash


这个灵活勾选,加载完so,在cli_bytecode_run(调用jit的地方)打断点,然后再取消勾选,可以快速下断
没使用jit,断到这来了

是一大坨的虚拟机,但是碰巧找到头文件的定义了
在 bytecode.h
和 clambc.h
clambc --printbcir print_flag.cbc能打印出来IR
用 Python 写一个 ir 转 c 的解释器
其实可以先初步把ir中的变量、常量替换成c的语法,再用mermaid自动生成流程图
用库 https://github.com/QuattroMusic/PyMermaid

这是 func0 ,传进来的 v1 是 0x18c
p.0 的意思是 pointer 0,把变量 0 当作指针
v20 = arg1 & 0x7 (即 0x111 )

有点像判断下标是否 < 32,如果小于,就继续循环,否则跳到 bb.87
v11 是下标,bb.2的时候用 v11 获取字符串的字符,bb.87 的时候加1

这里很像多个else,如果判断错误,就会跳到 bb.92,即退出

所有的条件判断都与 int64_t v167 有关


两条指令的含义

通过z3尝试求解,发现这里的多条分支、计算,其实是对 i 的 switch, i 在 (0,0x20) 之间,假如 i 超出了这个范围,就会进入 bb.90 并退出


这里是 func0,大胆猜测,这里一边是 PRINT_FLAG.Fake
,另一边是 PRINT_FLAG.Real

libclammspack/mspack/mspack.h
seek()用于判断文件长度,在 func0 中判断文件长度是否是 0x18c
首先编译一个正常的 sample.exe
int main()
{
int a;
}
sample = open("./test/sample_back.exe", "rb")
testbin = open("./test/test.bin", "wb")
testbin2 = open("./test/test2.bin", "wb")
testbin3 = open("./test/test3.bin", "wb")
data = sample.read(0x18b)
data2 = data + sample.read(1)
data3 = data2 + sample.read(1)
testbin.write(data)
testbin2.write(data2)
testbin3.write(data3)
testbin.close()
testbin2.close()
testbin3.close()

可以看到,当取正常 exe 的前面 0x18c 时,就恰好OK(走了func0中间那条线),其他都会 Fake FOUND
- 取中间的exe不行,应该识别了一些特征——找到特征了,以 “MZ” 为开头
- 验证了:右边分支的就是 PRINT_FLAG.Real
目标:Real


这里的 -2147483640 是个字符串,加密的过程中也会有个字符串,这是未知的。
有一个办法,frida hook setvirusname,然后拿到字符串指针,查看附近的内存。
hook失败,似乎不支持带参数运行的程序的hook
直接ida调试,断点在setvirusname,成功拿到字符串

内存中的字符串顺序:
- key
- data
- Real
- Fake
发现有些变量是成对存在的
# 开头
v_5 -> v_163
v_6 -> v_164
# 结尾
v_5 -> v_4+1
v_6 -> v_3
既然是按位加密,如果能找到规律,那就可以省很多事

然后提前打好硬件的读取断点,在比较的时候应该就可以找到
得再提前一些,在 seek 打断点
好消息,存在heap的字符串偏移不会变,都是0x18ffc,在seek打完断点就可以打硬件读断点了
cli_bcapi_seek

各种打断点,找到比较的 cmp 指令,然后手动 dump出前 0x20 个位,找规律
断在 cli_bcapi_seek → 在 heap 里找 enc,打硬件断点 →

rsi+rdx 是 enc 临时存放的地方,打硬件断点

段到这里,红框就是比较指令,al是输入的加密,dl是enc,需要al,但是每个cmp好像都不一定,还是需要硬件断点,在 rdi+rax
歪日,还有反调试,时间长了就退出

丧心病狂)
有个办法,IDA debug + frida hook
安装ps命令,以查看pid
apt update
apt install procps
这样,hook的时候,判断dl的值是不是enc,如果是,就打印出来al,把al设置成一样的
gdb,启动!
gdb clamscan
set args --bytecode-unsigned -d /test/print_flag.cbc /test/test.bin
catch load # 暂停在动态库
run
info shared # 显示加载的动态库列表,包括基地址
b clock_gettime #
0x00007ffff7838e70 + 0x0E74C1
b cli_bcapi_seek # 记住地址,seek 的地址是 0xE0C20,cmp的地址是 0xE74C1
能断
对哦,我只要在read的时候拿到指针,最后再打印出来不就完了
b read,搜了下 cli_bcapi_read 真有,
b cli_bcapi_read
c
set $temp = $rsi
n
x/386xb $temp

可以看到,输入的文件确实存放在了这里
试试这个:
b cli_bcapi_read
b bytecode_context_reset

没事了,找到了 clamscan 自带的反调
硬件断点,启动!
set $temp = $rsi
rwatch $temp + 0x18b
x/386xb $temp
define cdd
continue
x/396xb $temp
end
define ww
set $temp = $rsi
end
怀疑是enc dump错了
x/396xb 0x55555559a03c - 0x10 - 396
没 dump 错,又试了几次,input_enc dump 错了,变换输入,有几个位是不变的
b cli_bcapi_read
b bytecode_context_reset
b gettimeofday
直接断反调试,我真是天才
set logging file gdb_log.txt
set logging on
