-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathGuessClassNameFromLogcatTag.py
More file actions
199 lines (164 loc) · 8.65 KB
/
GuessClassNameFromLogcatTag.py
File metadata and controls
199 lines (164 loc) · 8.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# -*- coding: utf-8 -*-
# Guess class name from original source or potential Logcat Tag. And list top 20 possible Logger/LogUtil classes.
# Author: 22s1mple
import os
import re
import sys
from collections import Counter
from com.pnfsoftware.jeb.client.api import IScript
from com.pnfsoftware.jeb.core.units.code.android.dex import IDexMethod, IDalvikInstruction, IDexClass
from com.pnfsoftware.jeb.core.units.code.java import IJavaInstanceField, IJavaElement, IJavaReturn, \
IJavaArithmeticExpression, IJavaConditionalExpression, IJavaPredicate, IJavaAssignment
from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, IClientContext
from com.pnfsoftware.jeb.core import RuntimeProjectUtil
from com.pnfsoftware.jeb.core.events import JebEvent, J
from com.pnfsoftware.jeb.core.output import AbstractUnitRepresentation, UnitRepresentationAdapter
from com.pnfsoftware.jeb.core.units.code import ICodeUnit, ICodeItem, DecompilationOptions, IDecompilerUnit, \
DecompilationContext
from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit, IJavaStaticField, IJavaNewArray, IJavaConstant, \
IJavaCall, IJavaField, IJavaMethod, IJavaClass
from com.pnfsoftware.jeb.core.actions import ActionTypeHierarchyData
from com.pnfsoftware.jeb.core.actions import ActionRenameData
from com.pnfsoftware.jeb.core.util import DecompilerHelper
from com.pnfsoftware.jeb.core.output.text import ITextDocument
from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, IDexDecompilerUnit
from com.pnfsoftware.jeb.core.actions import ActionOverridesData
from com.pnfsoftware.jeb.core.units import UnitUtil
from com.pnfsoftware.jeb.core.units import UnitAddress
from com.pnfsoftware.jeb.core.actions import Actions, ActionContext, ActionCommentData, ActionRenameData, \
ActionXrefsData
def checkClassName(rawstr):
if rawstr and any([c.islower() for c in rawstr]):
return re.match(r'^[A-Z]+[0-9a-zA-Z]*$', rawstr, flags=0)
else:
return None
Black_list = [
"Ljava/lang/String;",
"Landroid/text/TextUtils;",
"Ljavax/net/ssl/SSLContext;",
"Ljava/util/regex/Pattern;",
]
Conflict_clzname_pool = None
Possible_Logger = None
Possible_Clzname = None
class GuessClassNameFromLogcatTag(IScript):
def run(self, ctx):
self.ctx = ctx
if not isinstance(self.ctx, IGraphicalClientContext):
print ('This script must be run within a graphical client')
return
engctx = ctx.getEnginesContext()
if not engctx:
print('Back-end engines not initialized')
return
projects = engctx.getProjects()
if not projects:
print('There is no opened project')
self.prj = projects[0]
self.Dexunits = RuntimeProjectUtil.findUnitsByType(self.prj, IDexUnit, False)
for unit in self.Dexunits:
assert isinstance(unit, IDexUnit)
global Conflict_clzname_pool, Possible_Logger, Possible_Clzname
Possible_Logger = Counter()
Conflict_clzname_pool = Counter()
for clz in unit.getClasses():
assert isinstance(clz, IDexClass)
clzSig = clz.getSignature()
sourceIndex = clz.getSourceStringIndex()
clazzAddress = clz.getAddress()
if sourceIndex == -1 or '$' in clazzAddress: # Do not rename inner class
self.guess_classname_from_logcat_tag(unit, clz)
continue
sourceStr = str(unit.getString(sourceIndex))
if not sourceStr.strip() or sourceStr.lower() == "proguard" or sourceStr.lower() == "sourcefile":
self.guess_classname_from_logcat_tag(unit, clz)
continue
x = sourceStr.rfind(".")
if x > 0: sourceStr = sourceStr[:x] # .java .kt
sourceStr = sourceStr.replace(".", "_").replace("-", "_")
if clz.getName(True) != sourceStr:
self.comment_class(unit, clz, clz.getName(True)) # Backup origin clazz name to comment
self.rename_class(unit, clz, sourceStr) # Rename to source name
# Todo:
# 1. Need check name conflict.
# 2. Since source string is unreliable either, need extra judgement.
print(u"\nPossible logger class: ")
for x, t in Possible_Logger.most_common(20):
print("{:<8d}".format(t) + x)
def guess_classname_from_logcat_tag(self, unit, clazz):
if "$" in clazz.getSignature(): return
global Conflict_clzname_pool, Possible_Logger, Possible_Clzname
Possible_Clzname = Counter()
for mtd in clazz.getMethods():
assert isinstance(mtd, IDexMethod)
dexCodeItem = mtd.getData().getCodeItem()
if not dexCodeItem: continue
insts = dexCodeItem.getInstructions()
register_map = {}
for inst in insts:
assert isinstance(inst, IDalvikInstruction)
# Get the Dalvik instruction opcode, as defined in DalvikInstructionOpcodes.
# https://developer.android.com/reference/dalvik/bytecode/Opcodes.html
# com.pnfsoftware.jeb.core.units.code.android.dex.DalvikInstructionOpcodes
opcode = inst.getOpcode()
if opcode in [26, 27]: # OP_CONST_STRING & OP_CONST_STRING_JUMBO
pl = inst.getParameters()
idx = pl[1].getValue()
register_map[int(pl[0].getValue())] = idx
if register_map and opcode in [113, 9727]: # OP_INVOKE_STATIC & OP_INVOKE_STATIC_JUMBO
pl = inst.getParameters()
if len(pl) != 3 or pl[1].getType() != 0:
register_map = {}
continue
call_mtd = unit.getMethod(pl[0].getValue())
if not call_mtd or any([call_mtd.getSignature().startswith(x) for x in Black_list]):
register_map = {}
continue
u = register_map.get(pl[1].getValue())
if not u:
register_map = {}
continue
try:
name = str(unit.getString(u))
except Exception as e:
register_map = {}
continue
if checkClassName(name):
Possible_Clzname.update([name])
posslogername = call_mtd.getSignature().split("->")[0]
Possible_Logger.update([posslogername])
register_map = {}
for name, times in Possible_Clzname.most_common(1): # Simply pick the highest frequency one
suffix = str(Conflict_clzname_pool[name])
if suffix == "0": suffix = ""
guess_clz_name = name + suffix
# print('rename %s to %s success!' % (clazz.getAddress(), guess_clz_name))
original_name = self.rename_class(unit, clazz, guess_clz_name)
self.comment_class(unit, clazz, original_name)
Conflict_clzname_pool.update([name])
def rename_class(self, unit, originClazz, sourceName):
actCtx = ActionContext(unit, Actions.RENAME, originClazz.getItemId(), originClazz.getAddress())
actData = ActionRenameData()
actData.setNewName(sourceName)
if unit.prepareExecution(actCtx, actData):
try:
originalName = actData.getOriginalName()
if len(originalName) > 10: # Skip for general cases: already have meaningful class name
return sourceName
result = unit.executeAction(actCtx, actData)
if result:
print('rename %s to %s success!' % (originalName, originClazz.getAddress()))
else:
print('rename to %s failed!' % sourceName)
except Exception, e:
print (Exception, e)
return originalName
def comment_class(self, unit, originClazz, commentStr):
actCtx = ActionContext(unit, Actions.COMMENT, originClazz.getItemId(), originClazz.getAddress())
actData = ActionCommentData()
actData.setNewComment(commentStr)
if unit.prepareExecution(actCtx, actData):
try:
result = unit.executeAction(actCtx, actData)
except Exception, e:
print (Exception, e)