经过半天的分析和了解,大致明白了这个工具的使用方法和原理。
这个工具,会将一个源文件(目前我是用单一源文件测试的,没有使用目录测试),
每一个有效符号或者元素都解析出来,之后储存在一个大list里面,供后续模块检测时使用,
但是一些特殊的元素,不会被列入list,如调用约定(__stdcall 此类等等),其他应该还有,但是还没使用到,
目前看到的情况是,整个文件所有内容全部都被放到了一个list 里面,挺痛苦的。
内置模块部分,其实它内置了很多功能
一堆check,很方便我来学习,据此我也写出了一个简单的check模块,代码如下
1 #pragma once
2
3 #include "check.h"
4 #include "ctu.h"
5
6 #include <cstddef>
7 #include <list>
8 #include <map>
9 #include <string>
10 #include <vector>
11
12 class CPPCHECKLIB CheckZooFrame : public Check {
13 public:
14
15 /** This constructor is used when registering the CheckClass */
16 CheckZooFrame() : Check(myName()) {
17 }
18
19 /** This constructor is used when running checks. */
20 CheckZooFrame(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger)
21 : Check(myName(), tokenizer, settings, errorLogger) {
22 }
23
24 void runChecks(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) OVERRIDE;
25
26 void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE;
27
28 /** @brief Parse current TU and extract file info */
29 Check::FileInfo* getFileInfo(const Tokenizer* tokenizer, const Settings* settings) const OVERRIDE;
30
31 /** @brief Analyse all file infos for all TU */
32 bool analyseWholeProgram(const CTU::FileInfo* ctu, const std::list<Check::FileInfo*>& fileInfo, const Settings& settings, ErrorLogger& errorLogger) OVERRIDE;
33
34 private:
35
36 /** data for multifile checking */
37 class MyFileInfo : public Check::FileInfo {
38 public:
39 /** unsafe array index usage */
40 std::list<CTU::FileInfo::UnsafeUsage> unsafeArrayIndex;
41
42 /** unsafe pointer arithmetics */
43 std::list<CTU::FileInfo::UnsafeUsage> unsafePointerArith;
44
45 /** Convert MyFileInfo data into xml string */
46 std::string toString() const OVERRIDE {
47 return "Zoos";
48 }
49 };
50
51 Check::FileInfo* loadFileInfoFromXml(const tinyxml2::XMLElement* xmlElement) const OVERRIDE;
52
53
54 static std::string myName() {
55 return "Zoo checking";
56 }
57
58 std::string classInfo() const OVERRIDE {
59 return "Zoo Check Frame\n";
60 }
61 };
1 #include "check_zoo.h"
2 #include "check.h"
3
4 namespace {
5 CheckZooFrame instance;
6 }
7
8
9
10 static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U);
11
12
13 void CheckZooFrame::runChecks(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) {
14 printf("FILE [%s] LINE [%d] Function [%s] \n", __FILE__, __LINE__, tokenizer->tokens()->next()->str().c_str());
15 CheckZooFrame c(tokenizer, settings, errorLogger);
16 c.reportError(tokenizer->tokens(), Severity::portability, "Zoo检测点", "Zoo框架", CWE_POINTER_ARITHMETIC_OVERFLOW, false);
17
18 for (const Token* tok = tokenizer->tokens(); tok; tok = tok->next())
19 {
20 printf("%s ", tok->str().c_str());
21 if (tok->str() == "{" || tok->str() == "}" || tok->str() == ";")
22 {
23 printf("\n");
24 }
25 }
26 }
27
28 void CheckZooFrame::getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const {
29 printf("FILE [%s] LINE [%d] \n", __FILE__, __LINE__);
30 }
31
32 /** @brief Parse current TU and extract file info */
33 Check::FileInfo* CheckZooFrame::getFileInfo(const Tokenizer* tokenizer, const Settings* settings) const {
34 printf("FILE [%s] LINE [%d] \n", __FILE__, __LINE__);
35 return NULL;
36 }
37
38 /** @brief Analyse all file infos for all TU */
39 bool CheckZooFrame::analyseWholeProgram(const CTU::FileInfo* ctu, const std::list<Check::FileInfo*>& fileInfo, const Settings& settings, ErrorLogger& errorLogger) {
40 printf("FILE [%s] LINE [%d] \n", __FILE__, __LINE__);
41 return true;
42 }
43
44
45 Check::FileInfo* CheckZooFrame::loadFileInfoFromXml(const tinyxml2::XMLElement* xmlElement) const {
46 printf("FILE [%s] LINE [%d] \n", __FILE__, __LINE__);
47 return NULL;
48 }
扩展性还是非常强的,我的代码,几乎全部都是对照其他模块来写的,
而且接口也非常简单,很友好。
这里简单地解释一下吧。
cppcheck的模块系统属于构造时直接靠全局变量串起来的这种模式(相似的可以看llvm,clang的命令行系统,也是这么串起来的),
优点是简单方便,只要继承了父类,然后实例化一个自己,就解决了问题,
缺点是,很多关键信息无法在初次构造时就获取到,所以使用时有点麻烦。
本人在编写这个简单模块的时候,就遇到了这个小麻烦,不过还是解决了。
主要体现在,错误上报的部分。
整个cppcheck框架,其实比较强大,它实际上真的仅仅是作为一个普通框架来执行的,
内部功能通过一个个模块,一次次传递参数,一个个reportError来列出。
以 CheckZooFrame 为例,
首先需要实现若干虚函数,继承于 check 父类,
然后定义一个全局变量,供初始化的时候,挂接自己,
但是这时候,由于自己无法获取到任何关键信息,所以内部变量初始化全部都是空,
即CPP代码中第五行。
挂接了自己之后,最重要的函数,其实只有一个,是runcheck,
这个函数会被外面check管理器直接调用,会传入一系列的参数,
让当前模块来检测是否有需要检测的异常,
如果有的话,则通过reportError上报。
这时候就有问题了,reportError上报需要知道当前的代码list,但是初始化的时候已经全都是空了,没有list,
怎么办,那就只能在自己内部重新创建个局部变量,构造时让它有list,
即CPP代码中15行的作用。
然后就可以尽情地玩耍了。
还有个问题,就是CWE是什么,其实CWE是一个索引ID,这个ID遵守一套XXXX的规则,
这个规则里面定义了若干种代码可能出现的问题,通过这个ID,大家就可以统一使用并查询出代码出现了什么问题。
具体这个ID去哪查,就去下面的网站。
https://cwe.mitre.org/data/downloads.html
例子中的CWE是我随便写的,并不影响。
其实挺容易的,没有那么难,只不过,任重而道远啊。比如
下图,指定位置有一个非常明显的内存越界写操作,可能会导致出现 0xC0000005,但是呢,cppcheck内置的模块没有发现这个问题,
这说明了一个事情,就是它内置模块的检测范围可能还是以函数为单位的,只能检测到当前函数内的事情,无法朔源检测前面或者后面函数内的事情。
这只是一个点吧,我希望基于对它的学习,能多少再了解一些代码审计相关的知识。
就到这吧
其实,由于它的模块化是如此地出色,甚至可以插入一个python或者lua脚本解释器之类的,让它支持通过脚本来加模块,那么应该会更完美。