分类: C/C++
2012-07-03 09:53:14
Hydra意思是九头蛇, 这是作者查看Mozilla项目的CFG(Control Flow Graph)映入脑海的一个印象, 所以该项目叫 Dehydra, 意思是处理这种复杂调用关系.
代码随DeHydra一起发布的是TreeHydra. 默认编译 DeHydra时, 会同时编译TreeHydra. TreeHydra比 DeHydra更高级, 也更复杂, 基于GCC的GIMPLE中间代码表示. 两者都是把gcc工作过程中某个阶段的中间结果以JavaScript对象的形式, 提供给用户以JavaScript写的静态分析代码.
DeHydra和TreeHydra都必需与支持插件机制的GCC一起使用, 最早能支持的版本可能是4.2, 推荐用4.5, 但没具体说明4.5.0到4.5.3之间的具体哪个版本, 但在mozilla网站的build例子中, 看到以4.5.3 为例.
整个build过程应该是比较麻烦的, 需要build:
GCC 4.5 with plugin enabled,
SpiderMondy javascript 引擎
DeHydra & TreeHydra
源代码以 mercurial版本控制工具管理, 所以你还得安装这个软件. 整个需要下载的源代码也非常大. 所以我这里推荐的办法是:
直接使用 Ubuntu 11, 因为它自带的是 GCC 4.6.1, 支持插件功能, 经我试用, 可以与 DeHydra/TreeHydra搭配使用. 需要安装的软件包有:
* SpiderMonkey
* DeHydra
这两个软件包默认都是没有安装的, 不过这是二进制安装, 使用 Ubuntu的包管理工具非常便利的就能安装上.
SpiderMonkey是一个动态库形式的JavaScript引擎, 它被GCC的插件机制在编译期调用.
DeHydra是以动态库形式和用作静态分析的javascript文件发布. 调用时需要用以下命令行:
gcc -fno-builtin -fplugin=/usr/share/dehydra/libs/gcc_dehydra.so -fplugin-arg-gcc_dehydra-script=static_analyze.js -c test.cpp -o null
其中 -fno-builtin 不是必需, 不过我发现在一个例子中, 使用静态分析尝试截获对 strcpy的调用时, 没有成功, 原因是gcc默认使用内置的memcpy来代替对C库中strcpy的调用. 使用了 -fno-builtin之后, 就会使用C库中的strcpy而不是内置版本了.
-fplugin= 后面的.so文件就是 dehydra包提供的动态库文件. 注意这个包没有提供 treehydra, 而手工编译dehydra是会同时编译出gcc_treehydra.so的.
静态分析的javascript文件是应用的核心, 其它功能都是这套工具本身提供的, 正是通过javascript文件, 用户可以定制自己的静态分析规则.
有4个回调函数可以使用:
process_decl(decl)
process_function(decl, body)
process_type(type)
input_end()
具体使用以及每个javascript对象的可用属性/方法, 可以参考 https://developer.mozilla.org/En/Dehydra/Object_Reference
下面是我的一个测试例子 test.js :
function process_function(decl, body)
{
let var_decl_name = decl.name;
print(var_decl_name);
for each(let bodyItem in body) {
for each(let stmtItem in bodyItem.statements) {
let stmtStr = ("stmt at " + stmtItem.loc + " :" + stmtItem.name + " in " + var_decl_name);
if (stmtItem.isFcall) {
stmtStr += "function arguments: " + stmtItem.arguments;
}
if (stmtItem.isReturn) {
stmtStr += ". Return!" ;
}
error(stmtStr);
}
}
}
error函数是内置可用的函数, 用于向gcc的编译过程添加一条错误信息.
下面是这个分析产生的输出:
test.cpp: In function ‘void g()’:
test.cpp:24:1: error: stmt at test.cpp:6:6 :foo() in g()function arguments:
test.cpp: In function ‘int main()’:
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at undefined :undefined in main()
test.cpp:24:1: error: stmt at test.cpp:17:8 :p in main()
test.cpp:24:1: error: stmt at test.h:2:9 :T::i in main()
test.cpp:24:1: error: stmt at test.cpp:19:10 :a in main()
test.cpp:24:1: error: stmt at /usr/include/string.h:128:14 :strcpy(char*, const char*) in main()function arguments: ({shortName:"a", assemblerName:"_ZZ4mainE1a", name:"a", type:{isArray:true, max:"4u", type:#1={min:{value:"-128", type:#1#}, max:{value:"127", type:#1#}, isSigned:true, precision:8, name:"char"}}, loc:{_source_location:765480, file:"test.cpp", line:19, column:10}}),({value:"\"12\"", type:{variantOf:{isArray:true, max:"2u", type:#1={min:{value:"-128", type:#1#}, max:{value:"127", type:#1#}, isSigned:true, precision:8, name:"char"}}, isArray:true, max:"2u", type:{variantOf:#1#, isConst:true, min:{value:"-128", type:#1#}, max:{value:"127", type:#1#}, isSigned:true, precision:8, name:"char"}}})
test.cpp:24:1: error: stmt at test.cpp:6:6 :foo() in main()function arguments:
test.cpp:24:1: error: stmt at test.cpp:10:6 :g() in main()function arguments:
test.cpp:24:1: error: stmt at undefined :undefined in main(). Return!
test.cpp:24:1: error: stmt at undefined :undefined in main(). Return!
foo()
g()
main()
被分析的源程序是:
01: #include "test.h"
02: #include
03: #include
04: #include
05:
06: void foo()
07: {
08: }
09:
10: void g()
11: {
12: foo();
13: }
14:
15: int main()
16: {
17: T p ;
18: p.i = 5;
19: char a[] = "asdf";
20: strcpy(a, "12");
21: foo();
22: g();
23: return 0;
24: }
头文件 test.h 是
01: struct T {
02: int i;
03: int j;
04: private:
05: char * p;
06: };
07:
08: T * foo(int i);
09:
10: enum Color
11: {
12: red,
13: blue,
14: yellow
15: };
可以看出目前 DeHydra(0.9版)还是有一些局限和不便的:
1. 对于return语句, 不知道为什么对应2个statement
2. 对于函数调用, 其.loc参数返回的不是该函数被调用时的行号, 而是该函数被定义时的行号, 我在参考文档里没有找到函数调用语句在被调用行的行号
3. 若.js有非法语句调用, gcc调用过程就crash了, 不够健壮.
粗浅试用, 眼下的局限也许是了解不深的原因.
对于 TreeHydra还没有试用.