From 37ab0eb29e1ac1f24db71958df4792a539b09ae6 Mon Sep 17 00:00:00 2001 From: REAndroid Date: Fri, 22 Sep 2023 02:07:42 +0200 Subject: [PATCH] [DEX] Link front end models --- .../java/com/reandroid/dex/index/ClassId.java | 4 + .../com/reandroid/dex/index/MethodId.java | 6 ++ src/main/java/com/reandroid/dex/item/Def.java | 4 + .../com/reandroid/dex/item/MethodDef.java | 37 +++++++++ .../com/reandroid/dex/model/DexClass.java | 78 ++++++++++++------- .../com/reandroid/dex/model/DexField.java | 10 ++- .../java/com/reandroid/dex/model/DexFile.java | 13 +++- .../reandroid/dex/model/DexInstruction.java | 40 ++++++++++ .../com/reandroid/dex/model/DexMethod.java | 26 ++++++- .../com/reandroid/dex/model/DexModel.java | 41 ++++++++++ .../com/reandroid/dex/sections/Section.java | 3 + 11 files changed, 229 insertions(+), 33 deletions(-) create mode 100644 src/main/java/com/reandroid/dex/model/DexInstruction.java create mode 100644 src/main/java/com/reandroid/dex/model/DexModel.java diff --git a/src/main/java/com/reandroid/dex/index/ClassId.java b/src/main/java/com/reandroid/dex/index/ClassId.java index 916c64c01..7e978f282 100644 --- a/src/main/java/com/reandroid/dex/index/ClassId.java +++ b/src/main/java/com/reandroid/dex/index/ClassId.java @@ -50,6 +50,10 @@ public ClassId() { this.staticValues = new ItemOffsetReference<>(SectionType.ENCODED_ARRAY, this, offset += 4); } + @Override + public String getKey(){ + return getName(); + } public String getName(){ TypeId typeId = getClassType(); if(typeId != null){ diff --git a/src/main/java/com/reandroid/dex/index/MethodId.java b/src/main/java/com/reandroid/dex/index/MethodId.java index 707e15521..f8e14004b 100644 --- a/src/main/java/com/reandroid/dex/index/MethodId.java +++ b/src/main/java/com/reandroid/dex/index/MethodId.java @@ -63,6 +63,9 @@ StringReference getNameReference(){ public TypeId getClassType(){ return classType.getItem(); } + public void setClassType(TypeId typeId){ + classType.setItem(typeId); + } public String getClassName() { return classType.getKey(); } @@ -90,6 +93,9 @@ public Iterator getParameters(){ public ProtoId getProto(){ return proto.getItem(); } + public void setProto(ProtoId protoId) { + proto.setItem(protoId); + } public TypeId getReturnTypeId() { ProtoId protoId = getProto(); diff --git a/src/main/java/com/reandroid/dex/item/Def.java b/src/main/java/com/reandroid/dex/item/Def.java index 4af838d2b..6a3b5e8e6 100644 --- a/src/main/java/com/reandroid/dex/item/Def.java +++ b/src/main/java/com/reandroid/dex/item/Def.java @@ -85,6 +85,10 @@ public boolean isStatic(){ T getItem(){ return mItem; } + void setItem(T item) { + this.mItem = item; + updateIndex(); + } int getIdIndex() { DefArray parentArray = getParentInstance(DefArray.class); diff --git a/src/main/java/com/reandroid/dex/item/MethodDef.java b/src/main/java/com/reandroid/dex/item/MethodDef.java index 8a01cc24d..0187dde78 100644 --- a/src/main/java/com/reandroid/dex/item/MethodDef.java +++ b/src/main/java/com/reandroid/dex/item/MethodDef.java @@ -19,6 +19,8 @@ import com.reandroid.dex.index.MethodId; import com.reandroid.dex.index.ProtoId; import com.reandroid.dex.index.TypeId; +import com.reandroid.dex.ins.Ins; +import com.reandroid.dex.sections.Section; import com.reandroid.dex.sections.SectionType; import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.CompareUtil; @@ -26,6 +28,7 @@ import java.io.IOException; import java.util.Iterator; +import java.util.Objects; public class MethodDef extends Def implements Comparable{ private final OffsetUle128Item codeOffset; @@ -35,9 +38,43 @@ public MethodDef() { this.codeOffset = new OffsetUle128Item<>(SectionType.CODE); addChild(2, codeOffset); } + + public String getName() { + MethodId methodId = getMethodId(); + if(methodId != null) { + return methodId.getName(); + } + return null; + } + public void setName(String name) { + if(Objects.equals(getName(), name)){ + return; + } + MethodId methodId = createMethodId(); + methodId.setName(name); + } + private MethodId createMethodId() { + MethodId exist = getMethodId(); + Section section = getSection(SectionType.METHOD_ID); + MethodId methodId = section.createIdItem(); + methodId.setName(exist.getName()); + methodId.setClassType(exist.getClassType()); + methodId.setProto(exist.getProto()); + setItem(methodId); + return methodId; + } public MethodId getMethodId(){ return getItem(); } + + public Iterator getInstructions() { + InstructionList instructionList = getInstructionList(); + if(instructionList != null) { + return instructionList.iterator(); + } + return EmptyIterator.of(); + } + public InstructionList getInstructionList(){ CodeItem codeItem = getCodeItem(); if(codeItem != null){ diff --git a/src/main/java/com/reandroid/dex/model/DexClass.java b/src/main/java/com/reandroid/dex/model/DexClass.java index dde3656e6..e765bd57e 100644 --- a/src/main/java/com/reandroid/dex/model/DexClass.java +++ b/src/main/java/com/reandroid/dex/model/DexClass.java @@ -18,29 +18,47 @@ import com.reandroid.dex.index.ClassId; import com.reandroid.dex.index.ItemOffsetReference; import com.reandroid.dex.item.StringData; -import com.reandroid.dex.index.TypeId; import com.reandroid.dex.item.*; import com.reandroid.dex.writer.SmaliWriter; -import com.reandroid.utils.collection.ComputeIterator; -import com.reandroid.utils.collection.EmptyIterator; -import com.reandroid.utils.collection.EmptyList; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; +import com.reandroid.utils.collection.*; + +import java.io.*; +import java.util.*; import java.util.function.Predicate; -public class DexClass implements Comparable{ +public class DexClass extends DexModel implements Comparable { + private final DexFile dexFile; private final ClassId classId; - public DexClass(ClassId classId){ + public DexClass(DexFile dexFile, ClassId classId){ + this.dexFile = dexFile; this.classId = classId; } + public Set listSuperClasses(){ + Set results = new HashSet<>(); + listSuperClasses(results); + return results; + } + private void listSuperClasses(Set results) { + DexClass dexClass = getSuperClass(); + if(dexClass != null && !results.contains(dexClass)){ + results.add(dexClass); + dexClass.listSuperClasses(results); + } + Iterator interfaceNames = getInterfaces(); + while (interfaceNames.hasNext()){ + dexClass = dexFile.get(interfaceNames.next()); + if(dexClass != null && !results.contains(dexClass)){ + results.add(dexClass); + dexClass.listSuperClasses(results); + } + } + } + + public DexClass getSuperClass() { + return dexFile.get(getSuperClassName()); + } public Iterator getStaticFields() { ClassData classData = getClassData(); if(classData != null){ @@ -53,6 +71,9 @@ public Iterator getInstanceFields() { return ComputeIterator.of(getClassData() .getInstanceFields().iterator(), this::createField); } + public Iterator getMethods() { + return new CombiningIterator<>(getDirectMethods(), getVirtualMethods()); + } public Iterator getDirectMethods() { return ComputeIterator.of(getClassData() .getDirectMethods().iterator(), this::createMethod); @@ -91,13 +112,16 @@ private String toFilePath(){ return name + ".smali"; } + public DexFile getDexFile() { + return dexFile; + } public ClassId getClassId() { return classId; } public String getName(){ return getClassId().getName(); } - public String getSuperClass(){ + public String getSuperClassName(){ return getClassId().getSuperClass().getName(); } public void setSuperClass(String superClass){ @@ -113,6 +137,10 @@ public String getSourceFile(){ public void setSourceFile(String sourceFile){ getClassId().setSourceFile(sourceFile); } + + public Iterator getInterfaceClasses(){ + return ComputeIterator.of(getInterfaces(), DexClass.this.dexFile::get); + } public Iterator getInterfaces(){ TypeList typeList = getClassId().getInterfaces(); if(typeList != null){ @@ -159,7 +187,10 @@ public AnnotationSet getAnnotations(){ } ClassData getClassData(){ - return getClassId().getClassData(); + ClassId classId = getClassId(); + ClassData classData = classId.getClassData(); + classData.setClassId(classId); + return classData; } EncodedArray getStaticValues(){ return getClassId().getStaticValues(); @@ -205,17 +236,8 @@ public int hashCode() { return 0; } @Override - public String toString() { - String name = getName(); - if(name != null){ - return name; - } - return "null"; - } - - public static DexClass create(ClassId classId){ - DexClass dexClass = new DexClass(classId); - dexClass.refresh(); - return dexClass; + public void append(SmaliWriter writer) throws IOException { + getClassData(); + getClassId().append(writer); } } diff --git a/src/main/java/com/reandroid/dex/model/DexField.java b/src/main/java/com/reandroid/dex/model/DexField.java index 1dd83590b..9cb839a7b 100644 --- a/src/main/java/com/reandroid/dex/model/DexField.java +++ b/src/main/java/com/reandroid/dex/model/DexField.java @@ -22,10 +22,13 @@ import com.reandroid.dex.item.FieldDef; import com.reandroid.dex.item.StringData; import com.reandroid.dex.value.DexValueBlock; +import com.reandroid.dex.writer.SmaliFormat; +import com.reandroid.dex.writer.SmaliWriter; +import java.io.IOException; import java.util.Iterator; -public class DexField { +public class DexField extends DexModel { private final DexClass dexClass; private final FieldDef fieldDef; @@ -95,4 +98,9 @@ public String toString() { } return builder.toString(); } + + @Override + public void append(SmaliWriter writer) throws IOException { + getFieldDef().append(writer); + } } diff --git a/src/main/java/com/reandroid/dex/model/DexFile.java b/src/main/java/com/reandroid/dex/model/DexFile.java index 35fb4b036..3b92d7dc6 100644 --- a/src/main/java/com/reandroid/dex/model/DexFile.java +++ b/src/main/java/com/reandroid/dex/model/DexFile.java @@ -40,7 +40,18 @@ public DexFile(DexFileBlock dexFileBlock){ this.dexFileBlock = dexFileBlock; } public Iterator getDexClasses() { - return ComputeIterator.of(getClassIds(), DexClass::new); + return ComputeIterator.of(getClassIds(), this::create); + } + public DexClass get(String typeName){ + Section section = getDexFileBlock().get(SectionType.CLASS_ID); + ClassId classId = section.getPool().get(typeName); + if(classId == null) { + return null; + } + return create(classId); + } + private DexClass create(ClassId classId) { + return new DexClass(this, classId); } public Marker getOrCreateMarker() { Marker marker = CollectionUtil.getFirst(getMarkers()); diff --git a/src/main/java/com/reandroid/dex/model/DexInstruction.java b/src/main/java/com/reandroid/dex/model/DexInstruction.java new file mode 100644 index 000000000..666af3679 --- /dev/null +++ b/src/main/java/com/reandroid/dex/model/DexInstruction.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.dex.model; + +import com.reandroid.dex.ins.Ins; +import com.reandroid.dex.writer.SmaliWriter; + +import java.io.IOException; + +public class DexInstruction extends DexModel{ + private final DexMethod dexMethod; + private final Ins ins; + + public DexInstruction(DexMethod dexMethod, Ins ins) { + this.dexMethod = dexMethod; + this.ins = ins; + } + + public Ins getIns() { + return ins; + } + + @Override + public void append(SmaliWriter writer) throws IOException { + getIns().append(writer); + } +} diff --git a/src/main/java/com/reandroid/dex/model/DexMethod.java b/src/main/java/com/reandroid/dex/model/DexMethod.java index 349ba5019..0be82e8ec 100644 --- a/src/main/java/com/reandroid/dex/model/DexMethod.java +++ b/src/main/java/com/reandroid/dex/model/DexMethod.java @@ -18,12 +18,15 @@ import com.reandroid.dex.common.AccessFlag; import com.reandroid.dex.index.MethodId; import com.reandroid.dex.index.TypeId; +import com.reandroid.dex.ins.Ins; import com.reandroid.dex.item.MethodDef; +import com.reandroid.dex.writer.SmaliWriter; import com.reandroid.utils.collection.ComputeIterator; +import java.io.IOException; import java.util.Iterator; -public class DexMethod { +public class DexMethod extends DexModel { private final DexClass dexClass; private final MethodDef methodDef; @@ -31,15 +34,21 @@ public DexMethod(DexClass dexClass, MethodDef methodDef){ this.dexClass = dexClass; this.methodDef = methodDef; } + public Iterator getImplementations() { + return null; + } + public Iterator getSuperMethods() { + return null; + } public String getAccessFlags() { return AccessFlag.formatForField(getMethodDef().getAccessFlagsValue()); } public String getName(){ - return getMethodId().getName(); + return getMethodDef().getName(); } public void setName(String name){ - getMethodId().setName(name); + getMethodDef().setName(name); } public int getParametersCount() { return getMethodId().getParametersCount(); @@ -61,6 +70,12 @@ public String getReturnType() { } return null; } + public Iterator getInstructions() { + return ComputeIterator.of(getMethodDef().getInstructions(), this::create); + } + private DexInstruction create(Ins ins){ + return new DexInstruction(this, ins); + } public String getKey(){ return getMethodId().getKey(); @@ -74,6 +89,11 @@ public DexClass getDexClass() { public MethodDef getMethodDef() { return methodDef; } + + @Override + public void append(SmaliWriter writer) throws IOException { + getMethodDef().append(writer); + } @Override public String toString() { return getKey(); diff --git a/src/main/java/com/reandroid/dex/model/DexModel.java b/src/main/java/com/reandroid/dex/model/DexModel.java new file mode 100644 index 000000000..07a162524 --- /dev/null +++ b/src/main/java/com/reandroid/dex/model/DexModel.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 github.com/REAndroid + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.reandroid.dex.model; + +import com.reandroid.dex.writer.SmaliFormat; +import com.reandroid.dex.writer.SmaliWriter; + +import java.io.IOException; +import java.io.StringWriter; + +public class DexModel implements SmaliFormat { + @Override + public void append(SmaliWriter writer) throws IOException { + + } + @Override + public String toString() { + StringWriter writer = new StringWriter(); + SmaliWriter smaliWriter = new SmaliWriter(writer); + try { + append(smaliWriter); + smaliWriter.close(); + } catch (IOException exception) { + return exception.toString(); + } + return writer.toString(); + } +} diff --git a/src/main/java/com/reandroid/dex/sections/Section.java b/src/main/java/com/reandroid/dex/sections/Section.java index 9a0b5dcc1..f572c86f5 100644 --- a/src/main/java/com/reandroid/dex/sections/Section.java +++ b/src/main/java/com/reandroid/dex/sections/Section.java @@ -121,6 +121,9 @@ public T[] getAt(int[] offsets){ } return results; } + public T createIdItem() { + return getItemArray().createNext(); + } public T createOffsetItem() { int position = estimateLastOffset(); T item = getItemArray().createNext();