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

jadx.plugins.input.smali.SmaliConvert Maven / Gradle / Ivy

The newest version!
package jadx.plugins.input.smali;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.android.tools.smali.smali.SmaliOptions;

import jadx.plugins.input.dex.utils.IDexData;
import jadx.plugins.input.dex.utils.SimpleDexData;

public class SmaliConvert {
	private static final Logger LOG = LoggerFactory.getLogger(SmaliConvert.class);

	private final List dexData = new ArrayList<>();

	public boolean execute(List input, SmaliInputOptions options) {
		List smaliFiles = filterSmaliFiles(input);
		if (smaliFiles.isEmpty()) {
			return false;
		}
		try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
			collectSystemErrors(out, () -> compile(smaliFiles, options));
			boolean success = out.size() == 0;
			if (!success) {
				LOG.error("Smali error:\n{}", out);
			}
		} catch (Exception e) {
			LOG.error("Smali process error", e);
		}
		return !dexData.isEmpty();
	}

	@SuppressWarnings("ResultOfMethodCallIgnored")
	private void compile(List inputFiles, SmaliInputOptions options) {
		SmaliOptions smaliOptions = new SmaliOptions();
		smaliOptions.apiLevel = options.getApiLevel();
		smaliOptions.verboseErrors = true;
		smaliOptions.allowOdexOpcodes = false;
		smaliOptions.printTokens = false;

		int threads = options.getThreads();
		LOG.debug("Compiling smali files: {}, threads: {}", inputFiles.size(), threads);
		long start = System.currentTimeMillis();
		if (threads == 1) {
			for (Path inputFile : inputFiles) {
				assemble(inputFile, smaliOptions);
			}
		} else {
			try {
				ExecutorService executor = Executors.newFixedThreadPool(threads);
				for (Path inputFile : inputFiles) {
					executor.execute(() -> assemble(inputFile, smaliOptions));
				}
				executor.shutdown();
				executor.awaitTermination(1, TimeUnit.DAYS);
			} catch (InterruptedException e) {
				LOG.error("Smali compile interrupted", e);
			}
		}
		if (LOG.isDebugEnabled()) {
			LOG.debug("Smali compile done in: {}ms", System.currentTimeMillis() - start);
		}
	}

	private void assemble(Path inputFile, SmaliOptions smaliOptions) {
		String fileName = inputFile.toAbsolutePath().toString();
		try (Reader reader = Files.newBufferedReader(inputFile)) {
			byte[] assemble = SmaliUtils.assemble(reader, smaliOptions);
			dexData.add(new SimpleDexData(fileName, assemble));
		} catch (Exception e) {
			throw new RuntimeException("Fail to compile: " + fileName, e);
		}
	}

	private static void collectSystemErrors(OutputStream out, Runnable exec) {
		PrintStream systemErr = System.err;
		try (PrintStream err = new PrintStream(out)) {
			System.setErr(err);
			try {
				exec.run();
			} catch (Exception e) {
				e.printStackTrace(err);
			}
		} finally {
			System.setErr(systemErr);
		}
	}

	private List filterSmaliFiles(List input) {
		PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.smali");
		return input.stream()
				.filter(matcher::matches)
				.collect(Collectors.toList());
	}

	public List getDexData() {
		return dexData;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy