
JSON 규칙으로 사용자 정의할 수 있는 Java 바이트코드 분석기
JSON 규칙을 통해 사용자 지정이 가능한 Java 바이트코드 분석기입니다. 하나 이상의 Jar 또는 War 파일이 포함된 경로를 입력받아, 제공된 규칙을 사용하여 분석하고 결과를 HTML 보고서로 생성하는 명령줄 도구입니다.
usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE
-a,--analyze <pathToAnalyze> 분석을 실행할 디렉터리의 경로입니다.
-c,--checks <checks...> 분석에서 실행할 사용자 지정 검사 목록입니다.
(공백으로 구분)
-f,--custom-file <customFile> 사용자 지정 규칙을 실행하기 위한 JSON 형식 파일을
지정합니다. 자세한 내용은
https://github.com/fergarrui/custom-bytecode-analyzer
에서 확인하세요.
-h,--help 이 메시지를 출력합니다.
-i,--items-report <maxItems> 보고서당 최대 항목 수입니다. 발견된 문제 수가 이 값을
초과하면 보고서가 여러 파일로 분할됩니다. 보고서에
문제가 너무 많을 것으로 예상될 때 유용합니다.
기본값: 50.
-o,--output <outputDir> 보고서를 저장할 디렉터리입니다. 경고 – 이 디렉터리에
이미 저장된 보고서가 있으면 덮어씁니다.
기본값은 "report"입니다.
-v,--verbose-debug 상세 정보를 디버그 모드로 증가시킵니다.
-vv,--verbose-trace 상세 정보를 추적 모드로 증가시킵니다. 속도가 느려지므로
필요할 때만 사용하세요.
규칙 파일은 -f,--custom-file 인수를 사용하여 지정할 수 있습니다. 파일은 JSON 형식이며 다음과 같은 구조를 가집니다:
final인 경우에만 지원됨net.nandgr.cba.custom.model.Rules.java 파일을 확인하여 Java 코드에서 구조를 볼 수도 있습니다.
examples 디렉터리 아래에 이미 여러 규칙이 있습니다. 아래에는 각 규칙에 대한 예제가 나열되어 있습니다.
사용자 지정 역직렬화를 수행하는 클래스를 찾아야 한다면, 아주 쉽게 할 수 있습니다. 클래스는 private void readObject(ObjectInputStream in)을 구현하여 사용자 지정 역직렬화를 정의합니다. 따라서 해당 메서드가 정의된 모든 클래스만 찾으면 됩니다. 다음과 같이 규칙을 정의하는 것만으로 충분합니다:
{
"rules": [{
"name": "Custom deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
private 가시성, readObject 이름, java.io.ObjectInputStream 타입의 매개변수를 가진 메서드를 보고합니다. 매개변수는 배열이며, 둘 이상이 지정된 경우 모두 일치해야 보고됩니다. 규칙이 하나뿐이므로 custom-deserialization-0.html이라는 보고서가 생성됩니다.
이 경우 두 개의 메서드를 가진 하나의 규칙을 정의해야 합니다. 역직렬화를 위한 첫 번째 예제와 동일한 규칙, 그리고 private void writeObject(ObjectOutputStream out)에 일치하는 새로운 규칙입니다. 위의 JSON 구조에서 보듯이 rules.rule.methods 속성은 메서드의 배열이므로 다음과 같은 규칙을 작성할 수 있습니다:
{
"rules": [{
"name": "Custom serialization and deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
},{
"name": "writeObject",
"report": "false",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectOutputStream"
}]
}]
}]
}
report 속성을 false로 설정하여 동일한 규칙에 대해 두 번 보고되지 않도록 했습니다. 두 번째 메서드는 조건으로만 사용하고, readObject 메서드만 보고하는 것이 이 규칙의 목적에 충분합니다.
속성이 정의되지 않으면 항상 true로 일치합니다. 예를 들어, 이 규칙은 모든 메서드 정의를 반환합니다:
{
"rules": [{
"name": "Method definitions",
"methods": [{
}]
}]
}
메서드 호출도 찾을 수 있습니다. 이 경우 JSON은 다음과 같습니다:
{
"rules": [{
"name": "String equals",
"invocations": [{
"owner": "java.lang.String",
"method": {
"name": "equals"
}
}]
}]
}
owner 속성은 메서드를 포함하는 클래스를 지정합니다.
이전 예제보다 조금 더 유용한 또 다른 메서드 호출 예제입니다:
{
"rules": [{
"name": "Method invocation by reflection",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
메서드 호출과 동일하지만, 이 경우 메서드 이름은 <init>이어야 합니다.
{
"rules": [{
"name" : "String instantiation",
"invocations" : [{
"owner" : "java.lang.String",
"method" : {
"name" : "<init>"
}
}]
}]
}
이 규칙은 다음 코드에서 발생하는 경우를 찾습니다:
[...]
String s = new String("foo");
[...]
이 예제에서는 역직렬화 사용(이전 예제처럼 직렬화 동작을 정의하는 클래스가 아닌)을 찾고자 합니다. 역직렬화는 ObjectInputStream.readObject()가 호출될 때 발생합니다. 예를 들어 다음 코드 조각에서:
ObjectInputStream in = new ObjectInputStream(fileInputStream);
Object o = in.readObject();
따라서 ObjectInputStream에서 readObject라는 메서드 호출을 찾아야 합니다. 하지만 연구 맥락에서 많은 가양성(false positive)이 발생할 수 있습니다. 클래스가 사용자 지정 역직렬화를 정의할 때 private void readObject(ObjectInputStream in) 메서드 내부에서 이 메서드를 호출하기 때문입니다. 이로 인해 보고서가 너무 오염될 수 있습니다. 이러한 경우를 제외하고 진정한 역직렬화만 유지하려면 notFrom 속성을 사용할 수 있습니다:
{
"rules": [{
"name": "Deserialization usage",
"invocations": [{
"owner": "java.io.ObjectInputStream",
"method": {
"name": "readObject"
},
"notFrom": {
"name": "readObject",
"visibility": "private"
}
}]
}]
}
이 파일은 호출이 private void readObject(ObjectInputStream in) 메서드 내에서 이루어지지 않은 경우 java.io.ObjectInputStream.readObject() 호출을 찾습니다.
다음 코드로 컴파일된 클래스는 보고되지 않습니다:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
Object o = in.readObject();
}
하지만 다음 코드는 보고됩니다:
public Object deserializeObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
Object o = in.readObject();
return o;
}
from 속성도 notFrom과 정확히 같은 방식으로 호출에 설정할 수 있지만, 결과는 반대입니다: 정의된 메서드에서 호출이 이루어진 경우에만 일치합니다.
이 경우 superClass 속성을 사용할 수 있습니다. javax.servlet.http.HttpServlet을 확장하는 모든 클래스를 찾으려면 다음과 같은 규칙을 사용할 수 있습니다:
{
"rules": [{
"name": "Java servlets",
"superClass" : "javax.servlet.http.HttpServlet"
}]
}
인터페이스 배열을 구현하는 클래스를 찾기 위해 규칙을 작성할 수 있습니다. 규칙에 둘 이상의 인터페이스가 정의된 경우 클래스는 이들 모두를 구현해야 보고됩니다. javax.net.ssl.X509TrustManager를 구현하는 클래스를 찾으려면 규칙은 다음과 같습니다:
{
"rules": [{
"name": "X509TrustManager implementations",
"interfaces" : ["javax.net.ssl.X509TrustManager"]
}]
}
interfaces는 배열이므로 문자열을 대괄호 안에 추가해야 합니다. 예: ["interface1", "interface2", ...]