Skip to content

Commit

Permalink
[Test] Create and test dex file
Browse files Browse the repository at this point in the history
  • Loading branch information
REAndroid committed Dec 30, 2023
1 parent 02da0aa commit 18818db
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 25 deletions.
79 changes: 54 additions & 25 deletions src/test/java/com/reandroid/apk/ApkModuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@
import com.reandroid.arsc.chunk.PackageBlock;
import com.reandroid.arsc.chunk.TableBlock;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
import com.reandroid.arsc.chunk.xml.ResXmlElement;
import com.reandroid.arsc.coder.EncodeResult;
import com.reandroid.arsc.coder.ValueCoder;
import com.reandroid.arsc.item.TableString;
import com.reandroid.arsc.model.ResourceEntry;
import com.reandroid.arsc.pool.TableStringPool;
import com.reandroid.arsc.value.*;
import com.reandroid.dex.SampleDexFileCreator;
import com.reandroid.dex.model.DexFile;
import com.reandroid.xml.*;
import org.junit.Assert;
import org.junit.FixMethodOrder;
Expand Down Expand Up @@ -69,14 +74,60 @@ public ApkModule createApkModule() throws IOException {

apkModule.add(tableSource);
tableSource.setMethod(ZipEntry.STORED);
apkModule.getTableBlock();

apkModule.add(new ByteInputSource(EMPTY_DEX_FILE, "classes.dex"));
int mainActivityLayoutId = createMainActivityContentViewXml(apkModule);

apkModule.getTableBlock().refreshFull();

String appClass = AndroidManifestBlock.getAndroidNameValue(manifestBlock.getApplicationElement());
String mainActivity = AndroidManifestBlock.getAndroidNameValue(manifestBlock.getMainActivity());

DexFile dexFile = SampleDexFileCreator.createApplicationClass(appClass, mainActivity, mainActivityLayoutId);
byte[] bytes = dexFile.getBytes();
apkModule.add(new ByteInputSource(bytes, "classes.dex"));

last_apkModule = apkModule;

return apkModule;
}
private int createMainActivityContentViewXml(ApkModule apkModule){
ResXmlDocument document = new ResXmlDocument();
ResXmlElement root = document.getDocumentElement();
root.setName("LinearLayout");

ResXmlAttribute attribute = root.getOrCreateAndroidAttribute("layout_width", 0x010100f4);
attribute.setTypeAndData(ValueType.DEC, -1); // match_parent

attribute = root.getOrCreateAndroidAttribute("layout_height", 0x010100f5);
attribute.setTypeAndData(ValueType.DEC, -1); // match_parent

attribute = root.getOrCreateAndroidAttribute("orientation", 0x010100c4);
attribute.setTypeAndData(ValueType.DEC, 1); // vertical

ResXmlElement textView = root.createChildElement("TextView");
attribute = textView.getOrCreateAndroidAttribute("layout_width", 0x010100f4);
attribute.setTypeAndData(ValueType.DEC, -2); // wrap_content

attribute = textView.getOrCreateAndroidAttribute("layout_height", 0x010100f5);
attribute.setTypeAndData(ValueType.DEC, -2); // wrap_content

attribute = textView.getOrCreateAndroidAttribute("text", 0x0101014f);
attribute.setValueAsString("Hello World!");

document.refreshFull();

String path = "res/layout/activity_main.xml";

ByteInputSource source = new ByteInputSource(document.getBytes(), path);
apkModule.add(source);

TableBlock tableBlock = apkModule.getTableBlock();
Entry entry = tableBlock.pickOne().getOrCreate("", "layout", "activity_main");
entry.setValueAsString(path);


return entry.getResourceId();
}
private TableBlock createTableBlock(AndroidManifestBlock manifestBlock){
TableBlock tableBlock = new TableBlock();
String packageName = manifestBlock.getPackageName();
Expand Down Expand Up @@ -323,7 +374,7 @@ private AndroidManifestBlock createManifest() throws IOException {
manifestBlock.addUsesPermission("android.permission.INTERNET");
manifestBlock.addUsesPermission("android.permission.READ_EXTERNAL_STORAGE");

manifestBlock.getOrCreateMainActivity("android.app.Activity");
manifestBlock.getOrCreateMainActivity(manifestBlock.getPackageName() + ".MyActivity");

manifestBlock.refresh();

Expand Down Expand Up @@ -355,26 +406,4 @@ public static ApkModule getLastApkModule(){
return last_apkModule;
}

private static final byte[] EMPTY_DEX_FILE = new byte[]{
(byte)0x64, (byte)0x65, (byte)0x78, (byte)0x0A, (byte)0x30, (byte)0x33, (byte)0x35, (byte)0x00,
(byte)0xE0, (byte)0x0E, (byte)0x82, (byte)0xEC, (byte)0xC5, (byte)0xCC, (byte)0x6A, (byte)0xFF,
(byte)0x1E, (byte)0x65, (byte)0xE2, (byte)0x24, (byte)0x9A, (byte)0x48, (byte)0x13, (byte)0x52,
(byte)0x4C, (byte)0xEE, (byte)0xA2, (byte)0xA1, (byte)0x71, (byte)0x9D, (byte)0x67, (byte)0xE6,
(byte)0x9C, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x78, (byte)0x56, (byte)0x34, (byte)0x12, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x2C, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x10, (byte)0x00, (byte)0x00,
(byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x74, (byte)0x00, (byte)0x00, (byte)0x00
};
}
110 changes: 110 additions & 0 deletions src/test/java/com/reandroid/dex/SampleDexFileCreator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.reandroid.dex;

import com.reandroid.dex.common.AccessFlag;
import com.reandroid.dex.common.DexUtils;
import com.reandroid.dex.ins.Opcode;
import com.reandroid.dex.key.MethodKey;
import com.reandroid.dex.key.TypeKey;
import com.reandroid.dex.model.DexClass;
import com.reandroid.dex.model.DexFile;
import com.reandroid.dex.model.DexInstruction;
import com.reandroid.dex.model.DexMethod;

import java.io.IOException;

public class SampleDexFileCreator {

public static DexFile createApplicationClass(String appSourceName, String activitySourceName, int contentViewResourceId) throws IOException {
DexFile dexFile = DexFile.createDefault();
createApplicationClass(dexFile, appSourceName);
createActivityClass(dexFile, activitySourceName, contentViewResourceId);
dexFile.clearUnused();
dexFile.clearEmptySections();
dexFile.refreshFull();
return dexFile;
}
private static void createApplicationClass(DexFile dexFile, String classSourceName){
if(classSourceName == null){
return;
}
String binClassName = DexUtils.toBinaryName(classSourceName);

DexClass dexClass = dexFile.getOrCreateClass(binClassName);
dexClass.addAccessFlag(AccessFlag.PUBLIC);
dexClass.setSuperClass(TypeKey.create("Landroid/app/Application;"));

createConstructor(dexClass);
}
private static void createActivityClass(DexFile dexFile, String activitySourceName, int contentViewResourceId){
String binClassName = DexUtils.toBinaryName(activitySourceName);

DexClass dexClass = dexFile.getOrCreateClass(binClassName);
dexClass.addAccessFlag(AccessFlag.PUBLIC);
dexClass.setSuperClass(TypeKey.create("Landroid/app/Activity;"));

createConstructor(dexClass);
create_onCreate(dexClass, contentViewResourceId);
}

private static void createConstructor(DexClass dexClass){
MethodKey methodKey = MethodKey.CONSTRUCTOR.changeDeclaring(dexClass.getKey());
DexMethod constructor = dexClass.getOrCreateDirectMethod(methodKey);

constructor.addAccessFlag(AccessFlag.PUBLIC);
constructor.addAccessFlag(AccessFlag.CONSTRUCTOR);

constructor.setParameterRegistersCount(1);
constructor.setLocalRegistersCount(0);

DexInstruction invokeSuper = constructor.addInstruction(Opcode.INVOKE_DIRECT);

TypeKey declaring = dexClass.getSuperClassKey();

MethodKey superMethodKey = methodKey.changeDeclaring(declaring);
invokeSuper.setKey(superMethodKey);

invokeSuper.setRegistersCount(1);
invokeSuper.setRegister(0); // p0 (this)

constructor.addInstruction(Opcode.RETURN_VOID);
constructor.refreshInstructions();
}

private static void create_onCreate(DexClass dexClass, int contentViewResourceId){

MethodKey superMethodKey = MethodKey.parse("Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V");

MethodKey methodKey = superMethodKey.changeDeclaring(dexClass.getKey());

DexMethod onCreate = dexClass.getOrCreateVirtualMethod(methodKey);

onCreate.addAccessFlag(AccessFlag.PUBLIC);

onCreate.setParameterRegistersCount(1 + superMethodKey.getParameterRegistersCount());
onCreate.setLocalRegistersCount(1);

DexInstruction invokeSuper = onCreate.addInstruction(Opcode.INVOKE_SUPER);
invokeSuper.setKey(superMethodKey);

invokeSuper.setRegistersCount(2);
invokeSuper.setRegister(0, 1); // p0 (this)
invokeSuper.setRegister(1, 2); // p1

DexInstruction constInstruction = onCreate.addInstruction(Opcode.CONST);
constInstruction.setRegister(0);
constInstruction.setAsInteger(contentViewResourceId);

MethodKey setContentViewKey = MethodKey.parse("Landroid/app/Activity;->setContentView(I)V");
setContentViewKey = setContentViewKey.changeDeclaring(dexClass.getKey());

DexInstruction invokeVirtual = onCreate.addInstruction(Opcode.INVOKE_VIRTUAL);
invokeVirtual.setKey(setContentViewKey);

invokeVirtual.setRegistersCount(2);
invokeVirtual.setRegister(0, 1); // p0 (this)
invokeVirtual.setRegister(1, constInstruction.getRegister()); // v0

onCreate.addInstruction(Opcode.RETURN_VOID);
onCreate.refreshInstructions();
}
}

0 comments on commit 18818db

Please sign in to comment.