1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.classfile.util; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.CodeAttribute; 25 import proguard.classfile.constant.*; 26 import proguard.classfile.constant.visitor.ConstantVisitor; 27 import proguard.classfile.instruction.*; 28 import proguard.classfile.instruction.visitor.InstructionVisitor; 29 30 /** 31 * This InstructionVisitor checks whether a given pattern instruction sequence 32 * occurs in the instructions that are visited. The arguments of the 33 * instruction sequence can be wildcards that are matched. 34 * 35 * @author Eric Lafortune 36 */ 37 public class InstructionSequenceMatcher 38 extends SimplifiedVisitor 39 implements InstructionVisitor, 40 ConstantVisitor 41 { 42 /* 43 private static boolean DEBUG = false; 44 public static boolean DEBUG_MORE = false; 45 /*/ 46 private static final boolean DEBUG = false; 47 private static final boolean DEBUG_MORE = false; 48 //*/ 49 50 public static final int X = 0x40000000; 51 public static final int Y = 0x40000001; 52 public static final int Z = 0x40000002; 53 54 public static final int A = 0x40000003; 55 public static final int B = 0x40000004; 56 public static final int C = 0x40000005; 57 public static final int D = 0x40000006; 58 59 60 private final Constant[] patternConstants; 61 private final Instruction[] patternInstructions; 62 63 private boolean matching; 64 private boolean matchingAnyWildCards; 65 private int patternInstructionIndex; 66 private final int[] matchedInstructionOffsets; 67 private int matchedArgumentFlags; 68 private final int[] matchedArguments = new int[7]; 69 private long matchedConstantFlags; 70 private final int[] matchedConstantIndices; 71 72 // Fields acting as a parameter and a return value for visitor methods. 73 private Constant patternConstant; 74 private boolean matchingConstant; 75 76 77 /** 78 * Creates a new InstructionSequenceMatcher. 79 * @param patternConstants any constants referenced by the pattern 80 * instruction. 81 * @param patternInstructions the pattern instruction sequence. 82 */ InstructionSequenceMatcher(Constant[] patternConstants, Instruction[] patternInstructions)83 public InstructionSequenceMatcher(Constant[] patternConstants, 84 Instruction[] patternInstructions) 85 { 86 this.patternConstants = patternConstants; 87 this.patternInstructions = patternInstructions; 88 89 matchedInstructionOffsets = new int[patternInstructions.length]; 90 matchedConstantIndices = new int[patternConstants.length]; 91 } 92 93 94 /** 95 * Starts matching from the first instruction again next time. 96 */ reset()97 public void reset() 98 { 99 patternInstructionIndex = 0; 100 matchedArgumentFlags = 0; 101 matchedConstantFlags = 0L; 102 } 103 104 isMatching()105 public boolean isMatching() 106 { 107 return matching; 108 } 109 110 isMatchingAnyWildcards()111 public boolean isMatchingAnyWildcards() 112 { 113 return matchingAnyWildCards; 114 } 115 116 instructionCount()117 public int instructionCount() 118 { 119 return patternInstructions.length; 120 } 121 122 matchedInstructionOffset(int index)123 public int matchedInstructionOffset(int index) 124 { 125 return matchedInstructionOffsets[index]; 126 } 127 128 matchedArgument(int argument)129 public int matchedArgument(int argument) 130 { 131 int argumentIndex = argument - X; 132 return argumentIndex < 0 ? 133 argument : 134 matchedArguments[argumentIndex]; 135 } 136 137 matchedArguments(int[] arguments)138 public int[] matchedArguments(int[] arguments) 139 { 140 int[] matchedArguments = new int[arguments.length]; 141 142 for (int index = 0; index < arguments.length; index++) 143 { 144 matchedArguments[index] = matchedArgument(arguments[index]); 145 } 146 147 return matchedArguments; 148 } 149 150 matchedConstantIndex(int constantIndex)151 public int matchedConstantIndex(int constantIndex) 152 { 153 int argumentIndex = constantIndex - X; 154 return argumentIndex < 0 ? 155 matchedConstantIndices[constantIndex] : 156 matchedArguments[argumentIndex]; 157 } 158 159 matchedBranchOffset(int offset, int branchOffset)160 public int matchedBranchOffset(int offset, int branchOffset) 161 { 162 int argumentIndex = branchOffset - X; 163 return argumentIndex < 0 ? 164 branchOffset : 165 matchedArguments[argumentIndex] - offset; 166 } 167 168 matchedJumpOffsets(int offset, int[] jumpOffsets)169 public int[] matchedJumpOffsets(int offset, int[] jumpOffsets) 170 { 171 int[] matchedJumpOffsets = new int[jumpOffsets.length]; 172 173 for (int index = 0; index < jumpOffsets.length; index++) 174 { 175 matchedJumpOffsets[index] = matchedBranchOffset(offset, 176 jumpOffsets[index]); 177 } 178 179 return matchedJumpOffsets; 180 } 181 182 183 // Implementations for InstructionVisitor. 184 visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)185 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 186 { 187 Instruction patternInstruction = patternInstructions[patternInstructionIndex]; 188 189 // Check if the instruction matches the next instruction in the sequence. 190 boolean condition = 191 matchingOpcodes(simpleInstruction, patternInstruction) && 192 matchingArguments(simpleInstruction.constant, 193 ((SimpleInstruction)patternInstruction).constant); 194 195 // Check if the instruction sequence is matching now. 196 checkMatch(condition, 197 clazz, 198 method, 199 codeAttribute, 200 offset, 201 simpleInstruction); 202 } 203 204 visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)205 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 206 { 207 Instruction patternInstruction = patternInstructions[patternInstructionIndex]; 208 209 // Check if the instruction matches the next instruction in the sequence. 210 boolean condition = 211 matchingOpcodes(variableInstruction, patternInstruction) && 212 matchingArguments(variableInstruction.variableIndex, 213 ((VariableInstruction)patternInstruction).variableIndex) && 214 matchingArguments(variableInstruction.constant, 215 ((VariableInstruction)patternInstruction).constant); 216 217 // Check if the instruction sequence is matching now. 218 checkMatch(condition, 219 clazz, 220 method, 221 codeAttribute, 222 offset, 223 variableInstruction); 224 } 225 226 visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)227 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 228 { 229 Instruction patternInstruction = patternInstructions[patternInstructionIndex]; 230 231 // Check if the instruction matches the next instruction in the sequence. 232 boolean condition = 233 matchingOpcodes(constantInstruction, patternInstruction) && 234 matchingConstantIndices(clazz, 235 constantInstruction.constantIndex, 236 ((ConstantInstruction)patternInstruction).constantIndex) && 237 matchingArguments(constantInstruction.constant, 238 ((ConstantInstruction)patternInstruction).constant); 239 240 // Check if the instruction sequence is matching now. 241 checkMatch(condition, 242 clazz, 243 method, 244 codeAttribute, 245 offset, 246 constantInstruction); 247 } 248 249 visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)250 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 251 { 252 Instruction patternInstruction = patternInstructions[patternInstructionIndex]; 253 254 // Check if the instruction matches the next instruction in the from 255 // sequence. 256 boolean condition = 257 matchingOpcodes(branchInstruction, patternInstruction) && 258 matchingBranchOffsets(offset, 259 branchInstruction.branchOffset, 260 ((BranchInstruction)patternInstruction).branchOffset); 261 262 // Check if the instruction sequence is matching now. 263 checkMatch(condition, 264 clazz, 265 method, 266 codeAttribute, 267 offset, 268 branchInstruction); 269 } 270 271 visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)272 public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) 273 { 274 Instruction patternInstruction = patternInstructions[patternInstructionIndex]; 275 276 // Check if the instruction matches the next instruction in the sequence. 277 boolean condition = 278 matchingOpcodes(tableSwitchInstruction, patternInstruction) && 279 matchingBranchOffsets(offset, 280 tableSwitchInstruction.defaultOffset, 281 ((TableSwitchInstruction)patternInstruction).defaultOffset) && 282 matchingArguments(tableSwitchInstruction.lowCase, 283 ((TableSwitchInstruction)patternInstruction).lowCase) && 284 matchingArguments(tableSwitchInstruction.highCase, 285 ((TableSwitchInstruction)patternInstruction).highCase) && 286 matchingJumpOffsets(offset, 287 tableSwitchInstruction.jumpOffsets, 288 ((TableSwitchInstruction)patternInstruction).jumpOffsets); 289 290 // Check if the instruction sequence is matching now. 291 checkMatch(condition, 292 clazz, 293 method, 294 codeAttribute, 295 offset, 296 tableSwitchInstruction); 297 } 298 299 visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)300 public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) 301 { 302 Instruction patternInstruction = patternInstructions[patternInstructionIndex]; 303 304 // Check if the instruction matches the next instruction in the sequence. 305 boolean condition = 306 matchingOpcodes(lookUpSwitchInstruction, patternInstruction) && 307 matchingBranchOffsets(offset, 308 lookUpSwitchInstruction.defaultOffset, 309 ((LookUpSwitchInstruction)patternInstruction).defaultOffset) && 310 matchingArguments(lookUpSwitchInstruction.cases, 311 ((LookUpSwitchInstruction)patternInstruction).cases) && 312 matchingJumpOffsets(offset, 313 lookUpSwitchInstruction.jumpOffsets, 314 ((LookUpSwitchInstruction)patternInstruction).jumpOffsets); 315 316 // Check if the instruction sequence is matching now. 317 checkMatch(condition, 318 clazz, 319 method, 320 codeAttribute, 321 offset, 322 lookUpSwitchInstruction); 323 } 324 325 326 // Implementations for ConstantVisitor. 327 visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)328 public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) 329 { 330 IntegerConstant integerPatternConstant = (IntegerConstant)patternConstant; 331 332 // Compare the integer values. 333 matchingConstant = integerConstant.getValue() == 334 integerPatternConstant.getValue(); 335 } 336 337 visitLongConstant(Clazz clazz, LongConstant longConstant)338 public void visitLongConstant(Clazz clazz, LongConstant longConstant) 339 { 340 LongConstant longPatternConstant = (LongConstant)patternConstant; 341 342 // Compare the long values. 343 matchingConstant = longConstant.getValue() == 344 longPatternConstant.getValue(); 345 } 346 347 visitFloatConstant(Clazz clazz, FloatConstant floatConstant)348 public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) 349 { 350 FloatConstant floatPatternConstant = (FloatConstant)patternConstant; 351 352 // Compare the float values. 353 matchingConstant = floatConstant.getValue() == 354 floatPatternConstant.getValue(); 355 } 356 357 visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)358 public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) 359 { 360 DoubleConstant doublePatternConstant = (DoubleConstant)patternConstant; 361 362 // Compare the double values. 363 matchingConstant = doubleConstant.getValue() == 364 doublePatternConstant.getValue(); 365 } 366 367 visitStringConstant(Clazz clazz, StringConstant stringConstant)368 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 369 { 370 StringConstant stringPatternConstant = (StringConstant)patternConstant; 371 372 // Check the UTF-8 constant. 373 matchingConstant = 374 matchingConstantIndices(clazz, 375 stringConstant.u2stringIndex, 376 stringPatternConstant.u2stringIndex); 377 } 378 379 visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)380 public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) 381 { 382 Utf8Constant utf8PatternConstant = (Utf8Constant)patternConstant; 383 384 // Compare the actual strings. 385 matchingConstant = utf8Constant.getString().equals( 386 utf8PatternConstant.getString()); 387 } 388 389 visitAnyRefConstant(Clazz clazz, RefConstant refConstant)390 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 391 { 392 RefConstant refPatternConstant = (RefConstant)patternConstant; 393 394 // Check the class and the name and type. 395 matchingConstant = 396 matchingConstantIndices(clazz, 397 refConstant.getClassIndex(), 398 refPatternConstant.getClassIndex()) && 399 matchingConstantIndices(clazz, 400 refConstant.getNameAndTypeIndex(), 401 refPatternConstant.getNameAndTypeIndex()); 402 } 403 404 visitClassConstant(Clazz clazz, ClassConstant classConstant)405 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 406 { 407 ClassConstant classPatternConstant = (ClassConstant)patternConstant; 408 409 // Check the class name. 410 matchingConstant = 411 matchingConstantIndices(clazz, 412 classConstant.u2nameIndex, 413 classPatternConstant.u2nameIndex); 414 } 415 416 visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)417 public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) 418 { 419 NameAndTypeConstant typePatternConstant = (NameAndTypeConstant)patternConstant; 420 421 // Check the name and the descriptor. 422 matchingConstant = 423 matchingConstantIndices(clazz, 424 nameAndTypeConstant.u2nameIndex, 425 typePatternConstant.u2nameIndex) && 426 matchingConstantIndices(clazz, 427 nameAndTypeConstant.u2descriptorIndex, 428 typePatternConstant.u2descriptorIndex); 429 } 430 431 432 // Small utility methods. 433 matchingOpcodes(Instruction instruction1, Instruction instruction2)434 private boolean matchingOpcodes(Instruction instruction1, 435 Instruction instruction2) 436 { 437 // Check the opcode. 438 return instruction1.opcode == instruction2.opcode || 439 instruction1.canonicalOpcode() == instruction2.opcode; 440 } 441 442 matchingArguments(int argument1, int argument2)443 private boolean matchingArguments(int argument1, 444 int argument2) 445 { 446 int argumentIndex = argument2 - X; 447 if (argumentIndex < 0) 448 { 449 // Check the literal argument. 450 return argument1 == argument2; 451 } 452 else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0) 453 { 454 // Store a wildcard argument. 455 matchedArguments[argumentIndex] = argument1; 456 matchedArgumentFlags |= 1 << argumentIndex; 457 458 return true; 459 } 460 else 461 { 462 // Check the previously stored wildcard argument. 463 return matchedArguments[argumentIndex] == argument1; 464 } 465 } 466 467 matchingArguments(int[] arguments1, int[] arguments2)468 private boolean matchingArguments(int[] arguments1, 469 int[] arguments2) 470 { 471 if (arguments1.length != arguments2.length) 472 { 473 return false; 474 } 475 476 for (int index = 0; index < arguments1.length; index++) 477 { 478 if (!matchingArguments(arguments1[index], arguments2[index])) 479 { 480 return false; 481 } 482 } 483 484 return true; 485 } 486 487 matchingConstantIndices(Clazz clazz, int constantIndex1, int constantIndex2)488 private boolean matchingConstantIndices(Clazz clazz, 489 int constantIndex1, 490 int constantIndex2) 491 { 492 if (constantIndex2 >= X) 493 { 494 // Check the constant index. 495 return matchingArguments(constantIndex1, constantIndex2); 496 } 497 else if ((matchedConstantFlags & (1L << constantIndex2)) == 0) 498 { 499 // Check the actual constant. 500 matchingConstant = false; 501 patternConstant = patternConstants[constantIndex2]; 502 503 if (clazz.getTag(constantIndex1) == patternConstant.getTag()) 504 { 505 clazz.constantPoolEntryAccept(constantIndex1, this); 506 507 if (matchingConstant) 508 { 509 // Store the constant index. 510 matchedConstantIndices[constantIndex2] = constantIndex1; 511 matchedConstantFlags |= 1L << constantIndex2; 512 } 513 } 514 515 return matchingConstant; 516 } 517 else 518 { 519 // Check a previously stored constant index. 520 return matchedConstantIndices[constantIndex2] == constantIndex1; 521 } 522 } 523 524 matchingBranchOffsets(int offset, int branchOffset1, int branchOffset2)525 private boolean matchingBranchOffsets(int offset, 526 int branchOffset1, 527 int branchOffset2) 528 { 529 int argumentIndex = branchOffset2 - X; 530 if (argumentIndex < 0) 531 { 532 // Check the literal argument. 533 return branchOffset1 == branchOffset2; 534 } 535 else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0) 536 { 537 // Store a wildcard argument. 538 matchedArguments[argumentIndex] = offset + branchOffset1; 539 matchedArgumentFlags |= 1 << argumentIndex; 540 541 return true; 542 } 543 else 544 { 545 // Check the previously stored wildcard argument. 546 return matchedArguments[argumentIndex] == offset + branchOffset1; 547 } 548 } 549 550 matchingJumpOffsets(int offset, int[] jumpOffsets1, int[] jumpOffsets2)551 private boolean matchingJumpOffsets(int offset, 552 int[] jumpOffsets1, 553 int[] jumpOffsets2) 554 { 555 if (jumpOffsets1.length != jumpOffsets2.length) 556 { 557 return false; 558 } 559 560 for (int index = 0; index < jumpOffsets1.length; index++) 561 { 562 if (!matchingBranchOffsets(offset, 563 jumpOffsets1[index], 564 jumpOffsets2[index])) 565 { 566 return false; 567 } 568 } 569 570 return true; 571 } 572 573 checkMatch(boolean condition, Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)574 private void checkMatch(boolean condition, 575 Clazz clazz, 576 Method method, 577 CodeAttribute codeAttribute, 578 int offset, 579 Instruction instruction) 580 { 581 if (DEBUG_MORE) 582 { 583 System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)+(condition?"\t== ":"\t ")+instruction.toString(offset)); 584 } 585 586 // Did the instruction match? 587 if (condition) 588 { 589 // Remember the offset of the matching instruction. 590 matchedInstructionOffsets[patternInstructionIndex] = offset; 591 592 // Try to match the next instruction next time. 593 patternInstructionIndex++; 594 595 // Did we match all instructions in the sequence? 596 matching = patternInstructionIndex == patternInstructions.length; 597 598 // Did we match any wildcards along the way? 599 matchingAnyWildCards = matchedArgumentFlags != 0; 600 601 if (matching) 602 { 603 if (DEBUG) 604 { 605 System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 606 for (int index = 0; index < patternInstructionIndex; index++) 607 { 608 System.out.println(" "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index])); 609 } 610 } 611 612 // Start matching from the first instruction again next time. 613 reset(); 614 } 615 } 616 else 617 { 618 // The instruction didn't match. 619 matching = false; 620 621 // Is this a failed second instruction? 622 boolean retry = patternInstructionIndex == 1; 623 624 // Start matching from the first instruction next time. 625 reset(); 626 627 // Retry a failed second instruction as a first instruction. 628 if (retry) 629 { 630 instruction.accept(clazz, method, codeAttribute, offset, this); 631 } 632 } 633 } 634 } 635