summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/convert/ClassIdentity.java
diff options
context:
space:
mode:
authorGravatar jeff2014-08-30 11:41:17 -0400
committerGravatar jeff2014-08-30 11:41:17 -0400
commite43fac9f55cfeebacd869352bfb090b7d8d063c1 (patch)
treedd4b01daa04dbdcecc765c7270e18bdf1b63d97f /src/cuchaz/enigma/convert/ClassIdentity.java
parentworking on version conversion (diff)
downloadenigma-fork-e43fac9f55cfeebacd869352bfb090b7d8d063c1.tar.gz
enigma-fork-e43fac9f55cfeebacd869352bfb090b7d8d063c1.tar.xz
enigma-fork-e43fac9f55cfeebacd869352bfb090b7d8d063c1.zip
got a decent class matcher working
Diffstat (limited to '')
-rw-r--r--src/cuchaz/enigma/convert/ClassIdentity.java255
1 files changed, 226 insertions, 29 deletions
diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java
index aecf7fc..0a3a449 100644
--- a/src/cuchaz/enigma/convert/ClassIdentity.java
+++ b/src/cuchaz/enigma/convert/ClassIdentity.java
@@ -13,10 +13,12 @@ package cuchaz.enigma.convert;
13import java.io.UnsupportedEncodingException; 13import java.io.UnsupportedEncodingException;
14import java.security.MessageDigest; 14import java.security.MessageDigest;
15import java.security.NoSuchAlgorithmException; 15import java.security.NoSuchAlgorithmException;
16import java.util.Enumeration;
16import java.util.List; 17import java.util.List;
17import java.util.Map; 18import java.util.Map;
18import java.util.Set; 19import java.util.Set;
19 20
21import javassist.CannotCompileException;
20import javassist.CtBehavior; 22import javassist.CtBehavior;
21import javassist.CtClass; 23import javassist.CtClass;
22import javassist.CtConstructor; 24import javassist.CtConstructor;
@@ -27,34 +29,58 @@ import javassist.bytecode.CodeIterator;
27import javassist.bytecode.ConstPool; 29import javassist.bytecode.ConstPool;
28import javassist.bytecode.Descriptor; 30import javassist.bytecode.Descriptor;
29import javassist.bytecode.Opcode; 31import javassist.bytecode.Opcode;
32import javassist.expr.ConstructorCall;
33import javassist.expr.ExprEditor;
34import javassist.expr.FieldAccess;
35import javassist.expr.MethodCall;
36import javassist.expr.NewExpr;
30 37
31import com.beust.jcommander.internal.Maps; 38import com.beust.jcommander.internal.Maps;
32import com.beust.jcommander.internal.Sets; 39import com.beust.jcommander.internal.Sets;
33import com.google.common.collect.Lists; 40import com.google.common.collect.Lists;
34 41
42import cuchaz.enigma.Constants;
35import cuchaz.enigma.Util; 43import cuchaz.enigma.Util;
44import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
45import cuchaz.enigma.analysis.EntryReference;
46import cuchaz.enigma.analysis.JarIndex;
36import cuchaz.enigma.bytecode.ConstPoolEditor; 47import cuchaz.enigma.bytecode.ConstPoolEditor;
37import cuchaz.enigma.bytecode.InfoType; 48import cuchaz.enigma.bytecode.InfoType;
38import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor; 49import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
50import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
51import cuchaz.enigma.mapping.BehaviorEntry;
39import cuchaz.enigma.mapping.ClassEntry; 52import cuchaz.enigma.mapping.ClassEntry;
53import cuchaz.enigma.mapping.ConstructorEntry;
54import cuchaz.enigma.mapping.Entry;
55import cuchaz.enigma.mapping.FieldEntry;
56import cuchaz.enigma.mapping.MethodEntry;
40import cuchaz.enigma.mapping.SignatureUpdater; 57import cuchaz.enigma.mapping.SignatureUpdater;
41import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 58import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
42 59
43public class ClassIdentity 60public class ClassIdentity
44{ 61{
45 private ClassEntry m_classEntry; 62 private ClassEntry m_classEntry;
63 private SidedClassNamer m_namer;
46 private Set<String> m_fields; 64 private Set<String> m_fields;
47 private Set<String> m_methods; 65 private Set<String> m_methods;
48 private Set<String> m_constructors; 66 private Set<String> m_constructors;
49 private String m_staticInitializer; 67 private String m_staticInitializer;
68 private String m_extends;
69 private Set<String> m_implements;
70 private Set<String> m_implementations;
71 private Set<String> m_references;
50 72
51 public ClassIdentity( CtClass c ) 73 public ClassIdentity( CtClass c, SidedClassNamer namer, JarIndex index, boolean useReferences )
52 { 74 {
75 m_namer = namer;
76
77 // stuff from the bytecode
78
53 m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) ); 79 m_classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
54 m_fields = Sets.newHashSet(); 80 m_fields = Sets.newHashSet();
55 for( CtField field : c.getDeclaredFields() ) 81 for( CtField field : c.getDeclaredFields() )
56 { 82 {
57 m_fields.add( scrubSignature( scrubSignature( field.getSignature() ) ) ); 83 m_fields.add( scrubSignature( field.getSignature() ) );
58 } 84 }
59 m_methods = Sets.newHashSet(); 85 m_methods = Sets.newHashSet();
60 for( CtMethod method : c.getDeclaredMethods() ) 86 for( CtMethod method : c.getDeclaredMethods() )
@@ -71,6 +97,73 @@ public class ClassIdentity
71 { 97 {
72 m_staticInitializer = getBehaviorSignature( c.getClassInitializer() ); 98 m_staticInitializer = getBehaviorSignature( c.getClassInitializer() );
73 } 99 }
100 m_extends = "";
101 if( c.getClassFile().getSuperclass() != null )
102 {
103 m_extends = scrubClassName( c.getClassFile().getSuperclass() );
104 }
105 m_implements = Sets.newHashSet();
106 for( String interfaceName : c.getClassFile().getInterfaces() )
107 {
108 m_implements.add( scrubClassName( interfaceName ) );
109 }
110
111 // stuff from the jar index
112
113 m_implementations = Sets.newHashSet();
114 @SuppressWarnings( "unchecked" )
115 Enumeration<ClassImplementationsTreeNode> implementations = index.getClassImplementations( null, m_classEntry ).children();
116 while( implementations.hasMoreElements() )
117 {
118 ClassImplementationsTreeNode node = implementations.nextElement();
119 m_implementations.add( scrubClassName( node.getClassEntry().getName() ) );
120 }
121
122 m_references = Sets.newHashSet();
123 if( useReferences )
124 {
125 for( CtField field : c.getDeclaredFields() )
126 {
127 FieldEntry fieldEntry = new FieldEntry( m_classEntry, field.getName() );
128 for( EntryReference<FieldEntry,BehaviorEntry> reference : index.getFieldReferences( fieldEntry ) )
129 {
130 addReference( reference );
131 }
132 }
133 for( CtMethod method : c.getDeclaredMethods() )
134 {
135 MethodEntry methodEntry = new MethodEntry( m_classEntry, method.getName(), method.getSignature() );
136 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( methodEntry ) )
137 {
138 addReference( reference );
139 }
140 }
141 for( CtConstructor constructor : c.getDeclaredConstructors() )
142 {
143 ConstructorEntry constructorEntry = new ConstructorEntry( m_classEntry, constructor.getSignature() );
144 for( EntryReference<BehaviorEntry,BehaviorEntry> reference : index.getBehaviorReferences( constructorEntry ) )
145 {
146 addReference( reference );
147 }
148 }
149 }
150 }
151
152 private void addReference( EntryReference<? extends Entry,BehaviorEntry> reference )
153 {
154 if( reference.context.getSignature() != null )
155 {
156 m_references.add( String.format( "%s_%s",
157 scrubClassName( reference.context.getClassName() ),
158 scrubSignature( reference.context.getSignature() )
159 ) );
160 }
161 else
162 {
163 m_references.add( String.format( "%s_<clinit>",
164 scrubClassName( reference.context.getClassName() )
165 ) );
166 }
74 } 167 }
75 168
76 public ClassEntry getClassEntry( ) 169 public ClassEntry getClassEntry( )
@@ -109,9 +202,38 @@ public class ClassIdentity
109 buf.append( m_staticInitializer ); 202 buf.append( m_staticInitializer );
110 buf.append( "\n" ); 203 buf.append( "\n" );
111 } 204 }
205 if( m_extends.length() > 0 )
206 {
207 buf.append( "\textends " );
208 buf.append( m_extends );
209 buf.append( "\n" );
210 }
211 for( String interfaceName : m_implements )
212 {
213 buf.append( "\timplements " );
214 buf.append( interfaceName );
215 buf.append( "\n" );
216 }
217 for( String implementation : m_implementations )
218 {
219 buf.append( "\timplemented by " );
220 buf.append( implementation );
221 buf.append( "\n" );
222 }
223 for( String reference : m_references )
224 {
225 buf.append( "\treference " );
226 buf.append( reference );
227 buf.append( "\n" );
228 }
112 return buf.toString(); 229 return buf.toString();
113 } 230 }
114 231
232 private String scrubClassName( String className )
233 {
234 return scrubSignature( "L" + Descriptor.toJvmName( className ) + ";" );
235 }
236
115 private String scrubSignature( String signature ) 237 private String scrubSignature( String signature )
116 { 238 {
117 return SignatureUpdater.update( signature, new ClassNameUpdater( ) 239 return SignatureUpdater.update( signature, new ClassNameUpdater( )
@@ -121,12 +243,30 @@ public class ClassIdentity
121 @Override 243 @Override
122 public String update( String className ) 244 public String update( String className )
123 { 245 {
124 // does the class have a package? 246 // classes not in the none package can be passed through
125 if( className.indexOf( '/' ) >= 0 ) 247 ClassEntry classEntry = new ClassEntry( className );
248 if( !classEntry.getPackageName().equals( Constants.NonePackage ) )
126 { 249 {
127 return className; 250 return className;
128 } 251 }
129 252
253 // is this class ourself?
254 if( className.equals( m_classEntry.getName() ) )
255 {
256 return "CSelf";
257 }
258
259 // try the namer
260 if( m_namer != null )
261 {
262 String newName = m_namer.getName( className );
263 if( newName != null )
264 {
265 return newName;
266 }
267 }
268
269 // otherwise, use local naming
130 if( !m_classNames.containsKey( className ) ) 270 if( !m_classNames.containsKey( className ) )
131 { 271 {
132 m_classNames.put( className, getNewClassName() ); 272 m_classNames.put( className, getNewClassName() );
@@ -141,6 +281,11 @@ public class ClassIdentity
141 } ); 281 } );
142 } 282 }
143 283
284 private boolean isClassMatchedUniquely( String className )
285 {
286 return m_namer != null && m_namer.getName( Descriptor.toJvmName( className ) ) != null;
287 }
288
144 private String getBehaviorSignature( CtBehavior behavior ) 289 private String getBehaviorSignature( CtBehavior behavior )
145 { 290 {
146 try 291 try
@@ -153,8 +298,7 @@ public class ClassIdentity
153 298
154 // compute the hash from the opcodes 299 // compute the hash from the opcodes
155 ConstPool constants = behavior.getMethodInfo().getConstPool(); 300 ConstPool constants = behavior.getMethodInfo().getConstPool();
156 ConstPoolEditor editor = new ConstPoolEditor( constants ); 301 final MessageDigest digest = MessageDigest.getInstance( "MD5" );
157 MessageDigest digest = MessageDigest.getInstance( "MD5" );
158 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator(); 302 CodeIterator iter = behavior.getMethodInfo().getCodeAttribute().iterator();
159 while( iter.hasNext() ) 303 while( iter.hasNext() )
160 { 304 {
@@ -164,46 +308,91 @@ public class ClassIdentity
164 int opcode = iter.byteAt( pos ); 308 int opcode = iter.byteAt( pos );
165 digest.update( (byte)opcode ); 309 digest.update( (byte)opcode );
166 310
167 // is there a constant value here?
168 int constIndex = -1;
169 switch( opcode ) 311 switch( opcode )
170 { 312 {
171 case Opcode.LDC: 313 case Opcode.LDC:
172 constIndex = iter.byteAt( pos + 1 ); 314 {
315 int constIndex = iter.byteAt( pos + 1 );
316 updateHashWithConstant( digest, constants, constIndex );
317 }
173 break; 318 break;
174 319
175 case Opcode.LDC_W: 320 case Opcode.LDC_W:
176 constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 );
177 break;
178
179 case Opcode.LDC2_W: 321 case Opcode.LDC2_W:
180 constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 ); 322 {
323 int constIndex = ( iter.byteAt( pos + 1 ) << 8 ) | iter.byteAt( pos + 2 );
324 updateHashWithConstant( digest, constants, constIndex );
325 }
181 break; 326 break;
182 } 327 }
328 }
329
330 // update hash with method and field accesses
331 behavior.instrument( new ExprEditor( )
332 {
333 @Override
334 public void edit( MethodCall call )
335 {
336 updateHashWithString( digest, scrubClassName( call.getClassName() ) );
337 updateHashWithString( digest, scrubSignature( call.getSignature() ) );
338 if( isClassMatchedUniquely( call.getClassName() ) )
339 {
340 updateHashWithString( digest, call.getMethodName() );
341 }
342 }
183 343
184 if( constIndex >= 0 ) 344 @Override
345 public void edit( FieldAccess access )
185 { 346 {
186 // update the hash with the constant value 347 updateHashWithString( digest, scrubClassName( access.getClassName() ) );
187 ConstInfoAccessor item = editor.getItem( constIndex ); 348 updateHashWithString( digest, scrubSignature( access.getSignature() ) );
188 if( item.getType() == InfoType.StringInfo ) 349 if( isClassMatchedUniquely( access.getClassName() ) )
189 { 350 {
190 String val = constants.getStringInfo( constIndex ); 351 updateHashWithString( digest, access.getFieldName() );
191 try
192 {
193 digest.update( val.getBytes( "UTF8" ) );
194 }
195 catch( UnsupportedEncodingException ex )
196 {
197 throw new Error( ex );
198 }
199 } 352 }
200 } 353 }
201 } 354
355 @Override
356 public void edit( ConstructorCall call )
357 {
358 updateHashWithString( digest, scrubClassName( call.getClassName() ) );
359 updateHashWithString( digest, scrubSignature( call.getSignature() ) );
360 }
361
362 @Override
363 public void edit( NewExpr expr )
364 {
365 updateHashWithString( digest, scrubClassName( expr.getClassName() ) );
366 }
367 } );
202 368
203 // convert the hash to a hex string 369 // convert the hash to a hex string
204 return toHex( digest.digest() ); 370 return toHex( digest.digest() );
205 } 371 }
206 catch( BadBytecode | NoSuchAlgorithmException ex ) 372 catch( BadBytecode | NoSuchAlgorithmException | CannotCompileException ex )
373 {
374 throw new Error( ex );
375 }
376 }
377
378 private void updateHashWithConstant( MessageDigest digest, ConstPool constants, int index )
379 {
380 ConstPoolEditor editor = new ConstPoolEditor( constants );
381 ConstInfoAccessor item = editor.getItem( index );
382 if( item.getType() == InfoType.StringInfo )
383 {
384 updateHashWithString( digest, constants.getStringInfo( index ) );
385 }
386 // TODO: other constants
387 }
388
389 private void updateHashWithString( MessageDigest digest, String val )
390 {
391 try
392 {
393 digest.update( val.getBytes( "UTF8" ) );
394 }
395 catch( UnsupportedEncodingException ex )
207 { 396 {
208 throw new Error( ex ); 397 throw new Error( ex );
209 } 398 }
@@ -239,7 +428,11 @@ public class ClassIdentity
239 return m_fields.equals( other.m_fields ) 428 return m_fields.equals( other.m_fields )
240 && m_methods.equals( other.m_methods ) 429 && m_methods.equals( other.m_methods )
241 && m_constructors.equals( other.m_constructors ) 430 && m_constructors.equals( other.m_constructors )
242 && m_staticInitializer.equals( other.m_staticInitializer ); 431 && m_staticInitializer.equals( other.m_staticInitializer )
432 && m_extends.equals( other.m_extends )
433 && m_implements.equals( other.m_implements )
434 && m_implementations.equals( other.m_implementations )
435 && m_references.equals( other.m_references );
243 } 436 }
244 437
245 @Override 438 @Override
@@ -250,6 +443,10 @@ public class ClassIdentity
250 objs.addAll( m_methods ); 443 objs.addAll( m_methods );
251 objs.addAll( m_constructors ); 444 objs.addAll( m_constructors );
252 objs.add( m_staticInitializer ); 445 objs.add( m_staticInitializer );
446 objs.add( m_extends );
447 objs.addAll( m_implements );
448 objs.addAll( m_implementations );
449 objs.addAll( m_references );
253 return Util.combineHashesOrdered( objs ); 450 return Util.combineHashesOrdered( objs );
254 } 451 }
255} 452}