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

com.github.unidbg.ios.dmg.DmgLoader Maven / Gradle / Ivy

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

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.ios.ipa.EmulatorConfigurator;
import com.github.unidbg.ios.ipa.SymbolResolver;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.spi.SyscallHandler;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
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 abstract class DmgLoader extends BaseLoader {

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

    @SuppressWarnings("unused")
    public final LoadedDmg load(String... loads) {
        return load(null, loads);
    }

    public abstract LoadedDmg load(EmulatorConfigurator configurator, String... loads);

    private final String appDir;
    protected final File dmgDir;
    protected final File rootDir;

    private final String executable;
    private final String bundleVersion;
    private final String bundleIdentifier;

    protected final String executableBundlePath;

    DmgLoader(File dmgDir, File rootDir) {
        this.dmgDir = dmgDir;
        this.rootDir = rootDir;
        this.appDir = FilenameUtils.normalize(dmgDir.getPath(), true);

        File infoFile = new File(dmgDir, "Contents/Info.plist");
        if (!infoFile.exists() || !infoFile.isFile()) {
            throw new IllegalStateException("Invalid dmg app dir: " + dmgDir);
        }

        try {
            NSDictionary info = (NSDictionary) PropertyListParser.parse(infoFile);
            this.executable = parseExecutable(info);
            this.bundleVersion = parseVersion(info);
            this.bundleIdentifier = parseCFBundleIdentifier(info);
        } catch (IOException | PropertyListFormatException | ParseException | ParserConfigurationException | SAXException e) {
            throw new IllegalStateException("load " + dmgDir.getAbsolutePath() + " failed", e);
        }
        this.executableBundlePath = generateExecutableBundlePath();
    }

    private String generateExecutableBundlePath() {
        File executable = new File(dmgDir, "Contents/MacOS/" + this.executable);
        if (!executable.exists() || !executable.isFile()) {
            throw new IllegalStateException("Invalid executable: " + executable);
        }
        return executable.getAbsolutePath();
    }

    protected void config(final Emulator emulator, File dmgDir) {
        SyscallHandler syscallHandler = emulator.getSyscallHandler();
        syscallHandler.addIOResolver(new DmgResolver(dmgDir));
        emulator.getMemory().addHookListener(new SymbolResolver(emulator));
    }

    protected Emulator createEmulator(File rootDir) throws IOException {
        return new DarwinARM64Emulator(executableBundlePath, rootDir, backendFactories, getEnvs(rootDir)) {
        };
    }

    LoadedDmg load64(EmulatorConfigurator configurator, String... loads) throws IOException {
        String bundleAppDir = new File(executableBundlePath).getParentFile().getParentFile().getPath();
        File rootDir = new File(this.rootDir, bundleVersion);
        Emulator emulator = createEmulator(rootDir);
        emulator.getSyscallHandler().setVerbose(log.isDebugEnabled());
        if (configurator != null) {
            configurator.configure(emulator, executableBundlePath, rootDir, bundleIdentifier);
        }
        config(emulator, dmgDir);
        Memory memory = emulator.getMemory();
        DarwinResolver resolver = createLibraryResolver();
        if (overrideResolver) {
            resolver.setOverride();
        }
        memory.setLibraryResolver(resolver);
        return load(emulator, bundleAppDir, configurator, loads);
    }

    protected String[] getEnvs(File rootDir) 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((bundleIdentifier + "_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));
        return list.toArray(new String[0]);
    }

    private LoadedDmg load(Emulator emulator, String bundleAppDir, EmulatorConfigurator configurator, String... loads) {
        MachOLoader loader = (MachOLoader) emulator.getMemory();
        loader.setLoader(this);
        File executableFile = new File(executableBundlePath);
        Module module = loader.load(new DmgLibraryFile(executableFile.getParent(), executable, bundleAppDir, executableFile, loads), forceCallInit);
        if (configurator != null) {
            configurator.onExecutableLoaded(emulator, (MachOModule) module);
        }
        loader.onExecutableLoaded(executable);
        return new LoadedDmg(emulator, module, bundleIdentifier, bundleVersion);
    }

    @Override
    public final boolean isPayloadModule(String path) {
        return path.startsWith(appDir);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy