All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.unidbg.ios.ipa.BundleLoader Maven / Gradle / Ivy

The newest version!
package com.github.unidbg.ios.ipa;

import com.dd.plist.NSDictionary;
import com.dd.plist.PropertyListFormatException;
import com.dd.plist.PropertyListParser;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.file.ios.DarwinFileIO;
import com.github.unidbg.ios.BaseLoader;
import com.github.unidbg.ios.DarwinARM64Emulator;
import com.github.unidbg.ios.DarwinResolver;
import com.github.unidbg.ios.MachOLoader;
import com.github.unidbg.ios.MachOModule;
import com.github.unidbg.spi.SyscallHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BundleLoader extends BaseLoader {

    private static final Log log = LogFactory.getLog(BundleLoader.class);

    public static final String APP_NAME = "UniDbg";

    private final File frameworkDir;
    protected final File rootDir;

    public BundleLoader(File frameworkDir, File rootDir) {
        this.frameworkDir = frameworkDir;
        this.rootDir = rootDir;

        if (!frameworkDir.exists() || !frameworkDir.isDirectory()) {
            throw new IllegalArgumentException("Invalid frameworkDir: " + frameworkDir);
        }
    }

    private String generateRandomSeed(String bundleIdentifier, String bundleVersion) {
        return bundleIdentifier + "_" + bundleVersion;
    }

    private String generateExecutableBundlePath(String bundleIdentifier, String bundleVersion) {
        String seed = generateRandomSeed(bundleIdentifier, bundleVersion);
        UUID uuid = UUID.nameUUIDFromBytes((seed + "_Application").getBytes(StandardCharsets.UTF_8));
        return APP_DIR + uuid.toString().toUpperCase() + "/" + APP_NAME + ".app";
    }

    public LoadedBundle load(String name, EmulatorConfigurator configurator) {
        final File bundleDir = new File(this.frameworkDir, name + ".framework");
        String executable;
        String bundleVersion;
        String bundleIdentifier;
        try {
            File infoFile = new File(bundleDir, "Info.plist");
            if (!infoFile.canRead()) {
                throw new IllegalStateException("load " + name + " failed");
            }
            byte[] data = FileUtils.readFileToByteArray(infoFile);
            NSDictionary info = (NSDictionary) PropertyListParser.parse(data);
            executable = parseExecutable(info);
            bundleVersion = parseVersion(info);
            bundleIdentifier = parseCFBundleIdentifier(info);
        } catch (IOException | PropertyListFormatException | ParseException | ParserConfigurationException |
                 SAXException e) {
            throw new IllegalStateException("load " + name + " failed", e);
        }

        String executableBundleDir = generateExecutableBundlePath(bundleIdentifier, bundleVersion);
        String executableBundlePath = executableBundleDir + "/" + APP_NAME;
//        String bundleAppDir = new File(executableBundlePath).getParentFile().getParentFile().getPath();
        File rootDir = new File(this.rootDir, bundleVersion);
        try {
            Emulator emulator = new DarwinARM64Emulator(executableBundlePath, rootDir, backendFactories, getEnvs(rootDir, executableBundlePath)) {
            };
            emulator.getSyscallHandler().setVerbose(log.isDebugEnabled());
            if (configurator != null) {
                configurator.configure(emulator, executableBundlePath, rootDir, bundleIdentifier);
            }
            config(emulator, executableBundlePath, rootDir);
            MachOLoader memory = (MachOLoader) emulator.getMemory();
            memory.setObjcRuntime(true);
            DarwinResolver resolver = createLibraryResolver();
            if (overrideResolver) {
                resolver.setOverride();
            }
            memory.setLibraryResolver(resolver);
            Module module = load(emulator, configurator, new File(bundleDir, executable), executableBundleDir);
            return new LoadedBundle(emulator, module, bundleIdentifier, bundleVersion);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    protected void config(final Emulator emulator, String executableBundlePath, File rootDir) throws IOException {
        File executable = new File(executableBundlePath);
        SyscallHandler syscallHandler = emulator.getSyscallHandler();
        File appDir = executable.getParentFile();
        syscallHandler.addIOResolver(new BundleResolver(appDir.getPath(), getBundleIdentifier()));
        FileUtils.forceMkdir(new File(rootDir, appDir.getParentFile().getPath()));
        emulator.getMemory().addHookListener(new SymbolResolver(emulator));

//        ((DarwinSyscallHandler) syscallHandler).setExecutableBundlePath(executableBundlePath);
    }

    protected String getBundleIdentifier() {
        return getClass().getPackage().getName();
    }

    protected String[] getEnvs(File rootDir, String seed) throws IOException {
        List list = new ArrayList<>();
        list.add("OBJC_PRINT_EXCEPTION_THROW=YES"); // log backtrace of every objc_exception_throw()
        addEnv(list);
        UUID uuid = UUID.nameUUIDFromBytes((seed + "_Documents").getBytes(StandardCharsets.UTF_8));
        String homeDir = "/var/mobile/Containers/Data/Application/" + uuid.toString().toUpperCase();
        list.add("CFFIXED_USER_HOME=" + homeDir);
        FileUtils.forceMkdir(new File(rootDir, homeDir + "/Documents"));
        return list.toArray(new String[0]);
    }

    private Module load(Emulator emulator, EmulatorConfigurator configurator, File executable, String executableBundleDir) throws IOException {
        MachOLoader loader = (MachOLoader) emulator.getMemory();
        loader.setLoader(this);
        Module module = loader.load(new BundleLibraryFile(executable, executableBundleDir), forceCallInit);
        if (configurator != null) {
            configurator.onExecutableLoaded(emulator, (MachOModule) module);
        }
        loader.onExecutableLoaded(executable.getName());
        return module;
    }

    @Override
    public boolean isPayloadModule(String path) {
        return false;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy