文章目录
前言
学习利用CodeQL进行代码审计。基础语法包含一些概念和例子。本文是查询和类的部分。
一、查询
查询就是整个QL语言运行后输出的结果。有两种查询类型:
- 第一种是该模块中定义了select 查询子句
- 查询谓词,这意味着我们可以在当前模块中定义或者从其他模块中导入
1.select查询子句
1)select子句格式如下所示(通常在文件末尾),其中from和where语句部分是可选的。我们可以在from中定义变量,在where中给变量赋值和对查询结果的过滤,最后在select中显示结果。
from /* ... variable declarations ... */
where /* ... logical formula ... */
select /* ... expressions ... */
2)在select语句中还可以使用一些关键字:
- as关键字,后面跟随一个名字。作用跟SQL中的as相似给个别名,给结果列提供了一个"标签",并允许在后续的select表达式中使用它们。
- order by关键字,后面跟随一个一个结果列名。作用相当于sql中的order by,用于排序结果,并且在结果列名后可选asc(升序)或desc(降序)关键字。
3)官方例子如下,第一张图结果返回的是select子句 查询的结果,第二张图加了个排序
from int x, int y
where x = 3 and y in [0 .. 2]
select x, y, x * y as product, "product: " + product
2.查询谓词
1)查询谓词是一个非成员谓词,并在最开头使用query作为注解。它返回谓词计算结果的所有元组,下面是一个官方的示例:
query int getProduct(int x, int y) {
x = 3 and
y in [0 .. 2] and
result = x * y
}
2)编写查询谓词相对比编写select子句的好处是你可以在代码的其他部分中调用谓词。例如,我们可以在类中去调用getProduct:
query int getProduct(int x, int y) {
x = 3 and
y in [0 .. 2] and
result = x * y
}
class MultipleOfThree extends int {
MultipleOfThree() {
this = getProduct(_, _) }
}
from MultipleOfThree m
select m
3)这样我们的查询结果就有两个,一个是内置的#select,一个是getProduct
4)二者对比之下,select 子句就像一个匿名谓词,后面你不能调用它。在调试代码时向谓词添加查询注释也很有帮助 。这样,您可以显式地看到谓词求值的元组集。
二、类型
1.概述
QL 是一种静态类型语言,因此每个变量都必须有一个声明的类型。在QL中类型是一组值。例如,类型int是整数集。而值可以归属多个集合,也就代表了一个值可以有多种类型。QL 中的类型有基本类型、类、字符类型、类域类型、代数数据类型、类型联合和数据库类型。
2.原始类型
这些类型内置在QL 中,并且在全局命名空间中始终可用,也与我们正在查询的数据库没有关系。
- boolean:此类型包含值true和false。
- float:此类型包含 64 位浮点数,例如6.28 和 -0.618。
- int:此类型包含 32 位二进制补码整数,例如-1 和 42。
- string:此类型包含 16 位字符的有限字符串。
- date:此类型包含日期(和可选的时间)。
3.类(Classes)
1)我们可以在CodeQL中定义自己的类型,一种方法定义一个类。
2)类提供了一种重用和结构化代码的简单方法,例如,你能将相关值分组在一起、在这些值上定义成员谓词、定义子类以重写成员谓词。
3)QL 中的类不会“创建”新对象,它只是表示一个逻辑属性。如果值满足该逻辑属性,则该值属于特定类。
4)定义一个类,需要以下参数:
- class关键字
- 类的名称。这是一个以大写字母开头的标识符
- 类通过extends和 / 或instanceof派生类的超类型
- 类的主体,用大括号括起来
5)官方案例:
class OneTwoThree extends int {
OneTwoThree() {
// characteristic predicate
this = 1 or this = 2 or this = 3
}
string getAString() {
// member predicate
result = "One, two or three: " + this.toString()
}
predicate isEven() {
// member predicate
this = 2
}
}
6)在CodeQL中,类允许多重继承,但是以下操作是非法的:
- 不能继承本身
- 不能继承final类
- 不能继承不兼容的类型,可以参考:https://codeql.github.com/docs/ql-language-reference/annotations/#annotations-overview
4.类的主体
1)类的主体可以包含以下内容:
- 特征谓词的声明
- 任意数量的成员谓词声明
- 任意数量的字段声明
2)在类中,我们可以使用this来指代类本身。当我们定义类时,该类还会从其父类继承所有非私有成员谓词和字段,我们可以覆盖(override)这些谓词和字段。
5.特征谓词
类似于其他语言中类的构造函数,只能定义一个,我们可以在特征谓词中使用this来限制类中可能的值。在上述例子中,OneTwoThree被限制为1-3中的整数。
6.成员谓词
这些谓词仅适用于特定类的成员。我们可以在值上调用成员谓词。
(OneTwoThree).getAString()
// 结果是 One, two or three: 1
7.字段
字段是在类的主体中声明的变量,一个类的主题中可以有任意数量的字段声明。我们可以在类中的谓词适用这些变量,用法和this类似,字段必须受限于特征谓词。官方案例:
class SmallInt extends int {
SmallInt() {
this = [1 .. 10] }
}
class DivisibleInt extends SmallInt {
SmallInt divisor; // declaration of the field `divisor`
DivisibleInt() {
this % divisor = 0 }
SmallInt getADivisor() {
result = divisor }
}
from DivisibleInt i
select i, i.getADivisor()
在这个案例中,先声明了SmallInt divisor并定义了一个divisor字段,将其约束在特征谓词中,然后在成员谓词getADivisor的声明中使用它 。
8.具体类
上面的例子都是具体类,具体类是通过限制较大类型中的值来定义的。具体类中的值也是超类型交集中的那些值,这些值也满足类的特征谓词。
9.抽象类
抽象类使用关键字abstract放在关键字class前来定义。抽象的概念相信在许多其他语言中我们都有接触到(例如java)。抽象类我们又可以叫做元类,它定义其子类的谓词和字段。格式如下:
abstract class SqlExpr extends Expr {
...
}