From 571bb071fe83e5cdd2f5dd3b4a3f649dacb3f2a8 Mon Sep 17 00:00:00 2001 From: hoenicke Date: Mon, 2 Oct 2000 13:04:09 +0000 Subject: [PATCH] Initial release git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1269 379699f6-c40d-0410-875b-85095c16579e --- jode/jode/decompiler/OptionNames.properties | 96 +++++++++++++ .../obfuscator/modules/IdentityRenamer.java | 50 +++++++ jode/scripts/createStackDelta.pl | 134 ++++++++++++++++++ jode/test/innerclasses/CheckPrivate.java | 40 ++++++ jode/test/innerclasses/CheckSuperRemove.java | 28 ++++ 5 files changed, 348 insertions(+) create mode 100644 jode/jode/decompiler/OptionNames.properties create mode 100644 jode/jode/obfuscator/modules/IdentityRenamer.java create mode 100644 jode/scripts/createStackDelta.pl create mode 100644 jode/test/innerclasses/CheckPrivate.java create mode 100644 jode/test/innerclasses/CheckSuperRemove.java diff --git a/jode/jode/decompiler/OptionNames.properties b/jode/jode/decompiler/OptionNames.properties new file mode 100644 index 0000000..8b9e7d6 --- /dev/null +++ b/jode/jode/decompiler/OptionNames.properties @@ -0,0 +1,96 @@ +options = tabwidth,indent,style,linewidth,import,verbose,lvt,inner,anonymous,push,pretty,decrypt,onetime,immediate,verify,contrafo,debug + +tabwidth.0= +tabwidth.1=Set tab width to n. +tabwidth.2=This means that Jode uses tabs to replace n spaces. \ +Don't confound this with the indent option. Use 0 if you don't want \ +tabs. Default is 8. + +indent.0= +indent.1=Indent blocks by n spaces. + +style.0={sun|gnu} +style.1=Specify indentation style. + +linewidth.0= +linewidth.1=Set maximum line width to n. +linewidth.2=Jode breaks lines that are longer than this. It tries it's best \ +to make all lines fit in this limit, but sometimes this won't succeed. + +import.0=, +import.1=import classes if they occur more than clslimit times and packages \ +with more than pkglimit used classes. Default is 0,1 which means that all \ +used classes are imported, but never a whole package. + +verbose.0= +verbose.1=Be verbose (higher n means more verbose). +verbose.2=This prints some information about the currently decompiled \ +class or method to the console. + +debug.0=,... +debug.1=Enable debugging options. Useful to track errors in the decompiler. +debug.2=Possible flags are: \ +"bytecode", to print raw bytecode. \ +"lvt", dump LocalVariableTable. \ +"verifier", to trace bytecode verification. \ +"check", do time consuming sanity checks; useful to spot serious errors. \ +"types", to see the type intersections. \ +"flow", for a very verbose trace of the decompile process. \ +"analyze", briefly inform about "T1/T2" analyzation. \ +"inout", to view the in/out local variable analyzation. \ +"locals", to see how local variable merging is done. \ +"constructors", to trace constructor transformation. \ +"interpreter", to follow the execution of the interpreter \ +(the interpreter is used for string decryption). + +inner.0={yes|no} +inner.1=(Don't) decompiler inner classes. + +anonymous.0={yes|no} +anonymous.1=(Don't) decompiler method scoped classes. + +contrafo.0={yes|no} +contrafo.1=(Don't) transform constructors. + +lvt.0={yes|no} +lvt.1=(Don't) use the local variable table. +lvt.2=Turning it off is useful if an obfuscator filled it with bogus values. + +pretty.0={yes|no} +pretty.1=(Don't) use `pretty' names for local variables. +pretty.2=The non pretty names have the advantage, that their names are \ +unique. This make search & replace possible. + +push.0={yes|no} +push.1=Allow PUSH pseudo instructions in output. +push.2=Sometimes, when methods were inlined, Jode can't reconstruct \ +the original expression. It has to split a complex expression into \ +several ones, using temporary variables. If this option is on, it won't \ +use the temporary variables, but uses pseudo PUSH/POP instructions instead, \ +as they are in the bytecode. + +decrypt.0={yes|no} +decrypt.1=(Don't) decrypt encrypted strings. +decrypt.2=Some obfuscators encrypt all strings. To decrypt them at runtime \ +they add a small decryption routine to the code. If Jode detects such a \ +decryption routine it interprets it to decrypt the strings at decompile time. + +onetime.0={yes|no} +onetime.1=(Don't) remove locals that written and then immediately read. +onetime.2=When javac inlines a method it uses temporary local variables for \ +the parameters. Often these local variables can be removed, which makes \ +the code much better to read. + +immediate.0={yes|no} +immediate.1=Output the source immediately as it gets decompiled. +immediate.2=This leads to more instant output, but has many disadvantages.\ +For one the import statements can't be correct. But it also may lead to \ +buggy code. The advantage is, that you can get partial output even if an +exception is thrown. + +verify.0={yes|no} +verify.1=(Don't) verify code before decompiling it. +verify.2=Jode assumes at many places that your byte code is legal. To \ +be sure it verifies it before decompiling. If verification fails it \ +rejects the code. Since verification can fail for legal code if the \ +type hierarchy is not known, you can turn this option off. diff --git a/jode/jode/obfuscator/modules/IdentityRenamer.java b/jode/jode/obfuscator/modules/IdentityRenamer.java new file mode 100644 index 0000000..dd1b4ac --- /dev/null +++ b/jode/jode/obfuscator/modules/IdentityRenamer.java @@ -0,0 +1,50 @@ +/* IdentityRenamer Copyright (C) 1999 Jochen Hoenicke. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.obfuscator.modules; +import jode.obfuscator.Renamer; +import jode.obfuscator.Identifier; +///#def COLLECTIONS java.util +import java.util.Iterator; +///#enddef +///#def COLLECTIONEXTRA java.lang +import java.lang.UnsupportedOperationException; +///#enddef + +public class IdentityRenamer implements Renamer { + public Iterator generateNames(Identifier ident) { + final String base = ident.getName(); + return new Iterator() { + int last = 0; + + public boolean hasNext() { + return true; + } + + public Object next() { + return (last++ == 0 ? base : base + last); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} + diff --git a/jode/scripts/createStackDelta.pl b/jode/scripts/createStackDelta.pl new file mode 100644 index 0000000..6fe0386 --- /dev/null +++ b/jode/scripts/createStackDelta.pl @@ -0,0 +1,134 @@ +#!/usr/bin/perl +# createStackDelta.pl Copyright (C) 1999 Jochen Hoenicke. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +# +# $Id$ + +# This perl script creates the stackDelta string needed by +# jode.bytecode.Instruction. + +my $OPCODEFILE="jode/bytecode/Opcodes.java"; +my @delta; +my $lastOpcode = 0; +my $nr; + +open OPCODES, "<$OPCODEFILE"; +while () { + next unless /opc_([a-z0-9_]+)\s*=\s*(\d+)/; + + $_ = $1; + $nr = $2; + if (/^(nop|iinc)$/) { + # no pop, no push + $delta[$nr] = "000"; + } elsif (/^([aif](const|load).*|[sb]ipush|ldc(_w)?)$/) { + # no pop, one push + $delta[$nr] = "010"; + } elsif (/^([ld](const|load).*|ldc2_w)$/) { + # no pop, two push + $delta[$nr] = "020"; + } elsif (/^([aif]store.*|pop)$/) { + # one pop, no push + $delta[$nr] = "001"; + } elsif (/^([ld]store.*|pop2)$/) { + # two pop, no push + $delta[$nr] = "002"; + } elsif (/^[aifbcs]aload$/) { + # two pop, one push + $delta[$nr] = "012"; + } elsif (/^[dl]aload$/) { + # two pop, two push + $delta[$nr] = "022"; + } elsif (/^[aifbcs]astore$/) { + # three pop, no push + $delta[$nr] = "003"; + } elsif (/^[dl]astore$/) { + # four pop, no push + $delta[$nr] = "004"; + } elsif (/^dup(2)?(_x([12]))?$/) { + $count = $1 ? 2 : 1; + $depth = $2 ? $3 : 0; + $pop = $count + $depth; + $push = $pop + $count; + $delta[$nr] = "0".$push.$pop; + } elsif (/^swap$/) { + # two pop, two push + $delta[$nr] = "022"; + } elsif (/^[if](add|sub|mul|div|rem|u?sh[lr]|and|or|xor)$/) { + # two pop, one push + $delta[$nr] = "012"; + } elsif (/^[ld](add|sub|mul|div|rem|and|or|xor)$/) { + # four pop, two push + $delta[$nr] = "024"; + } elsif (/^[if]neg$/) { + # one pop, one push + $delta[$nr] = "011"; + } elsif (/^[ld]neg$/) { + # two pop, two push + $delta[$nr] = "022"; + } elsif (/^lu?sh[lr]$/) { + # 3 pop, two push + $delta[$nr] = "023"; + } elsif (/^[if]2[ifbcs]$/) { + # one pop, one push + $delta[$nr] = "011"; + } elsif (/^[if]2[ld]$/) { + # one pop, two push + $delta[$nr] = "021"; + } elsif (/^[ld]2[if]$/) { + # two pop, one push + $delta[$nr] = "012"; + } elsif (/^[ld]2[ld]$/) { + # two pop, two push + $delta[$nr] = "022"; + } elsif (/^fcmp[lg]$/) { + $delta[$nr] = "012"; + } elsif (/^[ld]cmp[lg]?$/) { + $delta[$nr] = "014"; + } elsif (/^if_[ia]cmp(eq|ne|lt|ge|le|gt)$/) { + $delta[$nr] = "002"; + } elsif (/^(if(eq|ne|lt|ge|le|gt|(non)?null)|tableswitch|lookupswitch)$/) { + # order does matter + $delta[$nr] = "001"; + } elsif (/^(goto(_w)?|jsr(_w)?|ret|return)$/) { + $delta[$nr] = "000"; + } elsif (/^([ifa]return)$/) { + $delta[$nr] = "001"; + } elsif (/^([ld]return)$/) { + $delta[$nr] = "002"; + } elsif (/^(new)$/) { + $delta[$nr] = "010"; + } elsif (/^(multianewarray|(get|put|invoke).*)$/) { + # unknown + $delta[$nr] = "100"; + } elsif (/^(athrow|monitor(enter|exit))$/) { + $delta[$nr] = "001"; + } elsif (/^(a?newarray|arraylength|checkcast|instanceof)$/) { + $delta[$nr] = "011"; + } else { + # illegal + next; + } + $lastOpcode = $nr + if ($nr > $lastOpcode); +} + +print " private final static String stackDelta = \n\t\""; +for ($nr = 0; $nr <= $lastOpcode; $nr ++) { + defined $delta[$nr] or $delta[$nr] = "177"; + print "\\$delta[$nr]"; +} +print "\";\n"; diff --git a/jode/test/innerclasses/CheckPrivate.java b/jode/test/innerclasses/CheckPrivate.java new file mode 100644 index 0000000..93fa895 --- /dev/null +++ b/jode/test/innerclasses/CheckPrivate.java @@ -0,0 +1,40 @@ + + +public class CheckPrivate { + private class InnerClass { + private int field; + + private InnerClass(int j) { + field = j; + } + + private int method(int a) { + int old = field; + field = a; + return old; + } + + private void test() { + outerField = 4; + outerMethod(); + System.err.println(outerField); + new CheckPrivate(); + } + } + + private int outerField; + + private int outerMethod() { + return outerField; + } + + private CheckPrivate() { + InnerClass inner = new InnerClass(1); + inner.field = 3; + inner.method(inner.field); + } + + public static void main(String[] test) { + new CheckPrivate(); + } +} diff --git a/jode/test/innerclasses/CheckSuperRemove.java b/jode/test/innerclasses/CheckSuperRemove.java new file mode 100644 index 0000000..8c00b2b --- /dev/null +++ b/jode/test/innerclasses/CheckSuperRemove.java @@ -0,0 +1,28 @@ +public class CheckSuperRemove { + public class Inner { + public void test() { + class MyInner extends Inner { + } + MyInner myInner = new MyInner(); + Inner anonInner = new Inner() { + public void test() {} + }; + MyInner anonInner2 = new MyInner() { + public void test() {} + }; + } + } + + public class SubInner extends Inner { + public SubInner(int a) { + } + + public SubInner() { + } + } + + public static void main(String[] param) { + new CheckSuperRemove().new SubInner(); + new CheckSuperRemove().new SubInner(1); + } +}