mirror of
https://github.com/revanced/smali.git
synced 2025-05-14 21:27:06 +02:00
Add a CodeFragmentFactory to allow evaluation of arbitrary registers
This commit is contained in:
parent
cbd87b904e
commit
a28e8f2363
@ -24,6 +24,7 @@
|
|||||||
<debugger.positionManagerFactory implementation="org.jf.smalidea.debugging.SmaliPositionManagerFactory"/>
|
<debugger.positionManagerFactory implementation="org.jf.smalidea.debugging.SmaliPositionManagerFactory"/>
|
||||||
<xdebugger.debuggerSupport id="SmaliDebuggerSupport" order="first,before XDebuggerSupport"
|
<xdebugger.debuggerSupport id="SmaliDebuggerSupport" order="first,before XDebuggerSupport"
|
||||||
implementation="org.jf.smalidea.debugging.SmaliDebuggerSupport"/>
|
implementation="org.jf.smalidea.debugging.SmaliDebuggerSupport"/>
|
||||||
|
<debugger.codeFragmentFactory implementation="org.jf.smalidea.debugging.SmaliCodeFragmentFactory"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.smalidea.debugging;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.intellij.debugger.DebuggerManagerEx;
|
||||||
|
import com.intellij.debugger.SourcePosition;
|
||||||
|
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||||
|
import com.intellij.debugger.engine.evaluation.*;
|
||||||
|
import com.intellij.debugger.engine.events.DebuggerCommandImpl;
|
||||||
|
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||||
|
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||||
|
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.psi.JavaCodeFragment;
|
||||||
|
import com.intellij.psi.JavaRecursiveElementVisitor;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.PsiLocalVariable;
|
||||||
|
import com.sun.jdi.*;
|
||||||
|
import com.sun.tools.jdi.LocalVariableImpl;
|
||||||
|
import com.sun.tools.jdi.LocationImpl;
|
||||||
|
import org.jf.dexlib2.analysis.AnalyzedInstruction;
|
||||||
|
import org.jf.dexlib2.analysis.RegisterType;
|
||||||
|
import org.jf.smalidea.SmaliFileType;
|
||||||
|
import org.jf.smalidea.psi.impl.SmaliInstruction;
|
||||||
|
import org.jf.smalidea.psi.impl.SmaliMethod;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SmaliCodeFragmentFactory extends DefaultCodeFragmentFactory {
|
||||||
|
@Override
|
||||||
|
public JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, Project project) {
|
||||||
|
return super.createCodeFragment(item, wrapContext(project, context), project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaCodeFragment createPresentationCodeFragment(TextWithImports item, PsiElement context, Project project) {
|
||||||
|
return super.createPresentationCodeFragment(item, wrapContext(project, context), project);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public LanguageFileType getFileType() {
|
||||||
|
return SmaliFileType.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PsiElement wrapContext(final Project project, final PsiElement originalContext) {
|
||||||
|
if (project.isDefault()) return originalContext;
|
||||||
|
|
||||||
|
final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext();
|
||||||
|
|
||||||
|
SourcePosition position = debuggerContext.getSourcePosition();
|
||||||
|
SmaliInstruction currentInstruction = (SmaliInstruction)position.getElementAt();
|
||||||
|
|
||||||
|
final SmaliMethod containingMethod = currentInstruction.getParentMethod();
|
||||||
|
AnalyzedInstruction analyzedInstruction = currentInstruction.getAnalyzedInstruction();
|
||||||
|
|
||||||
|
final Map<String, String> registerMap = Maps.newHashMap();
|
||||||
|
StringBuilder variablesText = new StringBuilder();
|
||||||
|
for (int i=0; i<containingMethod.getRegisterCount(); i++) {
|
||||||
|
RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(i);
|
||||||
|
switch (registerType.category) {
|
||||||
|
case RegisterType.UNKNOWN:
|
||||||
|
case RegisterType.UNINIT:
|
||||||
|
case RegisterType.CONFLICTED:
|
||||||
|
case RegisterType.LONG_HI:
|
||||||
|
case RegisterType.DOUBLE_HI:
|
||||||
|
continue;
|
||||||
|
case RegisterType.NULL:
|
||||||
|
case RegisterType.ONE:
|
||||||
|
case RegisterType.INTEGER:
|
||||||
|
variablesText.append("int v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "I");
|
||||||
|
break;
|
||||||
|
case RegisterType.BOOLEAN:
|
||||||
|
variablesText.append("boolean v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "Z");
|
||||||
|
break;
|
||||||
|
case RegisterType.BYTE:
|
||||||
|
case RegisterType.POS_BYTE:
|
||||||
|
variablesText.append("byte v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "B");
|
||||||
|
break;
|
||||||
|
case RegisterType.SHORT:
|
||||||
|
case RegisterType.POS_SHORT:
|
||||||
|
variablesText.append("short v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "S");
|
||||||
|
break;
|
||||||
|
case RegisterType.CHAR:
|
||||||
|
variablesText.append("char v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "C");
|
||||||
|
break;
|
||||||
|
case RegisterType.FLOAT:
|
||||||
|
variablesText.append("float v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "F");
|
||||||
|
break;
|
||||||
|
case RegisterType.LONG_LO:
|
||||||
|
variablesText.append("long v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "J");
|
||||||
|
break;
|
||||||
|
case RegisterType.DOUBLE_LO:
|
||||||
|
variablesText.append("double v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "D");
|
||||||
|
break;
|
||||||
|
case RegisterType.UNINIT_REF:
|
||||||
|
case RegisterType.UNINIT_THIS:
|
||||||
|
case RegisterType.REFERENCE:
|
||||||
|
variablesText.append("Object v").append(i).append(";\n");
|
||||||
|
registerMap.put("v" + i, "Ljava/lang/Object;");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK,
|
||||||
|
variablesText.toString(), "", getFileType());
|
||||||
|
|
||||||
|
final JavaCodeFragment codeFragment = super.createCodeFragment(textWithImports, originalContext, project);
|
||||||
|
|
||||||
|
codeFragment.accept(new JavaRecursiveElementVisitor() {
|
||||||
|
public void visitLocalVariable(final PsiLocalVariable variable) {
|
||||||
|
final String name = variable.getName();
|
||||||
|
if (registerMap.containsKey(name)) {
|
||||||
|
debuggerContext.getDebugProcess().getManagerThread().invoke(new DebuggerCommandImpl() {
|
||||||
|
@Override protected void action() throws Exception {
|
||||||
|
Value value = evaluateRegister(debuggerContext.createEvaluationContext(),
|
||||||
|
containingMethod,
|
||||||
|
Integer.parseInt(name.substring(1)),
|
||||||
|
registerMap.get(name));
|
||||||
|
variable.putUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int offset = variablesText.length() - 1;
|
||||||
|
|
||||||
|
final PsiElement newContext = codeFragment.findElementAt(offset);
|
||||||
|
if (newContext != null) {
|
||||||
|
return newContext;
|
||||||
|
}
|
||||||
|
return originalContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value evaluateRegister(EvaluationContextImpl context, final SmaliMethod smaliMethod, final int registerNum,
|
||||||
|
final String type) {
|
||||||
|
final StackFrameProxyImpl frameProxy = context.getFrameProxy();
|
||||||
|
if (frameProxy == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the jdi apis don't provide any way to get the value of an arbitrary register, so we use reflection
|
||||||
|
// to create a LocalVariable instance for the register
|
||||||
|
final Value[] ret = new Value[1];
|
||||||
|
|
||||||
|
DebugProcessImpl debugProcess = context.getDebugProcess();
|
||||||
|
debugProcess.getManagerThread().invoke(
|
||||||
|
new DebuggerCommandImpl() {
|
||||||
|
|
||||||
|
@Override protected void action() throws Exception {
|
||||||
|
VirtualMachine vm = frameProxy.getVirtualMachine().getVirtualMachine();
|
||||||
|
Method method = frameProxy.location().method();
|
||||||
|
|
||||||
|
final Constructor<LocalVariableImpl> localVariableConstructor = LocalVariableImpl.class.getDeclaredConstructor(
|
||||||
|
VirtualMachine.class, Method.class, Integer.TYPE, Location.class, Location.class, String.class,
|
||||||
|
String.class, String.class);
|
||||||
|
localVariableConstructor.setAccessible(true);
|
||||||
|
|
||||||
|
Constructor<LocationImpl> locationConstructor = LocationImpl.class.getDeclaredConstructor(
|
||||||
|
VirtualMachine.class, Method.class, Long.TYPE);
|
||||||
|
locationConstructor.setAccessible(true);
|
||||||
|
|
||||||
|
// TODO: use frameProxy.location().method().locationOfCodeIndex() here
|
||||||
|
Location endLocation = locationConstructor.newInstance(vm, method, Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
LocalVariable localVariable = localVariableConstructor.newInstance(vm,
|
||||||
|
method,
|
||||||
|
mapRegisterForArt(smaliMethod, registerNum),
|
||||||
|
frameProxy.location().method().locationOfCodeIndex(0),
|
||||||
|
endLocation,
|
||||||
|
String.format("v%d", registerNum), type, null);
|
||||||
|
|
||||||
|
ret[0] = frameProxy.getStackFrame().getValue(localVariable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return ret[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int mapRegisterForArt(SmaliMethod smaliMethod, int register) {
|
||||||
|
int totalRegisters = smaliMethod.getRegisterCount();
|
||||||
|
int parameterRegisters = smaliMethod.getParameterRegisterCount();
|
||||||
|
|
||||||
|
// For ART, the parameter registers are rotated to the front
|
||||||
|
if (register >= (totalRegisters - parameterRegisters)) {
|
||||||
|
return register - (totalRegisters - parameterRegisters);
|
||||||
|
}
|
||||||
|
return register + parameterRegisters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user