diff --git a/schema/SchemaGrammar.g4 b/schema/SchemaGrammar.g4 index e91ee25..748d4e5 100644 --- a/schema/SchemaGrammar.g4 +++ b/schema/SchemaGrammar.g4 @@ -69,6 +69,8 @@ BIND_EVENT_MODIFIER: 'previous' | 'next' ; BIND_EVENT_SOURCE: 'reference' ; +AUTO_FIELDS: '.fields' ; + BYTECODE_STRING: '.string' ; BYTECODE_STRING_REGEX: 'regex' ; BYTECODE_STRING_CONTAINS: 'contains' ; @@ -127,6 +129,11 @@ definitionPrefix: schemaAttributes? annotation* ACCESS_FLAG* ; name: DOLLAR? IDENTIFIER ; +autoField: definitionPrefix type? name ; +autoFieldList: autoField + | autoField (COMMA autoField)* ; + +autoFieldsDefinition: definitionPrefix type AUTO_FIELDS OPEN_BRACE autoFieldList CLOSE_BRACE ; fieldDefinition: definitionPrefix type name SEMICOLON ; type: OPEN_TRI EXTENDS type CLOSE_TRI #extendsType @@ -178,7 +185,7 @@ expectsStatement: EXPECTS javaTypeName ; extendsStatement: EXTENDS type ; implementsStatement: IMPLEMENTS type (COMMA type)* ; -classDefinition: definitionPrefix CLASS_TYPE name expectsStatement? extendsStatement? implementsStatement? OPEN_BRACE fieldDefinition* methodDefinition* CLOSE_BRACE ; +classDefinition: definitionPrefix CLASS_TYPE name expectsStatement? extendsStatement? implementsStatement? OPEN_BRACE autoFieldsDefinition? fieldDefinition* methodDefinition* CLOSE_BRACE ; importStatement: IMPORT JAVA_TYPE_IDENTIFIER SEMICOLON ; diff --git a/src/main/java/xyz/rodit/dexsearch/parser/SchemaVisitor.java b/src/main/java/xyz/rodit/dexsearch/parser/SchemaVisitor.java index 53a31a1..7af29b5 100644 --- a/src/main/java/xyz/rodit/dexsearch/parser/SchemaVisitor.java +++ b/src/main/java/xyz/rodit/dexsearch/parser/SchemaVisitor.java @@ -89,7 +89,7 @@ public EnumSet visitSchemaAttributes(SchemaGrammarParser.SchemaAttrib public Annotation visitAnnotation(SchemaGrammarParser.AnnotationContext ctx) { Object visitedType = super.visit(ctx.type()); if (visitedType instanceof Type annotationType) { - return new Annotation(annotationType, ctx.annotationArguments().constant().stream().map(super::visit).collect(Collectors.toList())); + return new Annotation(annotationType, ctx.annotationArguments().constant().stream().map(super::visit).toList()); } return null; @@ -98,7 +98,7 @@ public Annotation visitAnnotation(SchemaGrammarParser.AnnotationContext ctx) { @Override public ClassNode visitClassDefinition(SchemaGrammarParser.ClassDefinitionContext ctx) { EnumSet attributes = visitSchemaAttributes(ctx.definitionPrefix().schemaAttributes(), Target.CLASS); - int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).collect(Collectors.toList())) + int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).toList()) | AccessUtils.getModifierValue(ctx.CLASS_TYPE().getText()); Name name = visitName(ctx.name()); String expected = ctx.expectsStatement() != null @@ -111,15 +111,36 @@ public ClassNode visitClassDefinition(SchemaGrammarParser.ClassDefinitionContext ? ctx.implementsStatement().type().stream().map(this::visitType).toList() : List.of(); List annotations = ctx.definitionPrefix().annotation().stream().map(this::visitAnnotation).toList(); - List fields = ctx.fieldDefinition().stream().map(this::visitFieldDefinition).toList(); + List fields = new ArrayList<>(); + if (ctx.autoFieldsDefinition() != null) { + fields.addAll(visitAutoFieldsDefinition(ctx.autoFieldsDefinition())); + } + fields.addAll(ctx.fieldDefinition().stream().map(this::visitFieldDefinition).toList()); List methods = ctx.methodDefinition().stream().map(this::visitMethodDefinition).toList(); return new ClassNode(attributes, accessModifiers, name, expected, annotations, extendsType, interfaceTypes, fields, methods); } + @Override + public List visitAutoFieldsDefinition(SchemaGrammarParser.AutoFieldsDefinitionContext ctx) { + EnumSet attributes = visitSchemaAttributes(ctx.definitionPrefix().schemaAttributes(), Target.FIELD); + int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).toList()); + Type type = visitType(ctx.type()); + List annotations = ctx.definitionPrefix().annotation().stream().map(this::visitAnnotation).toList(); + return ctx.autoFieldList().autoField().stream().map(f -> { + EnumSet fieldAttributes = visitSchemaAttributes(f.definitionPrefix().schemaAttributes(), Target.FIELD); + fieldAttributes.addAll(attributes); + int fieldAccessModifiers = AccessUtils.getModifiers(f.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).toList()) | accessModifiers; + List fieldAnnotations = new ArrayList<>(annotations); + fieldAnnotations.addAll(f.definitionPrefix().annotation().stream().map(this::visitAnnotation).toList()); + Type fieldType = f.type() != null ? visitType(f.type()) : type; + return new FieldNode(fieldAttributes, fieldAccessModifiers, fieldType, visitName(f.name()), fieldAnnotations); + }).toList(); + } + @Override public FieldNode visitFieldDefinition(SchemaGrammarParser.FieldDefinitionContext ctx) { EnumSet attributes = visitSchemaAttributes(ctx.definitionPrefix().schemaAttributes(), Target.FIELD); - int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).collect(Collectors.toList())); + int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).toList()); Type type = visitType(ctx.type()); Name name = visitName(ctx.name()); List annotations = ctx.definitionPrefix().annotation().stream().map(this::visitAnnotation).toList(); @@ -129,7 +150,7 @@ public FieldNode visitFieldDefinition(SchemaGrammarParser.FieldDefinitionContext @Override public MethodNode visitMethodDefinition(SchemaGrammarParser.MethodDefinitionContext ctx) { EnumSet attributes = visitSchemaAttributes(ctx.definitionPrefix().schemaAttributes(), Target.METHOD); - int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).collect(Collectors.toList())); + int accessModifiers = AccessUtils.getModifiers(ctx.definitionPrefix().ACCESS_FLAG().stream().map(ParseTree::getText).toList()); Type type = visitType(ctx.type()); Name name = visitName(ctx.name()); List annotations = ctx.definitionPrefix().annotation().stream().map(this::visitAnnotation).toList(); diff --git a/src/main/java/xyz/rodit/dexsearch/tree/attributes/Attribute.java b/src/main/java/xyz/rodit/dexsearch/tree/attributes/Attribute.java index faaad6b..68f8f47 100644 --- a/src/main/java/xyz/rodit/dexsearch/tree/attributes/Attribute.java +++ b/src/main/java/xyz/rodit/dexsearch/tree/attributes/Attribute.java @@ -15,6 +15,7 @@ public enum Attribute { NOT(EnumSet.of(Target.FIELD, Target.METHOD)), CONSERVE(EnumSet.of(Target.INSTRUCTION)), STRICT(EnumSet.of(Target.INSTRUCTION)), + OBFUSCATED(EnumSet.of(Target.CLASS, Target.FIELD, Target.METHOD)), // TODO: add support for markers in the future MARKER(EnumSet.noneOf(Target.class)); diff --git a/src/main/java/xyz/rodit/dexsearch/tree/nodes/ClassNode.java b/src/main/java/xyz/rodit/dexsearch/tree/nodes/ClassNode.java index 44ddce9..958afe5 100644 --- a/src/main/java/xyz/rodit/dexsearch/tree/nodes/ClassNode.java +++ b/src/main/java/xyz/rodit/dexsearch/tree/nodes/ClassNode.java @@ -1,5 +1,6 @@ package xyz.rodit.dexsearch.tree.nodes; +import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.iface.ClassDef; import org.jf.dexlib2.iface.Field; import org.jf.dexlib2.iface.Member; @@ -12,10 +13,14 @@ import xyz.rodit.dexsearch.tree.bindings.Reason; import xyz.rodit.dexsearch.tree.bindings.options.AccessModifiers; import xyz.rodit.dexsearch.tree.bindings.options.Option; +import xyz.rodit.dexsearch.tree.nodes.bytecode.BodyNode; +import xyz.rodit.dexsearch.tree.nodes.bytecode.events.*; +import xyz.rodit.dexsearch.tree.nodes.bytecode.matchers.StringMatcher; import xyz.rodit.dexsearch.tree.properties.Annotation; import xyz.rodit.dexsearch.tree.properties.Name; import xyz.rodit.dexsearch.tree.properties.types.ExtendsType; import xyz.rodit.dexsearch.tree.properties.types.ImplementsType; +import xyz.rodit.dexsearch.tree.properties.types.JavaType; import xyz.rodit.dexsearch.tree.properties.types.Type; import xyz.rodit.dexsearch.utils.CollectionUtils; @@ -50,6 +55,8 @@ public ClassNode(EnumSet attributes, int accessModifiers, Name name, CollectionUtils.separate(methods, this.notMethods, this.methods, m -> m.hasAttribute(Attribute.NOT)); lateFieldMap = this.fields.stream().filter(f -> f.hasAttribute(Attribute.LATE)).collect(Collectors.toMap(MemberNode::getName, f -> f)); lateMethodMap = this.methods.stream().filter(m -> m.hasAttribute(Attribute.LATE)).collect(Collectors.toMap(MemberNode::getName, m -> m)); + + processAttributes(); } public String getName() { @@ -120,4 +127,27 @@ private void bindMembers(Resolver resolver, ClassBinding bind } } } + + private void processAttributes() { + if (hasAttribute(Attribute.OBFUSCATED)) { + if (AccessUtils.hasModifiers(accessModifiers, AccessFlags.ENUM.getValue())) { + methods.add(new MethodNode( + EnumSet.of(Attribute.DISCARD), + AccessFlags.STATIC.getValue(), + new JavaType("void"), + new Name("", true), + Collections.emptySet(), + Collections.emptySet(), + fields.stream() + .filter(f -> + AccessUtils.hasModifiers(f.getAccessModifiers(), AccessFlags.STATIC.getValue()) + && f.hasAttribute(Attribute.LATE)) + .map(f -> new BodyNode( + EnumSet.of(Attribute.STRICT), + new StringMatcher(f.getName(), false, false), + Collections.singleton(new BindEvent(Operation.BIND, EventTarget.FIELD, f.getName(), Modifiers.get("next"), EventSource.REFERENCE)) + )).toList())); + } + } + } } diff --git a/src/main/java/xyz/rodit/dexsearch/tree/nodes/MemberNode.java b/src/main/java/xyz/rodit/dexsearch/tree/nodes/MemberNode.java index 7c15eae..84afa57 100644 --- a/src/main/java/xyz/rodit/dexsearch/tree/nodes/MemberNode.java +++ b/src/main/java/xyz/rodit/dexsearch/tree/nodes/MemberNode.java @@ -32,6 +32,10 @@ public MemberNode(EnumSet attributes, int accessModifiers, Type type, this.annotations = new ArrayList<>(annotations); } + public int getAccessModifiers() { + return accessModifiers; + } + public String getName() { return name.getName(); }