mirror of
https://github.com/revanced/smali.git
synced 2025-05-08 10:24:31 +02:00
Remove deodexerant and related functionality
git-svn-id: https://smali.googlecode.com/svn/trunk@797 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
parent
b3ba356d94
commit
b9a19bf3f1
@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* [The "BSD licence"]
|
|
||||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. 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.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.baksmali;
|
|
||||||
|
|
||||||
import org.apache.commons.cli.*;
|
|
||||||
import org.jf.dexlib.Code.Analysis.ClassPath;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class deodexCheck {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
CommandLineParser parser = new PosixParser();
|
|
||||||
CommandLine commandLine;
|
|
||||||
|
|
||||||
Options options = buildOptions();
|
|
||||||
|
|
||||||
try {
|
|
||||||
commandLine = parser.parse(options, args);
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
usage(options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
|
|
||||||
List<String> bootClassPathDirs = new ArrayList<String>();
|
|
||||||
bootClassPathDirs.add(".");
|
|
||||||
String deodexerantHost = null;
|
|
||||||
int deodexerantPort = 0;
|
|
||||||
int classStartIndex = 0;
|
|
||||||
|
|
||||||
|
|
||||||
String[] remainingArgs = commandLine.getArgs();
|
|
||||||
|
|
||||||
|
|
||||||
if (commandLine.hasOption("v")) {
|
|
||||||
main.version();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandLine.hasOption("?")) {
|
|
||||||
usage(options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingArgs.length > 0) {
|
|
||||||
usage(options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (commandLine.hasOption("c")) {
|
|
||||||
String bcp = commandLine.getOptionValue("c");
|
|
||||||
if (bcp.charAt(0) == ':') {
|
|
||||||
bootClassPath = bootClassPath + bcp;
|
|
||||||
} else {
|
|
||||||
bootClassPath = bcp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandLine.hasOption("i")) {
|
|
||||||
try {
|
|
||||||
classStartIndex = Integer.parseInt(commandLine.getOptionValue("i"));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandLine.hasOption("d")) {
|
|
||||||
bootClassPathDirs.add(commandLine.getOptionValue("d"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (commandLine.hasOption("x")) {
|
|
||||||
String deodexerantAddress = commandLine.getOptionValue("x");
|
|
||||||
String[] parts = deodexerantAddress.split(":");
|
|
||||||
if (parts.length != 2) {
|
|
||||||
System.err.println("Invalid deodexerant address. Expecting :<port> or <host>:<port>");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
deodexerantHost = parts[0];
|
|
||||||
if (deodexerantHost.length() == 0) {
|
|
||||||
deodexerantHost = "localhost";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
deodexerantPort = Integer.parseInt(parts[1]);
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
System.err.println("Invalid port \"" + deodexerantPort + "\" for deodexerant address");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] bootClassPathDirsArray = new String[bootClassPathDirs.size()];
|
|
||||||
for (int i=0; i<bootClassPathDirsArray.length; i++) {
|
|
||||||
bootClassPathDirsArray[i] = bootClassPathDirs.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassPath.InitializeClassPath(bootClassPathDirsArray, bootClassPath==null?null:bootClassPath.split(":"), null,
|
|
||||||
null, null, null);
|
|
||||||
|
|
||||||
ClassPath.validateAgainstDeodexerant(deodexerantHost, deodexerantPort, classStartIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the usage message.
|
|
||||||
*/
|
|
||||||
private static void usage(Options options) {
|
|
||||||
HelpFormatter formatter = new HelpFormatter();
|
|
||||||
formatter.setWidth(100);
|
|
||||||
formatter.printHelp("java -classpath baksmali.jar deodexCheck -x HOST:PORT [options]",
|
|
||||||
"disassembles and/or dumps a dex file", options, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Options buildOptions() {
|
|
||||||
Options options = new Options();
|
|
||||||
|
|
||||||
Option versionOption = OptionBuilder.withLongOpt("version")
|
|
||||||
.withDescription("prints the version then exits")
|
|
||||||
.create("v");
|
|
||||||
|
|
||||||
Option helpOption = OptionBuilder.withLongOpt("help")
|
|
||||||
.withDescription("prints the help message then exits")
|
|
||||||
.create("?");
|
|
||||||
|
|
||||||
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
|
||||||
.withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
|
|
||||||
"core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If you specify a value that " +
|
|
||||||
"begins with a :, it will be appended to the default bootclasspath")
|
|
||||||
.hasOptionalArg()
|
|
||||||
.withArgName("BOOTCLASSPATH")
|
|
||||||
.create("c");
|
|
||||||
|
|
||||||
Option classPathDirOption = OptionBuilder.withLongOpt("bootclasspath-dir")
|
|
||||||
.withDescription("the base folder to look for the bootclasspath files in. Defaults to the current " +
|
|
||||||
"directory.")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("DIR")
|
|
||||||
.create("d");
|
|
||||||
|
|
||||||
Option deodexerantOption = OptionBuilder.withLongOpt("deodexerant")
|
|
||||||
.isRequired()
|
|
||||||
.withDescription("connect to deodexerant on the specified HOST:PORT, and validate the virtual method " +
|
|
||||||
"indexes, field offsets and inline methods against what dexlib calculates")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("HOST:PORT")
|
|
||||||
.create("x");
|
|
||||||
|
|
||||||
Option classStartOption = OptionBuilder.withLongOpt("class-start-index")
|
|
||||||
.withDescription("Start checking classes at the given class index")
|
|
||||||
.hasArg()
|
|
||||||
.withArgName("CLASSINDEX")
|
|
||||||
.create("i");
|
|
||||||
|
|
||||||
options.addOption(versionOption);
|
|
||||||
options.addOption(helpOption);
|
|
||||||
options.addOption(deodexerantOption);
|
|
||||||
options.addOption(classPathOption);
|
|
||||||
options.addOption(classPathDirOption);
|
|
||||||
options.addOption(classStartOption);
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
# Copyright (C) 2008 The Android Open Source Project
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
# As per the Apache license requirements, this file has been modified
|
|
||||||
# from its original state.
|
|
||||||
#
|
|
||||||
# Such modifications are Copyright (C) 2010 Ben Gruver, and are released
|
|
||||||
# under the original license
|
|
||||||
|
|
||||||
LOCAL_PATH:= $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES:= \
|
|
||||||
Main.c
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := \
|
|
||||||
dalvik/include \
|
|
||||||
dalvik \
|
|
||||||
dalvik/libdex \
|
|
||||||
dalvik/vm \
|
|
||||||
$(JNI_H_INCLUDE)
|
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
|
||||||
libdvm \
|
|
||||||
libcutils
|
|
||||||
|
|
||||||
LOCAL_MODULE:= deodexerant
|
|
||||||
|
|
||||||
LOCAL_CFLAGS += -DANDROID_VER="$(PLATFORM_VERSION)"
|
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
|
@ -1,699 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* As per the Apache license requirements, this file has been modified
|
|
||||||
* from its original state.
|
|
||||||
*
|
|
||||||
* Such modifications are Copyright (C) 2010 Ben Gruver, and are released
|
|
||||||
* under the original license
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Command-line invocation of the Dalvik VM.
|
|
||||||
*/
|
|
||||||
#include "jni.h"
|
|
||||||
#include "Dalvik.h"
|
|
||||||
#include "libdex/OptInvocation.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
|
|
||||||
#include "utils/Log.h"
|
|
||||||
|
|
||||||
#define VERSION "1.0"
|
|
||||||
|
|
||||||
#define VER1_(x) #x
|
|
||||||
#define VER_(x) VER1_(x)
|
|
||||||
#define ANDROID_VERSION VER_(ANDROID_VER)
|
|
||||||
|
|
||||||
typedef struct InlineSub {
|
|
||||||
Method* method;
|
|
||||||
int inlineIdx;
|
|
||||||
} InlineSub;
|
|
||||||
|
|
||||||
static DexStringCache stringCache;
|
|
||||||
|
|
||||||
static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
|
|
||||||
|
|
||||||
//The following methods yanked from vm/analysis/CodeVerify.c
|
|
||||||
/*
|
|
||||||
* Compute the "class depth" of a class. This is the distance from the
|
|
||||||
* class to the top of the tree, chasing superclass links. java.lang.Object
|
|
||||||
* has a class depth of 0.
|
|
||||||
*/
|
|
||||||
static int getClassDepth(ClassObject* clazz)
|
|
||||||
{
|
|
||||||
int depth = 0;
|
|
||||||
|
|
||||||
while (clazz->super != NULL) {
|
|
||||||
clazz = clazz->super;
|
|
||||||
depth++;
|
|
||||||
}
|
|
||||||
return depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given two classes, walk up the superclass tree to find a common
|
|
||||||
* ancestor. (Called from findCommonSuperclass().)
|
|
||||||
*
|
|
||||||
* TODO: consider caching the class depth in the class object so we don't
|
|
||||||
* have to search for it here.
|
|
||||||
*/
|
|
||||||
static ClassObject* digForSuperclass(ClassObject* c1, ClassObject* c2)
|
|
||||||
{
|
|
||||||
int depth1, depth2;
|
|
||||||
|
|
||||||
depth1 = getClassDepth(c1);
|
|
||||||
depth2 = getClassDepth(c2);
|
|
||||||
|
|
||||||
/* pull the deepest one up */
|
|
||||||
if (depth1 > depth2) {
|
|
||||||
while (depth1 > depth2) {
|
|
||||||
c1 = c1->super;
|
|
||||||
depth1--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (depth2 > depth1) {
|
|
||||||
c2 = c2->super;
|
|
||||||
depth2--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* walk up in lock-step */
|
|
||||||
while (c1 != c2) {
|
|
||||||
c1 = c1->super;
|
|
||||||
c2 = c2->super;
|
|
||||||
|
|
||||||
assert(c1 != NULL && c2 != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return c1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Merge two array classes. We can't use the general "walk up to the
|
|
||||||
* superclass" merge because the superclass of an array is always Object.
|
|
||||||
* We want String[] + Integer[] = Object[]. This works for higher dimensions
|
|
||||||
* as well, e.g. String[][] + Integer[][] = Object[][].
|
|
||||||
*
|
|
||||||
* If Foo1 and Foo2 are subclasses of Foo, Foo1[] + Foo2[] = Foo[].
|
|
||||||
*
|
|
||||||
* If Class implements Type, Class[] + Type[] = Type[].
|
|
||||||
*
|
|
||||||
* If the dimensions don't match, we want to convert to an array of Object
|
|
||||||
* with the least dimension, e.g. String[][] + String[][][][] = Object[][].
|
|
||||||
*
|
|
||||||
* This gets a little awkward because we may have to ask the VM to create
|
|
||||||
* a new array type with the appropriate element and dimensions. However, we
|
|
||||||
* shouldn't be doing this often.
|
|
||||||
*/
|
|
||||||
static ClassObject* findCommonArraySuperclass(ClassObject* c1, ClassObject* c2)
|
|
||||||
{
|
|
||||||
ClassObject* arrayClass = NULL;
|
|
||||||
ClassObject* commonElem;
|
|
||||||
int i, numDims;
|
|
||||||
|
|
||||||
assert(c1->arrayDim > 0);
|
|
||||||
assert(c2->arrayDim > 0);
|
|
||||||
|
|
||||||
if (c1->arrayDim == c2->arrayDim) {
|
|
||||||
//commonElem = digForSuperclass(c1->elementClass, c2->elementClass);
|
|
||||||
commonElem = findCommonSuperclass(c1->elementClass, c2->elementClass);
|
|
||||||
numDims = c1->arrayDim;
|
|
||||||
} else {
|
|
||||||
if (c1->arrayDim < c2->arrayDim)
|
|
||||||
numDims = c1->arrayDim;
|
|
||||||
else
|
|
||||||
numDims = c2->arrayDim;
|
|
||||||
commonElem = c1->super; // == java.lang.Object
|
|
||||||
}
|
|
||||||
|
|
||||||
/* walk from the element to the (multi-)dimensioned array type */
|
|
||||||
for (i = 0; i < numDims; i++) {
|
|
||||||
arrayClass = dvmFindArrayClassForElement(commonElem);
|
|
||||||
commonElem = arrayClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arrayClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find the first common superclass of the two classes. We're not
|
|
||||||
* interested in common interfaces.
|
|
||||||
*
|
|
||||||
* The easiest way to do this for concrete classes is to compute the "class
|
|
||||||
* depth" of each, move up toward the root of the deepest one until they're
|
|
||||||
* at the same depth, then walk both up to the root until they match.
|
|
||||||
*
|
|
||||||
* If both classes are arrays of non-primitive types, we need to merge
|
|
||||||
* based on array depth and element type.
|
|
||||||
*
|
|
||||||
* If one class is an interface, we check to see if the other class/interface
|
|
||||||
* (or one of its predecessors) implements the interface. If so, we return
|
|
||||||
* the interface; otherwise, we return Object.
|
|
||||||
*
|
|
||||||
* NOTE: we continue the tradition of "lazy interface handling". To wit,
|
|
||||||
* suppose we have three classes:
|
|
||||||
* One implements Fancy, Free
|
|
||||||
* Two implements Fancy, Free
|
|
||||||
* Three implements Free
|
|
||||||
* where Fancy and Free are unrelated interfaces. The code requires us
|
|
||||||
* to merge One into Two. Ideally we'd use a common interface, which
|
|
||||||
* gives us a choice between Fancy and Free, and no guidance on which to
|
|
||||||
* use. If we use Free, we'll be okay when Three gets merged in, but if
|
|
||||||
* we choose Fancy, we're hosed. The "ideal" solution is to create a
|
|
||||||
* set of common interfaces and carry that around, merging further references
|
|
||||||
* into it. This is a pain. The easy solution is to simply boil them
|
|
||||||
* down to Objects and let the runtime invokeinterface call fail, which
|
|
||||||
* is what we do.
|
|
||||||
*/
|
|
||||||
static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2)
|
|
||||||
{
|
|
||||||
assert(!dvmIsPrimitiveClass(c1) && !dvmIsPrimitiveClass(c2));
|
|
||||||
|
|
||||||
if (c1 == c2)
|
|
||||||
return c1;
|
|
||||||
|
|
||||||
if (dvmIsInterfaceClass(c1) && dvmImplements(c2, c1)) {
|
|
||||||
return c1;
|
|
||||||
}
|
|
||||||
if (dvmIsInterfaceClass(c2) && dvmImplements(c1, c2)) {
|
|
||||||
return c2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dvmIsArrayClass(c1) && dvmIsArrayClass(c2) &&
|
|
||||||
!dvmIsPrimitiveClass(c1->elementClass) &&
|
|
||||||
!dvmIsPrimitiveClass(c2->elementClass))
|
|
||||||
{
|
|
||||||
return findCommonArraySuperclass(c1, c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return digForSuperclass(c1, c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//method yanked from vm/analysis/DexOptimize.c
|
|
||||||
/*
|
|
||||||
* Try to load all classes in the specified DEX. If they have some sort
|
|
||||||
* of broken dependency, e.g. their superclass lives in a different DEX
|
|
||||||
* that wasn't previously loaded into the bootstrap class path, loading
|
|
||||||
* will fail. This is the desired behavior.
|
|
||||||
*
|
|
||||||
* We have no notion of class loader at this point, so we load all of
|
|
||||||
* the classes with the bootstrap class loader. It turns out this has
|
|
||||||
* exactly the behavior we want, and has no ill side effects because we're
|
|
||||||
* running in a separate process and anything we load here will be forgotten.
|
|
||||||
*
|
|
||||||
* We set the CLASS_MULTIPLE_DEFS flag here if we see multiple definitions.
|
|
||||||
* This works because we only call here as part of optimization / pre-verify,
|
|
||||||
* not during verification as part of loading a class into a running VM.
|
|
||||||
*
|
|
||||||
* This returns "false" if the world is too screwed up to do anything
|
|
||||||
* useful at all.
|
|
||||||
*/
|
|
||||||
int loadAllClasses(DvmDex* pDvmDex)
|
|
||||||
{
|
|
||||||
u4 count = pDvmDex->pDexFile->pHeader->classDefsSize;
|
|
||||||
u4 idx;
|
|
||||||
int loaded = 0;
|
|
||||||
|
|
||||||
dvmSetBootPathExtraDex(pDvmDex);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We have some circularity issues with Class and Object that are most
|
|
||||||
* easily avoided by ensuring that Object is never the first thing we
|
|
||||||
* try to find. Take care of that here. (We only need to do this when
|
|
||||||
* loading classes from the DEX file that contains Object, and only
|
|
||||||
* when Object comes first in the list, but it costs very little to
|
|
||||||
* do it in all cases.)
|
|
||||||
*/
|
|
||||||
if (dvmFindSystemClass("Ljava/lang/Class;") == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (idx = 0; idx < count; idx++) {
|
|
||||||
const DexClassDef* pClassDef;
|
|
||||||
const char* classDescriptor;
|
|
||||||
ClassObject* newClass;
|
|
||||||
|
|
||||||
pClassDef = dexGetClassDef(pDvmDex->pDexFile, idx);
|
|
||||||
classDescriptor =
|
|
||||||
dexStringByTypeIdx(pDvmDex->pDexFile, pClassDef->classIdx);
|
|
||||||
|
|
||||||
//newClass = dvmDefineClass(pDexFile, classDescriptor,
|
|
||||||
// NULL);
|
|
||||||
newClass = dvmFindSystemClassNoInit(classDescriptor);
|
|
||||||
if (newClass == NULL) {
|
|
||||||
dvmClearOptException(dvmThreadSelf());
|
|
||||||
} else if (newClass->pDvmDex != pDvmDex) {
|
|
||||||
/*
|
|
||||||
* We don't load the new one, and we tag the first one found
|
|
||||||
* with the "multiple def" flag so the resolver doesn't try
|
|
||||||
* to make it available.
|
|
||||||
*/
|
|
||||||
SET_CLASS_FLAG(newClass, CLASS_MULTIPLE_DEFS);
|
|
||||||
} else {
|
|
||||||
loaded++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dvmSetBootPathExtraDex(NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dumpFields(char *classType, FILE *clientOut)
|
|
||||||
{
|
|
||||||
ClassObject *clazz;
|
|
||||||
if (classType[0] == '[')
|
|
||||||
clazz = dvmFindArrayClass(classType, NULL);
|
|
||||||
else
|
|
||||||
clazz = dvmFindSystemClassNoInit(classType);
|
|
||||||
|
|
||||||
if (clazz == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
InstField *pField = clazz->ifields;
|
|
||||||
for (i=0; i<clazz->ifieldCount; i++, pField++)
|
|
||||||
fprintf(clientOut, "field: %d %s:%s\n", pField->byteOffset, pField->field.name, pField->field.signature);
|
|
||||||
|
|
||||||
clazz = clazz->super;
|
|
||||||
} while (clazz != NULL);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dumpInlineMethods(FILE *clientOut)
|
|
||||||
{
|
|
||||||
const InlineOperation *inlineTable = dvmGetInlineOpsTable();
|
|
||||||
int count = dvmGetInlineOpsTableLength();
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i=0; i<count; i++) {
|
|
||||||
const InlineOperation *inlineOp = &inlineTable[i];
|
|
||||||
|
|
||||||
ClassObject *clazz = dvmFindSystemClassNoInit(inlineOp->classDescriptor);
|
|
||||||
if (clazz == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
char *methodType;
|
|
||||||
Method *method = dvmFindDirectMethodByDescriptor(clazz, inlineOp->methodName, inlineOp->methodSignature);
|
|
||||||
if (method == NULL)
|
|
||||||
{
|
|
||||||
method = dvmFindVirtualMethodByDescriptor(clazz, inlineOp->methodName, inlineOp->methodSignature);
|
|
||||||
methodType = "virtual";
|
|
||||||
} else {
|
|
||||||
if (dvmIsStaticMethod(method))
|
|
||||||
methodType = "static";
|
|
||||||
else
|
|
||||||
methodType = "direct";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fprintf(clientOut, "inline: %s %s->%s%s\n", methodType, method->clazz->descriptor, method->name, dexProtoGetMethodDescriptor(&method->prototype, &stringCache));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dumpVirtualMethods(char *classType, FILE *clientOut)
|
|
||||||
{
|
|
||||||
ClassObject *clazz;
|
|
||||||
if (classType[0] == '[')
|
|
||||||
clazz = dvmFindArrayClass(classType, NULL);
|
|
||||||
else
|
|
||||||
clazz = dvmFindSystemClassNoInit(classType);
|
|
||||||
|
|
||||||
|
|
||||||
if (clazz == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: could not find class %s\n", classType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//interface classes don't have virtual methods, by definition. But it's possible
|
|
||||||
//to call virtual methods defined on the Object class via an interface type
|
|
||||||
if (dvmIsInterfaceClass(clazz))
|
|
||||||
{
|
|
||||||
clazz = dvmFindSystemClassNoInit("Ljava/lang/Object;");
|
|
||||||
if (clazz == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: could not find class %s\n", classType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i=0; i<clazz->vtableCount; i++)
|
|
||||||
{
|
|
||||||
Method *method = clazz->vtable[i];
|
|
||||||
fprintf(clientOut, "vtable: %s%s\n", method->name,
|
|
||||||
dexProtoGetMethodDescriptor(&method->prototype, &stringCache));
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassObject *lookupSuperclass(char *classType)
|
|
||||||
{
|
|
||||||
ClassObject *clazz = dvmFindSystemClassNoInit(classType);
|
|
||||||
|
|
||||||
if (clazz == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return clazz->super;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse arguments. Most of it just gets passed through to the VM. The
|
|
||||||
* JNI spec defines a handful of standard arguments.
|
|
||||||
*/
|
|
||||||
int main(int argc, char* const argv[])
|
|
||||||
{
|
|
||||||
const char* inputFileName;
|
|
||||||
JavaVM* vm = NULL;
|
|
||||||
JNIEnv* env = NULL;
|
|
||||||
DvmDex* pDvmDex = NULL;
|
|
||||||
DexClassLookup* pClassLookup;
|
|
||||||
|
|
||||||
if (argc != 3) {
|
|
||||||
fprintf(stderr, "deodexerant %s (Android %s)\n", VERSION, ANDROID_VERSION);
|
|
||||||
fprintf(stderr, "usage: deodexerant <odex_file> <port>\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputFileName = argv[1];
|
|
||||||
|
|
||||||
struct stat inputInfo;
|
|
||||||
|
|
||||||
if (stat(inputFileName, &inputInfo) != 0) {
|
|
||||||
fprintf(stderr, "could not stat '%s' : %s\n", inputFileName, strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int odexFd = open(inputFileName, O_RDONLY);
|
|
||||||
if (odexFd < 0) {
|
|
||||||
fprintf(stderr, "Unable to open '%s': %s\n", inputFileName, strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int port = atoi(argv[2]);
|
|
||||||
int socketFd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (socketFd < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to open socket\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in serverAddress, clientAddress;
|
|
||||||
bzero((char *)&serverAddress, sizeof(serverAddress));
|
|
||||||
serverAddress.sin_family = AF_INET;
|
|
||||||
serverAddress.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
serverAddress.sin_port = htons(port);
|
|
||||||
if (bind(socketFd, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to bind socket\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* bcp = getenv("BOOTCLASSPATH");
|
|
||||||
if (bcp == NULL) {
|
|
||||||
fprintf(stderr, "BOOTCLASSPATH not set\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DexClassVerifyMode verifyMode = VERIFY_MODE_ALL;
|
|
||||||
DexOptimizerMode dexOptMode = OPTIMIZE_MODE_VERIFIED;
|
|
||||||
|
|
||||||
if (dvmPrepForDexOpt(bcp, dexOptMode, verifyMode,
|
|
||||||
0) != 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "VM init failed\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Map the entire file (so we don't have to worry about page
|
|
||||||
* alignment). The expectation is that the output file contains
|
|
||||||
* our DEX data plus room for a small header.
|
|
||||||
*/
|
|
||||||
bool success;
|
|
||||||
void* mapAddr;
|
|
||||||
mapAddr = mmap(NULL, inputInfo.st_size, PROT_READ,
|
|
||||||
MAP_PRIVATE, odexFd, 0);
|
|
||||||
if (mapAddr == MAP_FAILED) {
|
|
||||||
fprintf(stderr, "unable to mmap DEX cache: %s\n", strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dvmDexFileOpenPartial(mapAddr + *((int *)(mapAddr+8)), *((int *)(mapAddr+12)), &pDvmDex) != 0) {
|
|
||||||
fprintf(stderr, "Unable to create DexFile\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pClassLookup = dexCreateClassLookup(pDvmDex->pDexFile);
|
|
||||||
if (pClassLookup == NULL)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "unable to create class lookup\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDvmDex->pDexFile->pClassLookup = pClassLookup;
|
|
||||||
|
|
||||||
if (!loadAllClasses(pDvmDex))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error while loading classes\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
listen(socketFd, 1);
|
|
||||||
|
|
||||||
int clientSocketLength = sizeof(clientAddress);
|
|
||||||
int clientFd = accept(socketFd, (struct sockaddr *) &clientAddress, &clientSocketLength);
|
|
||||||
if (clientFd < 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to accept incomming connection\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *clientIn = fdopen(clientFd, "r");
|
|
||||||
if (clientIn == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to fdopen socket to get input stream\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *clientOut = fdopen(dup(clientFd), "w");
|
|
||||||
if (clientOut == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Unable to fdopen socket to get output stream\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *command = NULL;
|
|
||||||
unsigned int len = 0;
|
|
||||||
dexStringCacheInit(&stringCache);
|
|
||||||
|
|
||||||
while ((command = fgetln(clientIn, &len)) != NULL) {
|
|
||||||
while (len > 0 && (command[len-1] == '\r' || command[len-1] == '\n'))
|
|
||||||
len--;
|
|
||||||
char *buf = malloc(len+1);
|
|
||||||
memcpy(buf, command, len);
|
|
||||||
buf[len] = 0;
|
|
||||||
|
|
||||||
//printf("%s\n", buf);
|
|
||||||
|
|
||||||
char *cmd = strtok(buf, " ");
|
|
||||||
if (cmd == NULL) {
|
|
||||||
fprintf(clientOut, "err: error interpreting command\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (cmd[0])
|
|
||||||
{
|
|
||||||
case 'F' :
|
|
||||||
{
|
|
||||||
char *classType = strtok(NULL, " ");
|
|
||||||
if (classType == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: no classType for field lookup\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dumpFields(classType, clientOut))
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: error while dumping fields\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(clientOut, "done\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'I':
|
|
||||||
{
|
|
||||||
if (!dumpInlineMethods(clientOut))
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: inline method not found\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(clientOut, "done\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'V':
|
|
||||||
{
|
|
||||||
char *classType = strtok(NULL, " ");
|
|
||||||
if (classType == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: no classType for vtable dump\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dumpVirtualMethods(classType, clientOut)) {
|
|
||||||
fprintf(clientOut, "err: error encountered while dumping virtual methods\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(clientOut, "done\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'P':
|
|
||||||
{
|
|
||||||
char *classType = strtok(NULL, " ");
|
|
||||||
if (classType == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: no classType for superclass lookup\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassObject *clazz = lookupSuperclass(classType);
|
|
||||||
if (clazz == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "class: \n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(clientOut, "class: %s\n", clazz->descriptor);
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'C':
|
|
||||||
{
|
|
||||||
char *classType1 = strtok(NULL, " ");
|
|
||||||
if (classType1 == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: no classType for common superclass lookup\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassObject *clazz1;
|
|
||||||
if (classType1[0] == '[')
|
|
||||||
clazz1 = dvmFindArrayClass(classType1, NULL);
|
|
||||||
else
|
|
||||||
clazz1 = dvmFindSystemClassNoInit(classType1);
|
|
||||||
|
|
||||||
if (clazz1 == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: class %s could not be found for common superclass lookup. This can be caused if a library the odex depends on is not in the BOOTCLASSPATH environment variable\n", classType1);
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *classType2 = strtok(NULL, " ");
|
|
||||||
if (classType2 == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: no classType for common superclass lookup\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassObject *clazz2;
|
|
||||||
if (classType2[0] == '[')
|
|
||||||
clazz2 = dvmFindArrayClass(classType2, NULL);
|
|
||||||
else
|
|
||||||
clazz2 = dvmFindSystemClassNoInit(classType2);
|
|
||||||
|
|
||||||
if (clazz2 == NULL)
|
|
||||||
{
|
|
||||||
fprintf(clientOut, "err: class %s could not be found for common superclass lookup. This can be caused if a library the odex depends on is not in the BOOTCLASSPATH environment variable\n", classType2);
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassObject *clazz = findCommonSuperclass(clazz1, clazz2);
|
|
||||||
fprintf(clientOut, "class: %s\n", clazz->descriptor);
|
|
||||||
fflush(clientOut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fprintf(clientOut, "err: not a valid command\n");
|
|
||||||
fflush(clientOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*gettimeofday(&tv, NULL);
|
|
||||||
|
|
||||||
printf("end %07d\n", tv.tv_usec);*/
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
usage:
|
|
||||||
deodexerant <odex_file> <port>
|
|
||||||
|
|
||||||
deodexerant is a binary that is intended to run on an android phone, in order
|
|
||||||
to provide assistance to baksmali in deodexing .odex files. It communicates
|
|
||||||
over TCP and implements a simplistic protocol for looking up various
|
|
||||||
information needed during the deodex process, which can only be provided by
|
|
||||||
the dalvik vm. I.E. vtable lookups, field lookups by byte offset, superclass
|
|
||||||
lookups for classes not defined in the .odex file being processed, etc.
|
|
||||||
|
|
||||||
deodexerant is intended to be build within the AOSP build system. Assuming
|
|
||||||
you have $MYDROID set to the root of the AOSP source tree, and $SMALI
|
|
||||||
set to the root of the smali source tree,
|
|
||||||
|
|
||||||
1. cp -r $SMALI/deodexerant $MYDROID/dalvik/deodexerant
|
|
||||||
2. cd $MYDROID/dalvik/deodexerant
|
|
||||||
3. source ../../build/envsetup.sh
|
|
||||||
4. mm
|
|
||||||
|
|
||||||
It should should spit out a deodexerant binary at
|
|
||||||
|
|
||||||
$MYDROID/out/target/product/common/system/bin/deodexerant
|
|
||||||
|
|
||||||
Or wherever your current build setup is configured to stick those types of
|
|
||||||
things
|
|
||||||
|
|
@ -1212,79 +1212,4 @@ public class ClassPath {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void validateAgainstDeodexerant(String host, int port, int skipClasses) {
|
|
||||||
Deodexerant deodexerant = new Deodexerant(host, port);
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String[] inlineMethods = deodexerant.getInlineMethods();
|
|
||||||
(new DeodexUtil(null)).checkInlineMethods(inlineMethods);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw ExceptionWithContext.withContext(ex, "Error while checking inline methods");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (ClassDef classDef: theClassPath.classDefs.values()) {
|
|
||||||
if (count < skipClasses) {
|
|
||||||
count++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((count%1000)==0) {
|
|
||||||
System.out.println(count);
|
|
||||||
}
|
|
||||||
if (classDef instanceof UnresolvedClassDef || classDef instanceof ArrayClassDef ||
|
|
||||||
classDef instanceof PrimitiveClassDef) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] vtable = deodexerant.getVirtualMethods(classDef.classType);
|
|
||||||
|
|
||||||
if (vtable.length != classDef.vtable.length) {
|
|
||||||
throw new ValidationException(String.format("virtual table size mismatch for class %s",
|
|
||||||
classDef.classType));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<classDef.vtable.length; i++) {
|
|
||||||
if (!classDef.vtable[i].equals(vtable[i])) {
|
|
||||||
throw new ValidationException(String.format("virtual method mismatch for class %s at index %d",
|
|
||||||
classDef.classType, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] fields = deodexerant.getInstanceFields(classDef.classType);
|
|
||||||
|
|
||||||
if (fields.length != classDef.instanceFields.size()) {
|
|
||||||
throw new ValidationException(String.format("field count mismatch for class %s",
|
|
||||||
classDef.classType));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<classDef.instanceFields.size(); i++) {
|
|
||||||
String[] fieldValues = fields[i].split(" ");
|
|
||||||
if (fieldValues.length != 2) {
|
|
||||||
throw new ValidationException("Could not parse field");
|
|
||||||
}
|
|
||||||
|
|
||||||
int fieldOffset = Integer.parseInt(fieldValues[0]);
|
|
||||||
|
|
||||||
String field = classDef.instanceFields.get(fieldOffset);
|
|
||||||
if (field == null) {
|
|
||||||
throw new ValidationException("Could not find a field at the given offset");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!field.equals(fieldValues[1])) {
|
|
||||||
throw new ValidationException(String.format("field offset mismatch for class %s at index %d",
|
|
||||||
classDef.classType, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw ExceptionWithContext.withContext(ex, String.format("Error while checking class #%d", count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -288,49 +288,6 @@ public class DeodexUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare the inline methods that we have against the given set of inline methods from deodexerant.
|
|
||||||
* We want to make sure that each inline method in inlineMethods matches the method we have at the same
|
|
||||||
* index. We may have more inline methods than we are given in inlineMethods - this shouldn't be a problem.
|
|
||||||
* Newer versions of dalvik add additional inline methods, but (so far) have changed any existing ones.
|
|
||||||
*
|
|
||||||
* If anything doesn't look right, we just throw an exception
|
|
||||||
* @param inlineMethods the inline methods from deodexerant
|
|
||||||
*/
|
|
||||||
protected void checkInlineMethods(String[] inlineMethods) {
|
|
||||||
if (inlineMethods.length > this.inlineMethods.length) {
|
|
||||||
throw new ValidationException("Inline method count mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<inlineMethods.length; i++) {
|
|
||||||
String inlineMethod = inlineMethods[i];
|
|
||||||
int methodType;
|
|
||||||
|
|
||||||
if (inlineMethod.startsWith("static")) {
|
|
||||||
methodType = Static;
|
|
||||||
inlineMethod = inlineMethod.substring(7);
|
|
||||||
} else if (inlineMethod.startsWith("direct")) {
|
|
||||||
methodType = Direct;
|
|
||||||
inlineMethod = inlineMethod.substring(7);
|
|
||||||
} else if (inlineMethod.startsWith("virtual")) {
|
|
||||||
methodType = Virtual;
|
|
||||||
inlineMethod = inlineMethod.substring(8);
|
|
||||||
} else {
|
|
||||||
throw new ValidationException("Could not parse inline method");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inlineMethod.equals(this.inlineMethods[i].getMethodString())) {
|
|
||||||
throw new ValidationException(String.format("Inline method mismatch. %s vs. %s", inlineMethod,
|
|
||||||
this.inlineMethods[i].getMethodString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodType != this.inlineMethods[i].methodType) {
|
|
||||||
throw new ValidationException(String.format("Inline method type mismatch. %d vs. %d", methodType,
|
|
||||||
this.inlineMethods[i].methodType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InlineMethod {
|
public class InlineMethod {
|
||||||
public final int methodType;
|
public final int methodType;
|
||||||
public final String classType;
|
public final String classType;
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* [The "BSD licence"]
|
|
||||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. 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.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.dexlib.Code.Analysis;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class handles communication with the deodexerant helper binary,
|
|
||||||
* as well as caching the results of any deodexerant lookups
|
|
||||||
*/
|
|
||||||
public class Deodexerant {
|
|
||||||
private final String host;
|
|
||||||
private final int port;
|
|
||||||
|
|
||||||
private Socket socket = null;
|
|
||||||
private PrintWriter out = null;
|
|
||||||
private BufferedReader in = null;
|
|
||||||
|
|
||||||
public Deodexerant(String host, int port) {
|
|
||||||
this.host = host;
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getInlineMethods() {
|
|
||||||
return sendMultilineCommand("I");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getVirtualMethods(String classType) {
|
|
||||||
return sendMultilineCommand(String.format("V %s", classType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getInstanceFields(String classType) {
|
|
||||||
return sendMultilineCommand(String.format("F %s", classType));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String sendCommand(String cmd) {
|
|
||||||
try {
|
|
||||||
connectIfNeeded();
|
|
||||||
|
|
||||||
out.println(cmd);
|
|
||||||
out.flush();
|
|
||||||
String response = in.readLine();
|
|
||||||
if (response.startsWith("err")) {
|
|
||||||
String error = response.substring(5);
|
|
||||||
throw new RuntimeException(error);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//The command is still just a single line, but we're expecting a multi-line
|
|
||||||
//response. The repsonse is considered finished when a line starting with "err"
|
|
||||||
//or with "done" is encountered
|
|
||||||
private String[] sendMultilineCommand(String cmd) {
|
|
||||||
try {
|
|
||||||
connectIfNeeded();
|
|
||||||
|
|
||||||
out.println(cmd);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
ArrayList<String> responseLines = new ArrayList<String>();
|
|
||||||
String response = in.readLine();
|
|
||||||
if (response == null) {
|
|
||||||
throw new RuntimeException("Error talking to deodexerant");
|
|
||||||
}
|
|
||||||
while (!response.startsWith("done"))
|
|
||||||
{
|
|
||||||
if (response.startsWith("err")) {
|
|
||||||
throw new RuntimeException(response.substring(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
int pos = response.indexOf(':') + 1;
|
|
||||||
|
|
||||||
responseLines.add(response.substring(pos+1));
|
|
||||||
response = in.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] lines = new String[responseLines.size()];
|
|
||||||
|
|
||||||
for (int i=0; i<lines.length; i++) {
|
|
||||||
lines[i] = responseLines.get(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void connectIfNeeded() {
|
|
||||||
try {
|
|
||||||
if (socket != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket = new Socket(host, port);
|
|
||||||
|
|
||||||
out = new PrintWriter(socket.getOutputStream(), true);
|
|
||||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user