From 9f142781b06c62f263e66191f3c4a698cdb94b8a Mon Sep 17 00:00:00 2001 From: tiziw Date: Wed, 12 Feb 2020 11:35:01 +0100 Subject: [PATCH] Rewrite regex search so that it pattern checks each instruction without building a string of the whole method first. Regex duration on mobile cpu (i5-7200U) goes down from 2635ms to 430ms for one specific test case. --- .../searching/RegexInsnFinder.java | 109 ++++++++++++------ .../bytecodeviewer/searching/RegexSearch.java | 17 +-- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/src/the/bytecode/club/bytecodeviewer/searching/RegexInsnFinder.java b/src/the/bytecode/club/bytecodeviewer/searching/RegexInsnFinder.java index 34302249..6afa92f8 100644 --- a/src/the/bytecode/club/bytecodeviewer/searching/RegexInsnFinder.java +++ b/src/the/bytecode/club/bytecodeviewer/searching/RegexInsnFinder.java @@ -255,48 +255,83 @@ public class RegexInsnFinder { } offsets[i] = insnString.length(); insnString += opcodes[ain.getOpcode()]; - switch (ain.getType()) { - case AbstractInsnNode.INT_INSN: - final IntInsnNode iin = (IntInsnNode) ain; - insnString += "{" + iin.operand + "}"; - break; - case AbstractInsnNode.LDC_INSN: - final LdcInsnNode lin = (LdcInsnNode) ain; - insnString += "{" + lin.cst.toString().replace("}", "\\}") - + "}"; - break; - case AbstractInsnNode.VAR_INSN: - final VarInsnNode vin = (VarInsnNode) ain; - insnString += "_" + vin.var; - break; - case AbstractInsnNode.IINC_INSN: - final IincInsnNode iiin = (IincInsnNode) ain; - insnString += "{" + iiin.var + "," + iiin.incr + "}"; - break; - case AbstractInsnNode.FIELD_INSN: - final FieldInsnNode fin = (FieldInsnNode) ain; - insnString += "{" + fin.desc + "," + fin.owner + "," - + fin.name + "}"; - break; - case AbstractInsnNode.METHOD_INSN: - final MethodInsnNode min = (MethodInsnNode) ain; - insnString += "{" + min.desc + "," + min.owner + "," - + min.name + "}"; - break; - case AbstractInsnNode.TYPE_INSN: - final TypeInsnNode tin = (TypeInsnNode) ain; - insnString += "{" + tin.desc + "}"; - break; - case AbstractInsnNode.MULTIANEWARRAY_INSN: - final MultiANewArrayInsnNode manain = (MultiANewArrayInsnNode) ain; - insnString += "{" + manain.dims + "," + manain.desc + "}"; - break; - } + insnString = getInsString(ain); insnString += " "; } } } + // Do a pattern check against each instruction directly, + // without building a string of the whole method. + public static boolean staticScan(ClassNode node, MethodNode mn, Pattern pattern) { + final List il = new ArrayList(); + for (final AbstractInsnNode ain : mn.instructions.toArray()) + if (ain.getOpcode() >= 0) { + il.add(ain); + } + return il.stream().anyMatch(ain -> { + if (ain.getOpcode() >= 0) { + if (ain.getOpcode() >= opcodes.length) { + try { + throw new UnexpectedException( + "Unknown opcode encountered: " + + ain.getOpcode()); + } catch (final UnexpectedException e) { + new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e); + } + } + String insnString = getInsString(ain); + boolean result = pattern.matcher(insnString).find(); + if(result) { + return true; + } + } + return false; + }); + } + + private static String getInsString(AbstractInsnNode ain) { + String insnString = ""; + switch (ain.getType()) { + case AbstractInsnNode.INT_INSN: + final IntInsnNode iin = (IntInsnNode) ain; + insnString += "{" + iin.operand + "}"; + break; + case AbstractInsnNode.LDC_INSN: + final LdcInsnNode lin = (LdcInsnNode) ain; + insnString += "{" + lin.cst.toString().replace("}", "\\}") + + "}"; + break; + case AbstractInsnNode.VAR_INSN: + final VarInsnNode vin = (VarInsnNode) ain; + insnString += "_" + vin.var; + break; + case AbstractInsnNode.IINC_INSN: + final IincInsnNode iiin = (IincInsnNode) ain; + insnString += "{" + iiin.var + "," + iiin.incr + "}"; + break; + case AbstractInsnNode.FIELD_INSN: + final FieldInsnNode fin = (FieldInsnNode) ain; + insnString += "{" + fin.desc + "," + fin.owner + "," + + fin.name + "}"; + break; + case AbstractInsnNode.METHOD_INSN: + final MethodInsnNode min = (MethodInsnNode) ain; + insnString += "{" + min.desc + "," + min.owner + "," + + min.name + "}"; + break; + case AbstractInsnNode.TYPE_INSN: + final TypeInsnNode tin = (TypeInsnNode) ain; + insnString += "{" + tin.desc + "}"; + break; + case AbstractInsnNode.MULTIANEWARRAY_INSN: + final MultiANewArrayInsnNode manain = (MultiANewArrayInsnNode) ain; + insnString += "{" + manain.dims + "," + manain.desc + "}"; + break; + } + return insnString; + } + public void setMethod(final ClassNode ci, final MethodNode mi) { this.mn = mi; refresh(); diff --git a/src/the/bytecode/club/bytecodeviewer/searching/RegexSearch.java b/src/the/bytecode/club/bytecodeviewer/searching/RegexSearch.java index 94c40eaa..aeba614f 100644 --- a/src/the/bytecode/club/bytecodeviewer/searching/RegexSearch.java +++ b/src/the/bytecode/club/bytecodeviewer/searching/RegexSearch.java @@ -2,6 +2,7 @@ package the.bytecode.club.bytecodeviewer.searching; import java.awt.GridLayout; import java.util.Iterator; +import java.util.regex.Pattern; import javax.swing.JLabel; import javax.swing.JPanel; @@ -12,6 +13,8 @@ import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; import the.bytecode.club.bytecodeviewer.util.FileContainer; +import static the.bytecode.club.bytecodeviewer.searching.RegexInsnFinder.processRegex; + /*************************************************************************** * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com * @@ -71,21 +74,13 @@ public class RegexSearch implements SearchTypeDetails { if (srchText.isEmpty()) return; - + Pattern pattern = Pattern.compile(processRegex(srchText), + Pattern.MULTILINE); while (methods.hasNext()) { final MethodNode method = methods.next(); - if (regexFinder == null) - { - regexFinder = new RegexInsnFinder(node, method); - } - else - { - regexFinder.setMethod(node, method); - } - - if (regexFinder.find(srchText).length > 0) + if (RegexInsnFinder.staticScan(node, method, pattern)) { String desc2 = method.desc; try