CodeQL系列之基础语法
0x01 语法规则
import <language> /* 导入对应的语言包 */
/* 一些谓词、类的设置 即定义一些方法或者类*/
from /* 声明变量等 即定义各种变量*/
where /* 设置逻辑表达式 即代码逻辑*/
select /* 打印结果 即输出*/
0x02 常用的一些方法
0x02-1 Field
类或者实例的字段
import java
from Field f
select f
0x02-2 一些常见后缀的含义
XXXType是类型:IntegralType
XXXStmt是语句:ReturnStmt、SwitchStmt、IfStmt
XXXLiteral是类型:StringLiteral、IntegerLiteral、NullLiteral
TypeString 是字符串类型
0x02-3 predicate
定义没有返回类型的谓词(也就是方法)
0x02-4 result
可以理解为函数的返回值
int test(){
result = 5
}
select test()
0x02-5 bindingset
函数有参数时,需要绑定
bindingset[a, b]
int test(int a, int b){
result = a + b
}
select test(1, 2)
0x02-6 getDeclaringType
获取匹配到的元素是在哪个类里定义的
import java
from Field f, FieldRead read
where
f.hasName("chastityUtil") and
f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") and
f = read.getField()
select f.getDeclaration()
0x02-7 getName
获取匹配到的元素的名字
import java
from Field f, FieldRead read
where
f.hasName("chastityUtil") and
f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") and
f = read.getField()
select f, f.getName()
0x02-8 getLocation
获取匹配到的元素所在文件的路径
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
call.getCallee() = parseExpression and
parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
parseExpression.hasName("parseObject")
select call.getLocation()
0x02-9 getType
获取匹配到的元素的类型
import java
from Field f, FieldRead read
where
f.hasName("chastityUtil") and
f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") and
f = read.getField()
select f.getType()
0x02-10 exists
判断语句里是否为True
下面的例子因为a不等于b,所以exists的结果为False,所以select为空
import java
from int a
where
a = 9 and
exists(int b |
b = 8 and
a = b
)
select a
下面的例子因为a等于b,所以exists的结果为True,所以select打印了a
0x02-11 Expr
表示各种表达式的通用超类。
0x02-12 hasQualifiedName
个人理解:has(是的意思),QualifiedName是全称的意思,即判断某类的全称是什么,那么就要完整的包名和类名了。
import java
from Field f
where
f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController")
select f
打印出了AuditAuthController类的所有Field
0x02-13 BooleanLiteral
布尔类型
BooleanLiteral 的值为true or false
0x02-14 getFile
获取匹配到的元素所在的文件
import java
from Field f
where
f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController")
select f.getFile()
0x02-15 数据类型
StringLiteral、IntegerLiteral、NullLiteral等
StringLiteral –> 字符串类型
IntegerLiteral –> 数字类型
NullLiteral –> 空类型
import java
from MethodAccess ma, Method method
where
ma.getMethod().overrides*(method) and
method.hasName("append") and
method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.lang", "StringBuilder") and
ma.getAnArgument() instanceof StringLiteral
select ma, method, ma.getAnArgument()
匹配到的元素的参数都是字符串类型
0x02-16 concat
字符串的拼接
import semmle.code.java.frameworks.spring.SpringController
from SpringRequestMappingMethod route
where exists(route.getARequestParameter())
select route, route.getValue(), concat(string s | s = route.getARequestParameter().toString() | s, " ")
0x02-17 getAnAnnotation
获取匹配到元素的注解
class SpringControllerAnnotation2 extends AnnotationType {
SpringControllerAnnotation2() {
// `@Controller` used directly as an annotation.
this.hasQualifiedName("org.springframework.stereotype", "Controller")
or
// `@Controller` can be used as a meta-annotation on other annotation types.
this.getAnAnnotation().getType() instanceof SpringControllerAnnotation2
}
}
this.getAnAnnotation().getType() 获取注解,例如@RestController
this.hasQualifiedName(“org.springframework.stereotype”, “Controller”) 注解其实本质是接口,所在的包是在org.springframework.stereotype.Controller
所以就能够找到注解@Controller了
0x03 Call和Callable
0x03-1 Call.getCaller和Call.getCallee
Call表示调用Callable的这个过程(方法调用,构造器调用等等),Callable表示可调用的方法或构造器的集合。
Call类提供两个谓词函数来获取调用方和被调用方。getCaller()与getCallee()。
getCaller 调用方,例如下面例子的getRequestDatas
getCallee 被调用方,例如下面例子的parseObject
Callable 表示可调用的方法或构造器的集合,例如下面例子的parseObject
Call 表示过程,例如下面例子的JSONObject.parseObject(result)
Callable 等价于 call.getCallee()
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
call.getCallee() = parseExpression and
parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
parseExpression.hasName("parseObject")
select call, parseExpression, call.getCallee(), call.getCaller()
0x03-2 Call.getAnArgument
获取所有的参数
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
call.getCallee() = parseExpression and
parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
parseExpression.hasName("parseObject")
select call.getAnArgument()
0x03-3 Call.getArgument
getArgument(0) 获取第一个参数
getArgument(1) 获取第二个参数
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
call.getCallee() = parseExpression and
parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
parseExpression.hasName("parseObject")
select call.getArgument(0)
0x03-4 Call.getNumArgument
参数个数
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
call.getCallee() = parseExpression and
parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
parseExpression.hasName("parseObject")
select call, call.getNumArgument()
0x03-5 callable.getSignature()
获取参数类型
from Call call, Callable callable
where
call.getCallee() = callable and
callable = getRelatedMethods("javax.imageio", "ImageIO", "read")
select call, callable.getSignature()
0x04 数组
0x04-1 ArrayCreationExpr
匹配数组
import java
from ArrayCreationExpr ace
select ace, ace.getFile(), ace.getLocation()
0x04-2 getAChildExpr
返回数组类型
import java
from ArrayCreationExpr ace
select ace, ace.getAChildExpr(), ace.getFile(), ace.getLocation()
0x04-3 getParent
返回赋值的变量
import java
from ArrayCreationExpr ace
where ace.getAChildExpr().getType().hasName("String")
select ace, ace.getParent()
0x04-4 限定字符数组
import java
from ArrayCreationExpr ace
where ace.getAChildExpr().getType().hasName("String")
select ace, ace.getAChildExpr(), ace.getFile(), ace.getLocation()
0x04-5 ArrayInit
获取数组的一些值
由ArrayCreationExpr.getInit()返回
0x04-6 getInit(index)
获取数组里下标为x的值
import java
from ArrayCreationExpr ace
where ace.getAChildExpr().getType().hasName("String")
select ace, ace.getInit().getInit(2)
0x04-7 getSize()
获取数组的长度
0x04-8 ArrayAccess
ArrayAccess 等价于 a[i++]
个人理解:Array是数组的意思,Access是访问的意思,合起来就是访问数组。那么就是得到数组里的值,所以通过下标来获取数组里的值。因此等价于a[i++]
ArrayAccess.getIndexExpr() 等价于 i++
UnaryAssignExpr 等价于 ++或者–
import java
from ArrayAccess a
where a.getIndexExpr() instanceof UnaryAssignExpr
select a, a.getIndexExpr()
ArrayAccess就是ids[i++]
0x05 强制类型转换
0x05-1 castexpr
一个强制类型转换的表达式
查找从浮点类型到整数类型的强制转换
import java
from CastExpr c
where
c.getExpr().getType() instanceof FloatingPointType and
c.getType() instanceof IntegralType
select c, c.getExpr(), c.getType(), c.getExpr().getType()
0x06 实例化
0x06-1 ClassInstanceExpr
类实例化的表达式
0x06-2 getConstructedType
构造函数的类型
import java
from ClassInstanceExpr new
where new.getConstructedType().hasQualifiedName("com.jeecms.auth.domain.vo", "CoreUserAgent")
select new, new.getConstructedType()
0x07 if语句
0x07-1 IfStmt
IfStmt –> if语句
getCondition –> if的判断语句
getThen –> 判断条件为true时执行的代码块
getElse –> 判断条件为false时执行的代码块
import java
from IfStmt i
select i, i.getThen(), i.getCondition(), i.getTrueSuccessor(), i.getElse()
predicate isMybatisAnnotationSqlInjection(DataFlow::Node sink){
exists(MyBaTisSQLInjectAnnotationMethod m, MethodAccess ma, MyBatisSQLInjectAnnotation msa, string sqlStmt |
ma.getMethod() = m and
if msa.getAValue().getType() instanceof Array
then
sqlStmt = msa.getAValue().getAChildExpr().toString()
else
sqlStmt = msa.getAValue().toString()
and
myBatisSQLInjectValue(sqlStmt).matches("${%}") and
m.getAnAnnotation() = msa and
ma.getAnArgument() = sink.asExpr()
)
}
0x08 等于或不等于表达式
0x08-1 EqualityTest
EqualityTest –> xxx == yyy 或者 xxx != yyy
getAnOperand –> ==或者!=两边的表达式
getLeftOperand –> 左边的表达式
getRightOperand –> 右边的表达式
import java
from EqualityTest eq
// where eq.getAnOperand() instanceof BooleanLiteral
select eq, eq.getAnOperand(), eq.getLeftOperand(), eq.getRightOperand()
0x09 继承类、接口类
0x09-1 RefType
适用于各种参考类型的通用父类,包括类、接口、类型参数和数组。
getASupertype+ 获取到父类,并不断往上找父类
import java
from RefType type
where type.getASupertype+().hasQualifiedName("org.springframework.web.method.support", "HandlerMethodArgumentResolver")
select type, type.getASupertype()
0x09-2 匹配出指定类的所有子类
指定类为LoginSubmitController,是在包com.jeecms.auth.base里定义的
import java
bindingset[packageName, className]
RefType getRelatedClasses(string packageName, string className) {
result.hasQualifiedName(packageName, className)
or
// 父类也符合指定的类
result.getASupertype*().hasQualifiedName(packageName, className)
or
result.getASupertype*().hasQualifiedName(packageName, className + "<>")
}
from RefType r
where r = getRelatedClasses("com.jeecms.auth.base", "LoginSubmitController")
select r
结果里把所有继承于LoginSubmitController的类都匹配出来了
0x09-3 extendsOrImplements
获取接口的实现类
type2类实现了type类
from RefType type, RefType type2
where
type.hasName("BrandTuanActivityQueryService") and
type2.extendsOrImplements(type)
select type2, type2.getASupertype()
0x10 类变量
0x10-1 FieldRead
类变量被调用
import java
from Field f, FieldRead read
where
f.hasName("cmsOrgService") and
f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.auth", "CmsOrgController") and
f = read.getField()
select f, read, f.getName(), f.getDeclaringType(), f.getType()
0x11 方法
0x11-1 MethodAccess 和 Method
MethodAccess 方法调用
Method 方法的定义
import java
from MethodAccess ma, Method method
where
ma.getMethod() = method and
method.hasName("findListByParentId") and
method.getDeclaringType().hasQualifiedName("com.jeecms.system.service", "CmsOrgService")
select ma, method
0x11-2 Method.isPublic() Method.isPrivate() Method.isProtected()等
限定匹配到的方法必须是公共函数、私有函数等
import java
from Method m
where m.isProtected()
select m, m.getFile(), m.getLocation()
0x11-3 getNumberOfParameters
方法的参数个数
Method.getNumberOfParameters 返回参数的个数
import java
from Method m
where
m.getNumberOfParameters() = 3
select m, m.getFile(), m.getLocation()
0x11-4 getAParameter
方法的参数
Method.getAParameter 返回参数
import java
from Method m
where
m.getNumberOfParameters() = 3 and
m.hasName("updateName")
select m.getAParameter()
0x11-5 overrides
覆盖
import java
from Method override, Method base
where
base.hasName("save") and
base.getDeclaringType().hasQualifiedName("com.jeecms.audit.service", "AuditStrategyService") and
override.overrides+(base)
select override, base, override.getFile(), base.getFile()
AuditStrategyService接口定义了一个方法save
AuditStrategyServiceImpl实现类实现了save方法的具体内容
所以使用了Override注释符
所以codeql的代码里,override指的是AuditStrategyServiceImpl#save,base是AuditStrategyService#save
那么override.overrides+(base)就是说AuditStrategyServiceImpl#save覆盖了AuditStrategyService#save
0x11-6 getAReference
方法被调用
Method.getAReference 匹配的方法被调用过
import java
from Method m
where
m.isPublic() and
exists(m.getAReference())
select m, m.getAReference()
例如existItem这个方法就被调用了
0x11-7 getReturnType
方法的返回类型
getReturnType
import java
from Method m
where m.getName() = "existItem"
select m, m.getReturnType()
Method.getReturnType 返回方法的返回值类型
0x11-8 MethodAccess.getQualifier()
返回方法调用的类名
import java
from MethodAccess ma, Method method
where
ma.getMethod() = method and
method.hasName("save") and
method.getDeclaringType().hasQualifiedName("com.jeecms.audit.service", "AuditStrategyService")
select ma, method, ma.getQualifier()
0x11-9 匹配add方法
import java
from MethodAccess ma, Method method
where
ma.getMethod().overrides*(method) and
method.hasName("add") and
method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Collection")
select ma, ma.getMethod(), method, method.getDeclaringType(), method.getDeclaringType().getSourceDeclaration(), method.getLocation()
匹配到的add方法是HashSet的内置方法。
这里要分析下为什么要这么写method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Collection")
其实看method.getDeclaringType()和method.getDeclaringType().getSourceDeclaration()
的结果也能看出来
method.getDeclaringType()
返回了很多Collection的类型,有set,有int,有map等等
这是因为这些都是接口,继承的是Collection类,所以method.getDeclaringType().getSourceDeclaration()
的结果是Collection
看jdk里面关于set、list等定义
set
List
0x11-10 匹配字符串的append方法
import java
from MethodAccess ma, Method method
where
ma.getMethod().overrides*(method) and
method.hasName("append") and
method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.lang", "StringBuilder") and
ma.getAnArgument() instanceof StringLiteral
select ma, ma.getMethod(), method, method.getDeclaringType(), method.getDeclaringType().getSourceDeclaration(), method.getLocation()
0x12 ReturnStmt
0x12-1 返回语句
import java
from ReturnStmt r
where r.getResult() instanceof StringLiteral
select r, r.getResult()
r 的值 return “bean.hasDeleted=true”;
r.getResult() 的值是返回值 “bean.hasDeleted=true”
0x13 Switch语句
import java
from SwitchStmt switch, EnumType enum, EnumConstant missing
where
switch.getExpr().getType() = enum and
missing.getDeclaringType() = enum and
not switch.getAConstCase().getValue() = missing.getAnAccess() and
exists(switch.getDefaultCase())
select switch, enum, missing, switch.getExpr(), switch.getExpr().getType(), switch.getAConstCase(), missing.getAnAccess(), switch.getDefaultCase()
0x13-1 SwitchStmt
switch语句
0x13-2 EnumType
enum类型
0x13-3 EnumConstant
enum的常量
0x13-4 SwitchStmt.getExpr()
switch的判断语句
0x13-5 switch.getAConstCase()
每个case
0x13-6 switch.getDefaultCase()
默认defalut
0x14 三元运算符
0x14-1 ConditionalExpr
import java
from ConditionalExpr e
select e, e.getTrueExpr(), e.getFalseExpr(), e.getCondition()
ConditionalExpr 是整个三元运算符语句
ConditionalExpr.getCondition 是判断语句
ConditionalExpr.getTrueExpr() 是为True时的语句
ConditionalExpr.getFalseExpr() 是为False时的语句
0x15 抛出异常
import java
from ThrowStmt throw
select throw, throw.getThrownExceptionType()
ThrowStmt 抛出异常的语句 throw new GlobalException(RPCErrorCodeEnum.THIRD_PARTY_CALL_ERROR);
ThrowStmt.getThrownExceptionType() 异常的类型 GlobalException
0x16 try语句
0x16-1 TryStmt
import java
from TryStmt t
where
exists(t.getFinally()) and
exists(t.getACatchClause())
select t, t.getBlock(), t.getACatchClause(), t.getFinally()
TryStmt.getBlock 是 try语句的代码块
TryStmt.getACatchClause 是catch (Exception e)
TryStmt.getFinally 是 finally语句的代码块
0x17 Parameter 参数
Parameter 参数
0x17-1 getAnAccess
参数被调用
import java
from Parameter p
where exists(p.getAnAccess())
select p, p.getAnAccess()
0x18 自实现匹配注解
AnnotationType 注解类型类,是继承接口类
所以定义类SpringRequestMappingAnnotationType继承注解类型类AnnotationType
class SpringRequestMappingAnnotationType extends AnnotationType
@RequestMapping注解是在包org.springframework.web.bind.annotation里定义的接口RequestMapping
this.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping")
定义类SpringRequestMappingAnnotation继承注解类Annotation
private class SpringRequestMappingAnnotation extends Annotation
构造方法里,获取自身类型并且是SpringRequestMappingAnnotationType的实例
SpringRequestMappingAnnotation() { this.getType() instanceof SpringRequestMappingAnnotationType }
整体代码如下:
可以匹配到到@RequestMapping,@GetMapping,@PostMapping
import java
// 定义类SpringRequestMappingAnnotationType继承注解类AnnotationType
class SpringRequestMappingAnnotationType extends AnnotationType {
// 构造方法
SpringRequestMappingAnnotationType() {
// `@RequestMapping` used directly as an annotation.
// @RequestMapping注解是在包org.springframework.web.bind.annotation里定义的接口RequestMapping
this.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping")
or
// `@RequestMapping` can be used as a meta-annotation on other annotation types, e.g. GetMapping, PostMapping etc.
this.getAnAnnotation().getType() instanceof SpringRequestMappingAnnotationType
}
}
private class SpringRequestMappingAnnotation extends Annotation {
SpringRequestMappingAnnotation() { this.getType() instanceof SpringRequestMappingAnnotationType }
}
from SpringRequestMappingAnnotation var1
select var1, var1.getFile(), var1.getLocation()
0x19 匹配类
0x19-1 匹配SAXParserFactory类
import java
class SAXParserFactory extends RefType{
SAXParserFactory(){
this.hasQualifiedName("javax.xml.parsers", "SAXParserFactory")
}
}
from SAXParserFactory var1
select var1, var1.getFile(), var1.getLocation()
0x20 File文件
0x20-1 获取所有文件
import java
from File file
select file, file.getBaseName()
0x21 XML文件
0x21-1 XMLFile
获取xml文件
0x22 XML元素
import java
from XMLElement var
select var, var.getFile(), var.getLocation(), var.getAChild(), count(var.getAChild()), var.getFile().getAChild()
0x22-1 XMLElement.getAChild()
获取该元素下的子元素。
0x22-2 XMLElement.getFile().getAChild()
获取该元素所在文件最外层的元素。
0x22-3 allCharactersString
获取元素里的值
import java
/**
* 匹配出xml文件里有mapper元素的文件。
* 例如:<mapper namespace="org.joychou.mapper.UserMapper"> </mapper>
*/
class MyBatisMapperXMLFile extends XMLFile {
MyBatisMapperXMLFile() {
count(XMLElement e | e = this.getAChild()) = 1 and
this.getAChild().getName() = "mapper"
}
}
class MyBatisMapperXMLElement extends XMLElement{
MyBatisMapperXMLElement(){
this.getFile() instanceof MyBatisMapperXMLFile
}
}
0x23 Call和MethodAccess关联起来
0x23-1 通过getQualifier对应
call.getQualifier() = ma.getQualifier()
0x24 Node、PartialPathNode和m、ma关联起来
DataFlow::Node node
node.asParamter() = m.getAParameter()
node.asExpr() = ma.getAnArgument()
DataFlow::PartialPathNode node
node.getNode().asParamter() = m.getAParameter()
node.getNode().asExpr() = ma.getAnArgument()