JesusFreke / smali

smali/baksmali
6.26k stars 1.07k forks source link

any method to replace one String with modifiedString in StringIds section of dex file using dexlib2? #874

Closed ManojBhakarPCM closed 11 months ago

ManojBhakarPCM commented 11 months ago

i have a Dex Editor project which uses smali.. but smali uses dexlib2. so i need to use this existing library to create a class renamer. i have assumption that if i replace a string in StringId section of dex file then it will be replaced all over the dex file (ie . in methods, fields,class, annotations, instruction type refs. etc.). i think this is the fastest way to refector. but i cant find any class in dexlib2 where i can do this. i tried DexPool, but dexPool just let me read stringIdSection, i cant edit it. i dont know if this is the right place to ask this question about dexlib2

auermich93 commented 11 months ago

I am not sure what you actually intend to do but there is a class called DexRewriter that may suits your needs. See the following example: https://github.com/JesusFreke/smali/blob/2771eae0a11f07bd892732232e6ee4e32437230d/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java#L52

ManojBhakarPCM commented 11 months ago

i want to do refectoring/renaming/obsfucation. means i want to rename a class from "Lx/y/bar"; to "Lx/y/foo;" in a already compiled dex file. there are many way to do it.

  1. first disassemble all classDefs into smali files any then find and replace in all and then compile them to dex again. this method will take a lot of time because we need to decompile all classes in dex.
    1. using dexRewriter- this method is also heavy because it will need a lot more coding. because first i need to parse dex file then for its all classes, parse fields and methods, then for all methods, parse instructions, and then for all classdef + methods + method parameters + fields + instruction type references, parse annotations. and its not all, because in annotations, i need to parse annotation items and then encoded values which can also be type of annotations. very complex method it is.
    2. what i thought is - every thing in dex file such as classDef, method, field , annotations, method parameters etc reference a class name such as L/x/y/foo; from a single place. which is StringDataSection of dex file. if we replace L/x/y/foo; with L/x/y/bar; in StringDataSection of dex file, then it will be automatically replaced in all referenced classes, methods, fields, method parameters , annotatations etc. thats why i need the class that provide me list of strings in StringDataSection of dex file and then after modification i can set this list of string back to dex file. i should be clear now? aint i?
ManojBhakarPCM commented 11 months ago

woah, i did it by modifying dexlib and it found working in primary tests. first downloaded source of dexlib, loaded in intelliJ idea, converted it to pom project from gradle file using my script- "gradle2marven.py"(can be found in my repo). deleted smali and baksmali project folders, kept only dexlib2 project. modifications - modifications done in DexWriter, since DexPool is extending from it, so all methods added to this will apear in DexPool class. aim was to add a interface which let you modify orignal string just before it is written to the StringDataSection. so in DexWriter-

  1. added a field- "public StringSectionEditor sectionEditor = null;"

  2. added a method also-

    public void addSectionEditorListener(StringSectionEditor editor){
        sectionEditor = editor;
    }
    1. modifyed the writeStrings() method as following-
      for (Map.Entry<? extends StringKey, Integer>  entry: stringEntries) {
          entry.setValue(index++);
          indexWriter.writeInt(offsetWriter.getPosition());
          String stringValue = entry.getKey().toString();
          if(sectionEditor!=null){ // this if-condition is added
              stringValue = sectionEditor.modifyString(stringValue);
          }
          offsetWriter.writeUleb128(stringValue.length());
          offsetWriter.writeString(stringValue);
          offsetWriter.write(0);
      }
  3. added the interface at the end of file(but inside this class)

    public interface StringSectionEditor{
        public String modifyString(String orignalString);
    }

    testing

    this was my understanding about DexPool class - DexPool is a container where classes can be added, using internClass() method, and DexPool extracts its needed parts for writing all sections about added ClassDef. so first we need to create a Dex file, then a DexPool , then ClassDefs need to be added to DexPool from DexFile using internClass method. but just before we use writeTo() method on DexPool(which creates the output dex file according to interned classes), we will add a Interface. so that when writeStrings() method will be called during dexMaking process, all strings will be prompted to be modified before us on this interface. there we will modify our strings. then they will be written to the dex's StringDataSection.

to test whether the modification caused any errors or not, i used jadx-gui , if the moded dex is loaded in this software correctly without error (same as the orignal dex) then our test is successfull. we should also see the changed package names and changed method names etc, and these changes should appear everywhere in the dex, means if we change the name of a method then it should appear changed all the places where it is called.

testing code was like this-

public static void testModdedWrite(String dexFilePath){
        DexFile dexFile = null;
        try {
            dexFile = loadDexFile(dexFilePath, null);
            DexPool dexPool = new DexPool(dexFile.getOpcodes());
            for(ClassDef classDef: dexFile.getClasses()){
                dexPool.internClass(classDef);
            }

            for(Map.Entry<String, Integer> s: dexPool.stringSection.getItems()){
                System.out.printf("%d  : %s\n",s.getValue(),s.getKey());
            }
            dexPool.addSectionEditorListener(new DexWriter.StringSectionEditor() {
                @Override
                public String modifyString(String orignalString) {
                    if(orignalString.startsWith("Lbhakar/manoj/testappv32/")) {
                        return orignalString.replace("Lbhakar/manoj/testappv32/", "Lbhakar/manoj/testappv50/");
                    }else if(orignalString.equals("dbToLayout")){
                            return "modded";
                    }else{
                        return orignalString;
                    }
                }
            });
            dexPool.writeTo(new FileDataStore(new File("H:\\outdex_dexPool_moddedStringSection.dex")));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

and when i tested it. it was super successfull. all classes's package name was changed, and the method named "dbToLayout" was converted into "modded" , it also appeared as "modded" 3 other places where it was called.

i hope this will help someone who want to do refectoring using dexlib2, i wasted 5 days in search of this.