代码分析平台CodeQL学习手记(五) - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

代码分析平台CodeQL学习手记(五)

fanyeee 技术 2020-01-03 11:25:00
980334
收藏

导语:在本文中,我们通过一个抓小偷的示例学习了逻辑连接词和聚合操作的应用。

代码分析平台CodeQL入门(一)

代码分析平台CodeQL学习手记(二)

代码分析平台CodeQL学习手记(三)

代码分析平台CodeQL学习手记(四)

在前面的文章中,我们通过一个有趣的例子来介绍了逻辑谓词的用法。在本文中,随着破案故事的推进,我们将进一步学习逻辑连接词和聚合操作的运用。

逻辑连接词

在之前搜集的破案线索中,有些问题的答案是肯定的,例如“小偷身高超过150厘米吗?”的答案为“是”。为了利用这种类型的信息,我们可以编写如下所示的查询,来找出所有身高超过150厘米的村民。这些人都是我们的怀疑对象。

from Person t
where t.getHeight() > 150
select t

在利用其他线索编写相应的查询之前,我们需要先来熟悉另一个有用的工具:逻辑连接词。

借助于逻辑连接词,我们可以组合不同的信息或判断,从而编写出更加复杂的查询。例如,如果知道小偷的年龄超过30岁并且头发是棕色的,就可以使用下面的 where 子句来连接两个谓词:

where t.getAge() > 30 and t.getHairColor() = "brown"

需要注意的是,由于谓词getHairColor()返回的是一个字符串,因此,我们需要用引号将单词brown括起来。

如果小偷不住在城堡的北边,可以用下面的语句进行描述:

where not t.getLocation() = "north"

如果小偷头发颜色为棕色或黑色,可以用下面的语句进行描述:

where t.getHairColor() = "brown" or t.getHairColor() = "black"

下面,我们通过连接词来将上面的语句连成一句:

where t.getAge() > 30
  and (t.getHairColor() = "brown" or t.getHairColor() = "black")
  and not t.getLocation() = "north"

需要注意的是,我们在 or 子句的前后使用了一对圆括号,以使查询符合预期。如果没有括号的话,由于连接词and的优先级别高于连接词or,所以,实际上就会变成:

where (t.getAge() > 30 and t.getHairColor() = "brown")
  or (t.getHairColor() = "black" and not t.getLocation() = "north")

此外,如果一个人 p 的头发正在由黑变灰,那么可以用p.getHairColor() = "black" andp.getHairColor() = "gray"来表示。如果小偷是秃头怎么办?在这种情况下,小偷没有头发,因此,谓词getHairColor()就不应返回任何结果!

如果我们知道小偷肯定不是秃头,那么这时有一点可以肯定:必然有一种颜色与小偷的头发颜色相匹配。这时,可以引入一个字符串类型的新变量 c,并找出头发颜色,即t.getaircolor()返回结果与变量c 的值相匹配的那些人。

from Person t, string c
where t.getHairColor() = c
select t

注意,这里的变量c只是临时使用一下,在select子句中根本就没有用到它。在这种情况下,最好使用exists,具体如下所示:

from Person t
where exists(string c | t.getHairColor() = c)
select t

在这里,exists引入了一个字符串类型的临时变量 c,并且仅在至少有一个字符串c满足条件 t.getHairColor() = c时,这个where子句才成立。

注意:QL语言中exists对应于逻辑学中的存在量词,表示“存在某个”或“至少有一个”。 此外,QL语言中还提供了一个通用量词,即forall,表示“所有的”,或“每一个”。

从线索到查询

现在,我们已经为追查小偷做好了充分的准备了。接下来,我们将编写一个查询,找出符合前8个线索的村民,这些线索包括:

image.png

首先,一定不要忘了导入tutorial,具体如下所示:

import tutoria

然后,声明一个表示村民的变量:

from Person t

接下来,我们分别将上面的每条线索翻译成相应的QL语句即可。其中,对于问题1,可以翻译为如下所示的语句:

t.getHeight() > 150

对于问题2,可以翻译为如下所示的语句:

not t.getHairColor() = "blond"

对于问题3,我们知道秃子是没有头发的,那么也就无所谓发色了。换句话说,如果查询某人,返回了头发的颜色,那么,说明这个人肯定不是秃子。所以,这个线索等价于下面的语句:

exists (string c | t.getHairColor() = c)

对于问题4,可以翻译为如下所示的语句:

not t.getAge() < 30

对于问题5,可以翻译为如下所示的语句:

t.getLocation() = "east"

对于问题6,可以翻译为如下所示的语句:

(t.getHairColor() = "black" or t.getHairColor() = "brown")

注意,考虑到逻辑连接词的优先级问题,为了符合预期,这里需要使用圆括号。对于问题7,可以翻译为如下所示的语句:

not (t.getHeight() > 180 and t.getHeight() < 190)

同样,为了符合预期,这里也用到了圆括号。对于问题8,如果一个人不是最老的,那么至少有一个人比他/她年龄大,因此,可以将其变成判断存在性的问题:

exists(Person p | p.getAge() > t.getAge())

最后,我们使用逻辑连接词and将这些条件组合起来,也就是这些条件必须全部满足:

3.png

这样,我们就能得到一个嫌疑人列表,小偷就在这个范围内:

4.png

注意,上面代码使用了注释符/* 和*/,其中,位于/* 和*/之间的文本都不会作为 QL 代码进行处理,而是作为提示内容供我们人类“看的”。

现在,虽然我们已经排除了一部分人,但是,我们的嫌疑人名单还是很长。为了找出小偷,我们还必须收集更多的信息,以进一步改善我们的查询。

聚合操作

当我们需要找出村子里最老、最年轻、最高、最矮的人的时候,除了使用 exists间接完成之外,还有一种更有效的方法,那就是使用聚合函数,例如max和min函数等。

通常来说,聚合函数就是对多个数据块进程处理,并返回单个值的函数。常见的聚合函数为count、max、 min、avg和sum。聚合函数使用方法为:

例如,可以使用聚合函数max来查找村中最年长的人的年龄:

max(int i | exists(Person p | p.getAge() = i) | i)

该聚合函数考察的是所有整数 i,然后进一步将 i 限制为与村民年龄相匹配的值,最后返回与村民年龄相匹配的整数中最大的那一个。

但是,在实际的查询中该如何利用聚合函数呢?

如果小偷是村里最老的那个人,那么就意味着小偷的年龄等于村民年龄中的最大值:

from Person t
where t.getAge() = max(int i | exists(Person p | p.getAge() = i) | i)
select t

如您所见,这种通用的聚合语法非常冗长;不过,在大多数情况下,其中的某些部分是可以省略的。实际上,QL语言提供了一种非常有用的特性:有序聚合。利用这种特性,我们可以通过order by对表达式进行排序。

例如,如果使用有序聚合,那么选择最年长的村民的过程就会简单得多。

select max(Person p | | p order by p.getAge())

有序聚合会考察每个人 p ,并选择年龄最大的人。就本例来说,对于要考察的人没有其他方面的限制,因此,逻辑公式子句为空。请注意,如果有多个人具有相同的最大年龄,那么,该查询将列出所有这些人。

下面是一些聚合的示例代码:

min(Person p | p.getLocation() = "east" | p order by p.getHeight())

上面的代码的作用是找出村东最矮的人。

count(Person p | p.getLocation() = "south" | p)

上面的代码的作用是统计住在村南的村民人数。

avg(Person p | | p.getHeight())

上面的代码的作用是计算村民的平均身高。

sum(Person p | p.getHairColor() = "brown" | p.getAge())

上面的代码的作用是计算头发为棕色的村民的年龄总和。

揪出小偷

现在,我们可以将剩下的线索也放到查询中去:

image.png

最后,完整的代码如下图所示:

1.png

运行结果如下所示:

2.png

好了,终于把小偷揪出来了!

小结

在本文中,我们通过一个抓小偷的示例学习了逻辑连接词和聚合操作。在下一篇文章中,我们将通过一个新的故事来学习更多的知识点。

备注:本系列文章乃本人在学习CodeQL平台过程中所做的笔记,希望能够对大家有点滴帮助——若果真如此的话,本人将备感荣幸。

参考资料:

https://help.semmle.com/

如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务