hint
It seems to be hard to reverse-engineer the anti-virus signature???
一
阶段一:cbc后缀是啥?
print_flag.cbc
不知道是什么格式,看起来是plaintext,都是可见ascii字符run.sh
中出现了clamav、clamscan等关键词。--database=FILE/DIR
- load virus database from FILE or load all supported db files from DIRrun.sh
中命令是加载print_flag.cbc
这个病毒数据库,并允许加载不受信任的字节码。即print_flag.cbc
这个文件是一个病毒数据库。二
阶段二:字节码签名格式是啥?
/home/yssfpwn/下载/hitcon-av/run.sh: OK
/home/yssfpwn/下载/hitcon-av/print_flag.cbc: OK----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 0.103.11
Scanned directories: 1
Scanned files: 2
Infected files: 0
Data scanned: 0.02 MB
Data read: 0.01 MB (ratio 2.00:1)
Time: 0.005 sec (0 m 0 s)
Start Date: 2024:07:13 00:11:41
End Date: 2024:07:13 00:11:41
Loading: 0s, ETA: 0s [========================>] 1/1 sigs
Compiling: 0s, ETA: 0s [========================>] 40/40 tasksC:UserswindDownloadshitcon-avprint_flag.cbc: OK
----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 1.0.6
Scanned directories: 1
Scanned files: 1
Infected files: 0
Data scanned: 0.02 MB
Data read: 0.01 MB (ratio 2.00:1)
Time: 0.362 sec (0 m 0 s)
Start Date: 2024:07:13 13:15:26
End Date: 2024:07:13 13:15:26
> sigtool --decode-sigs < print_flag.cbc
ERROR: decodesig: Invalid or not supported signature format
TOKENS COUNT: 2
-c
方法可以输出 字节码汇编/字节码中间代码(bcir是bytecode ir吧)。--info
方法可以看到字节码签名的一些基本信息,没啥用。...
--printbcir -c Print IR of bytecode signature
...> clambc print_flag.cbc -c > bytecode.txt
> clambc print_flag.cbc --info
Bytecode format functionality level: 6
Bytecode metadata:
compiler version: 0.105.0
compiled on: (1719557581) Fri Jun 28 14:53:01 2024
compiled by:
target exclude: 0
bytecode type: logical only
bytecode functionality level: 0 - 0
bytecode logical signature: PRINT_FLAG.{Fake,Real};Engine:56-255,Target:0;0;0:4d5a
virusname prefix: (null)
virusnames: 0
bytecode triggered on: files matching logical signature
number of functions: 2
number of types: 25
number of global constants: 13
number of debug nodes: 0
bytecode APIs used:
read, seek, setvirusname
> clambc print_flag.cbc --input file.bin --debug
三
阶段三:处理字节码汇编(bcir)
clambc --help
中输出IR描述提到的clamav IR of bytecode signature "setvirusname"。
类型信息表
TID KIND INTERNAL
------------------------------------------------------------------------
65: DPointerType i8*
66: DPointerType i16*
67: DPointerType i32*
68: DPointerType i64*
......
函数
####################### Function id 0 ################################
########################################################################
函数:全局变量表、变量表、常量表
GID ID VALUE
------------------------------------------------------------------------
0 [ 0]: i0 unknown
1 [ 1]: [32 x i8] unknown
2 [ 2]: [396 x i8] unknown
......
5 [ 5]: i8* unknown
......
[32 x 8]
表示数组,等价于c语言的char g1[32];
i8*
表示8位整型的指针,等价于c语言的char*。
i64
表示64位整型,其他以此类推。VID ID VALUE
------------------------------------------------------------------------
0 [ 0]: i8* argument
1 [ 1]: i32 argument
2 [ 2]: alloc i64
3 [ 3]: alloc i64
......
CID ID VALUE
------------------------------------------------------------------------
0 [303]: 7(0x7)
1 [304]: 0(0x0)
2 [305]: 0(0x0)
3 [306]: 32(0x20)
......
函数:字节码列表
BB IDX OPCODE [ID /IID/MOD] INST
------------------------------------------------------------------------
0 0 OP_BC_TRUNC [14 / 71/ 1] 19 = 1 trunc ffffffff
0 1 OP_BC_AND [11 / 56/ 1] 20 = 19 & 303
0 2 OP_BC_ICMP_EQ [21 /108/ 3] 21 = (1 == 304)
0 3 OP_BC_BRANCH [17 / 85/ 0] br 21 ? bb.92 : bb.11 4 OP_BC_ZEXT [16 / 84/ 4] 22 = 1 zext ffffffff
1 5 OP_BC_COPY [34 /174/ 4] cp 305 -> 11
1 6 OP_BC_JMP [18 / 90/ 0] jmp bb.2
......
BB=0, IDX=1
的指令20 = 19 & 303
,中的20、19、303都是变量/常量的ID,查变量表和常量表可知是id=19的局部变量 与 id=303的常量,赋值给id=20的局部变量。(char*)value
,所以直接把p无视即可。OP_BC_CALL_API
v7 = setvirusname(g2147483660, v36)
(-2147483636=2147483660=0x8000000c,详见 索引全局变量 章节)字节码汇编转C语言
索引全局变量
#define READNfrom(maxBytes, from, x, n, p)
CHECK_GT((maxBytes), (p) + (n / 8) - 1);
CHECK_EQ((p) & (n / 8 - 1), 0);
x = *(uint_type(n) *)&(from)[(p)];
TRACE_R(x)// L315
#define READN(x, n, p)
do {
if (p & 0x80000000) {
uint32_t pg = p & 0x7fffffff;
if (!pg) {
x = 0;
} else {
READNfrom(bc->numGlobalBytes, bc->globalBytes, x, n, pg);
}
} else {
READNfrom(func->numBytes, values, x, n, p);
}
} while (0)
// L329
#define READ1(x, p)
READN(x, 8, p);
x = x & 1
#define READ8(x, p) READN(x, 8, p)
#define READ16(x, p) READN(x, 16, p)
// L1090
case OP_BC_COPY * 5 + 3: {
uint32_t op;
READ32(op, BINOP(0));
WRITE32(BINOP(1), op);
break;
}
case OP_BC_COPY * 5 + 4: {
uint64_t op;
READ64(op, BINOP(0));
WRITE64(BINOP(1), op);
break;
}
四
阶段四:分析字节码签名
大致功能
找全局变量初始化内容位置
特别处理OP_BC_ICMP_SGT有符号及常量长度问题
255(0xFF)
v30 = (((char)v29) > (-1));
五
阶段五:解密
f1(input) = input ^ xor
,然后比较f1(input) == secret
f1('A'*396) = fake_secret
xor = fake_secret ^ 'A'*396
secret ^ xor = input
得到正确输入。f1(input) = input ^ fxor(input, xor)。
f1(ori_input) = ori_input^ fxor(ori_input, xor)。
f1(ori_input)
和secret
是否相等,相等就结束,否则继续第3步。fake_xor = f1(ori_input) ^ ori_input。
new_input = fake_xor ^ secret。
new_input
带入ori_input
,回到第1步开始新一轮循环。const unsigned char v2147483649bak[32] = {0x2C, 0x5, ...};
const unsigned char v2147483650bak[396] = {0x45, 0x5F, ...};
const unsigned char v2147483651bak[16] = {0x50, 0x52, ...};
const unsigned char v2147483652bak[16] = {0x50, 0x52, ...};
// 全局变量(这里直接保留全局变量索引高位的1,然后转无符号十进制整数)
unsigned char v2147483649[32] = {0x2C, 0x5, ...};
unsigned char v2147483650[396] = {0x45, 0x5F, ...}; // 对应secret
unsigned char v2147483651[16] = {0x50, 0x52, ...};
unsigned char v2147483652[16] = {0x50, 0x52, ...};
unsigned char *v2147483653;
unsigned char *v2147483654 = v2147483649;
unsigned char *v2147483655;
unsigned char *v2147483656 = v2147483650;
unsigned char *v2147483657;
unsigned char *v2147483658 = v2147483651;
unsigned char *v2147483659;
unsigned char *v2147483660 = v2147483652;int main(int argc, char *argv[]) {
//fp = fopen(argv[1], "rb");
//if(fp==NULL){
// printf("file not found!n");
//}
//f0();
unsigned char obuf[396], xor[396];
unsigned char buf[396];
// 初始化输入全为A
for(int i = 0; i < 396; i++){
buf[i] = 'A';
}
// 迭代
for(int round = 0; round < 5; round++){
// 重置一下全局变量
memcpy(v2147483649, v2147483649bak, sizeof(v2147483649));
memcpy(v2147483650, v2147483650bak, sizeof(v2147483650));
memcpy(v2147483651, v2147483651bak, sizeof(v2147483651));
memcpy(v2147483652, v2147483652bak, sizeof(v2147483652));
v2147483654 = v2147483649;
v2147483656 = v2147483650;
v2147483658 = v2147483651;
v2147483660 = v2147483652;
// 5. 记录原始输入(ori_input)
memcpy(obuf, buf, 396);
// 1. 执行f1加密输入
f1(buf, 396);
// 2. 判定是否正确(f1(ori_input)==secret)
for(int i = 0; i < 396; i++){
if(buf[i] != v2147483650[i]){
printf("%d, ", i);
}
}
// 3. 计算近似的异或数组(fake_xor)
for(int i = 0; i < 396; i++){
xor[i] = buf[i] ^ obuf[i];
}
// 4. 计算近似的正确输入(new_input)
for(int i = 0; i < 396; i++){
buf[i] = v2147483650bak[i] ^ xor[i];
}
printf("round=%dn", round);
}
for(int i = 0; i < 396; i++){
printf("0x%X, ", obuf[i]);
}
printf("n");
return 0;
}
六
阶段z:一些未验证的东西
65: DPointerType i8*
,i8是1字节。v28 = v0 + (v27 * 1);
七
赛后总结
看雪ID:wx_御史神风
https://bbs.kanxue.com/user-home-907036.htm
# 往期推荐
2、恶意木马历险记
球分享
原文始发于微信公众号(看雪学苑):HITCON CTF 2024 re AntiVirus wp clamav bytecode signature逆向
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论