Durante o processo de desenvolvimento, encontramos algumas bibliotecas de terceiros que não são de código aberto. Às vezes, precisamos nos referir a alguns dos códigos e precisamos descompilá-los para visualizá-los. Às vezes também é encontrado que o código foi ofuscado. Este artigo apresenta principalmente como usar o Threadtear para desofuscar o pacote jar ofuscado.
Endereço Threadtear Git: https://github.com/GraxCode/threadtear
compilar jar executável
Existem duas maneiras:
1. Baixe diretamente a versão mais recente lançada pelo Github. (Baixei a última versão 3.0.1 e descobri que não roda normalmente e tem anormalidades)
2. Baixe o código do projeto Git e importe o projeto com o Android Studio. E execute os dois comandos a seguir e, em seguida, um arquivo jar executável será obtido nas compilações/libs do módulo gui: threadtear-gui-3.0.0-all.jar
.
gradle build
gradle fatJar
uso de ferramentas
Execute o seguinte comando no diretório do arquivo jar executável recém-gerado (você também pode copiar o arquivo jar recém-gerado para um novo diretório) para abrir a ferramenta Threadtear:
java -noverify -jar threadtear-gui-3.0.0-all.jar
A interface da ferramenta é mostrada na figura abaixo:
Há uma configuração de aparência no menu de ajuda para definir o estilo da interface, principalmente para definir o tamanho do texto, o tamanho padrão é um pouco pequeno. . .
Em seguida, primeiro definimos as tarefas a serem executadas, clique no botão Adicionar, conforme mostrado na figura abaixo:
A seguir, adicionaremos as tarefas mostradas na figura abaixo à lista de executáveis em ordem:
Em seguida, precisaremos descompilar e desofuscar o jar diretamente Arraste-o para a área da lista de classes à direita.
Aqui não encontrei uma combinação de lista de execução que restaure completamente todas as variáveis ofuscadas de cada arquivo para seus nomes originais.A tarefa de execução porta a porta é apenas analisar cada nome de classe. O nome da variável ainda está em estudo, atualize a tempo se houver algum progresso, desculpe~
Execução personalizada
Conforme mostrado na figura acima, depois de baixar o código-fonte do projeto, crie um novo diretório personalizado no módulo principal, crie uma Execução personalizada e adicione a Execução personalizada ao ExecutionLink. Em seguida, reembalar. Dessa forma, podemos ver a Execução personalizada que acabamos de criar na ExecutionList do jar executável.
public class MyExecution extends Execution implements IConstantReferenceHandler {
public MyExecution() {
super(ExecutionCategory.GENERIC, "My execution", "Performs stack analysis and replaces code.");
}
@Override
public boolean execute(Map<String, Clazz> classes, boolean verbose) {
classes.values().stream().map(c -> c.node).forEach(this::analyzeAndRewrite);
return true;
}
public void analyzeAndRewrite(ClassNode cn) {
cn.methods.forEach(m -> {
// this analyzer keeps known stack values, e.g. can be useful for jump prediction
Analyzer<ConstantValue> a = new Analyzer<ConstantValue>(new ConstantTracker(this,
Access.isStatic(m.access), m.maxLocals, m.desc, new Object[0]));
try {
a.analyze(cn.name, m);
} catch (AnalyzerException e) {
logger.error("Failed stack analysis in " + cn.name + "." + m.name + ":" + e.getMessage());
return;
}
Frame<ConstantValue>[] frames = a.getFrames();
InsnList rewrittenCode = new InsnList();
Map<LabelNode, LabelNode> labels = Instructions.cloneLabels(m.instructions);
// rewrite method instructions
for (int i = 0; i < m.instructions.size(); i++) {
AbstractInsnNode ain = m.instructions.get(i);
Frame<ConstantValue> frame = frames[i];
// replace / modify instructions, etc...
if (frame.getStackSize() > 0) {
ConstantValue top = frame.getStack(frame.getStackSize() - 1);
if (top.isKnown() && top.isInteger()) {
int knownTopStackValue = top.getAsInteger();
// use the known stack to remove jumps, simplify code, etc...
// if(...) { rewrittenCode.add(...); }
continue;
}
}
rewrittenCode.add(ain.clone(labels));
}
// update instructions and fix try catch blocks, local variables, etc...
Instructions.updateInstructions(m, labels, rewrittenCode);
});
}
/**
* Use this method to predict stack values if fields are loaded
*/
@Override
public Object getFieldValueOrNull(BasicValue v, String owner, String name, String desc) {
return null;
}
/**
* Use this method to predict stack values if methods are invoked on known objects
*/
@Override
public Object getMethodReturnOrNull(BasicValue v, String owner, String name, String desc, List<?
extends ConstantValue> values) {
if (name.equals("toCharArray") && owner.equals("java/lang/String")) {
if (!values.get(0).isKnown()) {
// invocation target is not known, we can't compute the return
return null;
}
return ((String) values.get(0).getValue()).toCharArray();
}
return null;
}
}