001: /**
002: * YGuard -- an obfuscation library for Java(TM) classfiles.
003: *
004: * Original Copyright (c) 1999 Mark Welsh (markw@retrologic.com)
005: * Modifications Copyright (c) 2002 yWorks GmbH (yguard@yworks.com)
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: *
021: * The author may be contacted at yguard@yworks.com
022: *
023: * Java and all Java-based marks are trademarks or registered
024: * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
025: */package com.yworks.yguard.obf;
026:
027: import java.io.*;
028: import java.util.*;
029:
030: /**
031: * Name generator that uses (almost) the full Java identifier namespace,
032: * and chooses to put some of the keyword names (legal in JVM, illegal in
033: * Java language) out front in sequence.
034: *
035: * @author Mark Welsh
036: */
037: public class KeywordNameMaker implements NameMaker {
038: // Constants -------------------------------------------------------------
039: private static final String DUMMY_ARG_LIST = "dummy";
040:
041: // Fields ----------------------------------------------------------------
042: private int skipped = 0; // Names skipped in the sequence
043: private Vector namesToDate = new Vector();
044: private Hashtable argCount = new Hashtable();
045: private String[] noObfNames = null; // List of names not to be obfuscated
046: private String[] keywordsToUse;
047: private String[] keywordsToExclude;
048: private String[] firstLetter;
049: private String[] nextLetter;
050: private String[] noKeywords = {};
051: private String[] someKeywords = { "a", "if", "do", "for", "int",
052: "new", "try", "byte", "case", "char", "else", "goto",
053: "long", "null", "void" };
054: private String[] allKeywords = { "if", "do", "for", "int", "new",
055: "try", "byte", "case", "char", "else", "goto", "long",
056: "null", "this", "void", "true", "false", "break", "catch",
057: "class", "const", "float", "final", "short", "super",
058: "throw", "while", "double", "import", "native", "public",
059: "return", "static", "switch", "throws", "boolean",
060: "default", "extends", "finally", "package", "private",
061: "abstract", "continue", "volatile", "interface",
062: "protected", "transient", "implements", "instanceof",
063: "synchronized" };
064: private String[] firstLetterLower = { "a", "b", "c", "d", "e", "f",
065: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
066: "s", "t", "u", "v", "w", "x", "y", "z" };
067: private String[] nextLetterLower = { "a", "b", "c", "d", "e", "f",
068: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
069: "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3",
070: "4", "5", "6", "7", "8", "9" };
071: private String[] firstLetterAll = { "a", "b", "c", "d", "e", "f",
072: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
073: "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D",
074: "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
075: "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
076: private String[] nextLetterAll = { "a", "b", "c", "d", "e", "f",
077: "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
078: "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D",
079: "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
080: "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1",
081: "2", "3", "4", "5", "6", "7", "8", "9" };
082:
083: // Class Methods ---------------------------------------------------------
084: /** Main method for testing. */
085: /* public static void main(String[] args)
086: {
087: PrintWriter pw = new PrintWriter(
088: new BufferedOutputStream(
089: new FileOutputStream("keywordnamemaker.tst")));
090: try
091: {
092: NameMaker nmk = new KeywordNameMaker();
093: for (int i = 0; i < 1000000; i++)
094: {
095: pw.println(nmk.nextName(null));
096: }
097: }
098: finally
099: {
100: pw.close();
101: }
102: }
103: */
104:
105: // Instance Methods ------------------------------------------------------
106: /** Ctor. */
107: public KeywordNameMaker() {
108: this (null);
109: }
110:
111: /** Ctor - block names not to be obfuscated from the mapping target space. */
112: public KeywordNameMaker(String[] noObfNames) {
113: this (noObfNames, true);
114: }
115:
116: /** Ctor - block names not to be obfuscated from the mapping target space. */
117: public KeywordNameMaker(String[] noObfNames, boolean useKeywords) {
118: this (noObfNames, true, false);
119: }
120:
121: /** Ctor - block names not to be obfuscated from the mapping target space. */
122: public KeywordNameMaker(String[] noObfNames, boolean useKeywords,
123: boolean lowerCaseOnly) {
124: this .noObfNames = noObfNames == null ? new String[0]
125: : noObfNames;
126: if (useKeywords) {
127: keywordsToUse = someKeywords;
128: keywordsToExclude = someKeywords;
129: } else {
130: keywordsToUse = noKeywords;
131: keywordsToExclude = allKeywords;
132: }
133: if (lowerCaseOnly) {
134: firstLetter = firstLetterLower;
135: nextLetter = nextLetterLower;
136: } else {
137: firstLetter = firstLetterAll;
138: nextLetter = nextLetterAll;
139: }
140: }
141:
142: /** Return the next unique name for this namespace. */
143: public String nextName(String descriptor) {
144: // Check for arg-list in hashtable
145: String argList = DUMMY_ARG_LIST;
146: if (descriptor != null) {
147: argList = getArgList(descriptor);
148: }
149: Integer intCount = (Integer) argCount.get(argList);
150: int theCount = 0;
151: if (intCount == null) {
152: argCount.put(argList, new Integer(theCount));
153: } else {
154: theCount = intCount.intValue() + 1;
155: argCount.remove(argList);
156: argCount.put(argList, new Integer(theCount));
157: }
158: return getName(theCount);
159: }
160:
161: // Extract the arg-list from a descriptor
162: private String getArgList(String descriptor) {
163: int pos = descriptor.indexOf(')');
164: return descriptor.substring(1, pos);
165: }
166:
167: // Generate i'th allowed, unique name
168: private String getName(int index) {
169: // If we have previously computed this name, just return it
170: String name = null;
171: if (index < namesToDate.size()) {
172: name = (String) namesToDate.elementAt(index);
173: } else {
174: // Generate a new valid name for the sequence
175: for (;;) {
176: name = getNewName(index + skipped);
177: if (!Tools.isInArray(name, noObfNames)
178: && (index + skipped < keywordsToUse.length || !Tools
179: .isInArray(name, keywordsToExclude))) {
180: break;
181: }
182: skipped++;
183: }
184: namesToDate.addElement(name);
185: }
186: return name;
187: }
188:
189: // Generate j'th name in sequence (can repeat keywords)
190: private String getNewName(int index) {
191: String name = null;
192:
193: // Check if we are in the 'keyword' part of the namespace
194: if (index < keywordsToUse.length) {
195: name = keywordsToUse[index];
196: } else {
197: // Check if we are in the single letter part of the namespace
198: index -= keywordsToUse.length;
199: if (index < firstLetter.length) {
200: name = firstLetter[index];
201: } else {
202: // We are in the >=2 letter part of namespace
203: index -= firstLetter.length;
204: int nextLetters = 1;
205: int subspaceSize = nextLetter.length;
206: while (index >= firstLetter.length * subspaceSize) {
207: index -= firstLetter.length * subspaceSize;
208: nextLetters++;
209: subspaceSize *= nextLetter.length;
210: }
211:
212: // Pull out the name
213: StringBuffer sb = new StringBuffer(firstLetter[index
214: / subspaceSize]);
215: while (subspaceSize != 1) {
216: index %= subspaceSize;
217: subspaceSize /= nextLetter.length;
218: sb.append(nextLetter[index / subspaceSize]);
219: }
220:
221: // Check for collision with keywords
222: name = sb.toString();
223: }
224: }
225: return name;
226: }
227: }
|