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:
JesusFreke@JesusFreke.com 2010-12-21 17:33:20 +00:00
parent b3ba356d94
commit b9a19bf3f1
7 changed files with 0 additions and 1211 deletions

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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));
}
}
} }

View File

@ -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;

View File

@ -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);
}
}
}