Logo WP

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
Article Image
Article Image

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

没使用jit,断到这来了

Article Image

clamav/libclamav/bytecode_vm.c at 1d305881813ce52f1ecd5b07a10b9d64cc52f095 · Cisco-Talos/clamav (github.com)

是一大坨的虚拟机,但是碰巧找到头文件的定义了

bytecode.hclambc.h

clambc --printbcir print_flag.cbc能打印出来IR

用 Python 写一个 ir 转 c 的解释器

其实可以先初步把ir中的变量、常量替换成c的语法,再用mermaid自动生成流程图

用库 https://github.com/QuattroMusic/PyMermaid

 

 

Article Image

这是 func0 ,传进来的 v1 是 0x18c

p.0 的意思是 pointer 0,把变量 0 当作指针

 

v20 = arg1 & 0x7 (即 0x111 )

Article Image

有点像判断下标是否 < 32,如果小于,就继续循环,否则跳到 bb.87

v11 是下标,bb.2的时候用 v11 获取字符串的字符,bb.87 的时候加1

Article Image

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

Article Image

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

Article Image
Article Image

两条指令的含义

Article Image

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

Article Image
Article Image

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

Article Image

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()
Article Image

可以看到,当取正常 exe 的前面 0x18c 时,就恰好OK(走了func0中间那条线),其他都会 Fake FOUND

  • 取中间的exe不行,应该识别了一些特征——找到特征了,以 “MZ” 为开头
  • 验证了:右边分支的就是 PRINT_FLAG.Real

目标:Real

Article Image
Article Image

 

这里的 -2147483640 是个字符串,加密的过程中也会有个字符串,这是未知的。

有一个办法,frida hook setvirusname,然后拿到字符串指针,查看附近的内存。

hook失败,似乎不支持带参数运行的程序的hook

直接ida调试,断点在setvirusname,成功拿到字符串

Article Image

内存中的字符串顺序:

  • key
  • data
  • Real
  • Fake

发现有些变量是成对存在的

# 开头
v_5 -> v_163
v_6 -> v_164

# 结尾
v_5 -> v_4+1
v_6 -> v_3

 

既然是按位加密,如果能找到规律,那就可以省很多事

Article Image

然后提前打好硬件的读取断点,在比较的时候应该就可以找到

得再提前一些,在 seek 打断点

好消息,存在heap的字符串偏移不会变,都是0x18ffc,在seek打完断点就可以打硬件读断点了

cli_bcapi_seek

Article Image

各种打断点,找到比较的 cmp 指令,然后手动 dump出前 0x20 个位,找规律

断在 cli_bcapi_seek → 在 heap 里找 enc,打硬件断点 →

Article Image

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

Article Image

段到这里,红框就是比较指令,al是输入的加密,dl是enc,需要al,但是每个cmp好像都不一定,还是需要硬件断点,在 rdi+rax

歪日,还有反调试,时间长了就退出

Article Image

丧心病狂)

有个办法,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
Article Image

可以看到,输入的文件确实存放在了这里

试试这个:

b cli_bcapi_read
b bytecode_context_reset
Article Image

没事了,找到了 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
Article Image