Java Extractor fails to resolve method calls when a package name is shadowed by a class name (Obfuscation scenario)(My Prediction)
- Lingua principale
- CodeQL
- Stelle
- 10.1k
- Fork
- 2.1k
- Merge medio
- 2g 15h
- PR unite (30g)
- 141
Descrizione
Hello CodeQL team,
I encountered an issue where CodeQL fails to resolve a method call correctly when analyzing decompiled/obfuscated Java code. The issue arises when a package name conflicts with a class name in the parent package.
In this scenario, CodeQL seems to prioritize resolving the name as a class, causing the subsequent path to be invalid, resulting in an in the AST and a broken data flow.
Reproduction Code (Synthetic Example):
Since I cannot share the proprietary code, I have constructed a minimal reproduction case that mimics the structure:
Context: A class named `a` exists in package `com.demo.util`.
Context: A package named `a` also exists under `com.demo.util`.
Target: We are trying to call a static method `g()` in class `com.demo.util.a.a`.
```
src/
├── com/demo/util/a.java <-- The class "a"
└── com/demo/util/a/a.java <-- The package "a" containing class "a"
```
JavaCode:
```java
// File: src/com/demo/util/a.java
package com.demo.util;
public class a {
// This class is empty or does NOT have method g()
}
// File: src/com/demo/util/a/a.java
package com.demo.util.a;
public class a {
public static String g(String input) {
return input;
}
}
```
My Vul Code is :
```java
@org.springframework.web.bind.annotation.PostMapping({"/testConnection"})
@org.jeecg.authz.annotation.RequiresPermissions({"drag:datasource:testConnection"})
public org.jeecg.modules.drag.config.common.Result a(@org.springframework.web.bind.annotation.RequestBody org.jeecg.modules.drag.vo.DynamicDataSourceVo dynamicDataSourceVo) throws java.sql.SQLException {
java.sql.Connection connection = null;
java.lang.String string = dynamicDataSourceVo.toString();
java.lang.Object objA = this.localCache.a(string);
if (org.jeecg.modules.drag.util.h.d(objA)) {
int iIntValue = org.jeecg.modules.drag.util.h.e(objA).intValue();
if (iIntValue >= 3) {
return org.jeecg.modules.drag.config.common.Result.error("数据源已连接错误3次以上,请检查配置信息!");
}
if (iIntValue == 0) {
return org.jeecg.modules.drag.config.common.Result.OK("数据库连接成功", true);
}
} else {
this.localCache.a(string, 0, org.jeecg.modules.jmreport.common.util.DateUtils.k);
}
try {
try {
try {
java.lang.Class.forName(dynamicDataSourceVo.getDbDriver());
java.sql.DriverManager.setLoginTimeout(60);
java.sql.Connection connection2 = java.sql.DriverManager.getConnection(org.jeecg.modules.drag.util.a.a.g(dynamicDataSourceVo.getDbUrl()), dynamicDataSourceVo.getDbUsername(), dynamicDataSourceVo.getDbPassword());
if (connection2 != null) {
org.jeecg.modules.drag.config.common.Result resultOK = org.jeecg.modules.drag.config.common.Result.OK("数据库连接成功", true);
if (connection2 != null) {
try {
if (!connection2.isClosed()) {
connection2.close();
}
} catch (java.sql.SQLException e) {
a.error(e.toString(), e);
}
}
return resultOK;
}
this.localCache.a(string, 1);
org.jeecg.modules.drag.config.common.Result resultOK2 = org.jeecg.modules.drag.config.common.Result.OK("failed:unknown", true);
if (connection2 != null) {
try {
if (!connection2.isClosed()) {
connection2.close();
}
} catch (java.sql.SQLException e2) {
a.error(e2.toString(), e2);
}
}
return resultOK2;
} catch (java.lang.Exception e3) {
a.error(e3.toString(), e3);
this.localCache.a(string, 1);
org.jeecg.modules.drag.config.common.Result resultError = org.jeecg.modules.drag.config.common.Result.error("failed:" + e3.getMessage());
if (0 != 0) {
try {
if (!connection.isClosed()) {
connection.close();
}
} catch (java.sql.SQLException e4) {
a.error(e4.toString(), e4);
return resultError;
}
}
return resultError;
}
} catch (java.lang.ClassNotFoundException e5) {
a.error(e5.toString(), e5);
this.localCache.a(string, 1);
org.jeecg.modules.drag.config.common.Result resultError2 = org.jeecg.modules.drag.config.common.Result.error("failed:not exist");
if (0 != 0) {
try {
if (!connection.isClosed()) {
connection.close();
}
} catch (java.sql.SQLException e6) {
a.error(e6.toString(), e6);
return resultError2;
}
}
return resultError2;
}
} catch (java.lang.Throwable th) {
if (0 != 0) {
try {
if (!connection.isClosed()) {
connection.close();
}
} catch (java.sql.SQLException e7) {
a.error(e7.toString(), e7);
throw th;
}
}
throw th;
}
}
```
and my QL code is:
```ql
/**
* @name Find JDBC Connection Calls
* @description Find all usages of DriverManager.getConnection
* @kind problem
* @problem.severity warning
* @id java/find-jdbc-connection
*/
import java
// 1. 定义这个目标方法 (和你写的一样,稍微优化了精准度)
class JDBCConnectionMethod extends Method {
JDBCConnectionMethod() {
// DriverManager 是类名,不需要 getAnAncestor,直接用 hasQualifiedName 更快更准
this.getDeclaringType().hasQualifiedName("java.sql", "DriverManager") and
this.hasName("getConnection")
}
}
// 2. 核心查询逻辑
from MethodCall call
where
// 限制这个调用必须是调用了我们上面定义的那个方法
call.getMethod() instanceof JDBCConnectionMethod
select call, "发现 DriverManager.getConnection 的调用,第0个参数是: " + call.getArgument(0)
// /**
// * @name Check for Ghost Class
// * @kind problem
// * @id java/check-ghost-class
// */
// import java
// from Class c
// where c.hasQualifiedName("org.jeecg.modules.drag.util.a", "a")
// select c, "文件路径: " + c.getFile().getAbsolutePath(), "代码行数: " + c.getNumberOfLinesOfCode()
```
Observed Behavior (AST View):
When using "CodeQL: View AST" in VS Code on the line `com.demo.util.a.a.g(...)`:
The method call is marked as .
The qualifier `com.demo.util.a` resolves to the Class `com.demo.util.a` instead of the Package.
Since Class `com.demo.util.a` does not contain method `a` or `g`, the resolution fails.
Expected Behavior:
CodeQL should correctly identify that com.demo.util.a.a refers to the class a inside package com.demo.util.a, even if a class named com.demo.util.a exists.
Build Environment:
Build Mode: `codeql database create --command="autobuild -none" ...` (Analyzing source code directly)
CodeQL CLI Version: [这里填你的版本,例如 2.23.8]
OS: [/ macOS Sonoma]
Impact: This causes severe data flow breakage in TaintTracking for obfuscated applications, as the source-to-sink path is interrupted at the method call.
thank you sir :) I await your reply
Guida per i contributori
Apri la guida per i contributori
Direzione di ricerca
Reproduce the case using the synthetic src/com/demo/util/a.java and src/com/demo/util/a/a.java files and the shown database create command, then inspect the com.demo.util.a.a.g(...) call with CodeQL: View AST. Compare the resolution with the JDBCConnectionMethod query; done means the call is no longer marked as an unknown method and its method target and data-flow path resolve correctly.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- java
- Ambito
- devtools
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 42/100