ASTRemoveDummySwitch.py
# JEB script - demo AST API
# Remove dummy switches
# See www.android-decompiler.com/blog
import sys
import os
import time
from jeb.api import IScript
from jeb.api import EngineOption
from jeb.api.ui import View
from jeb.api.dex import Dex
from jeb.api.ast import SwitchStm, Compound, Constant, Goto, Label, Optimizer
class ASTRemoveDummySwitch(IScript):
def run(self, jeb):
self.jeb = jeb
self.dex = self.jeb.getDex()
# change or allow user-input or caret-based selection
msig = 'Lcom/example/android/snake/TileView;->(Landroid/content/Context;Landroid/util/AttributeSet;I)V'
r = jeb.decompileMethod(msig)
m = jeb.getDecompiledMethodTree(msig)
self.checkBlock(m.getBody())
opt = Optimizer(jeb, m)
opt.removeUselessGotos()
opt.removeUnreferencedLabels()
def checkBlock(self, block, level=0):
i = 0
while i < block.size():
stm = block.get(i)
#print '%s%s' % (' '*level, stm)
if isinstance(stm, SwitchStm):
gotostm = self.checkDummySwitch(stm)
if gotostm:
block.set(i, gotostm)
print '-> Switch replaced by goto %s' % gotostm.getLabel().getName()
label = gotostm.getLabel()
r = self.findFirstLabel(block, i + 1, level)
if r and r[1] == level and r[0].getName() == label.getName():
print '-> Removing [%d, %d)' % (i + 1, r[2])
j = i + 1
cnt = r[2] - j
while cnt > 0:
block.remove(j)
cnt -= 1
elif isinstance(stm, Compound):
for b in stm.getBlocks():
self.checkBlock(b, level+1)
i += 1
def findFirstLabel(self, block, begin=0, level=0):
i = begin
while i < block.size():
stm = block.get(i)
if isinstance(stm, Label):
return (stm, level, i)
elif isinstance(stm, Compound):
for b in stm.getBlocks():
r = self.findFirstLabel(b, 0, level+1)
if r:
return r
i += 1
return None
def checkDummySwitch(self, sw):
val = sw.getSwitchedExpression()
if not isinstance(val, Constant):
return None
val = val.getInt()
b = sw.getCaseBody(val)
if not b or b.size() != 1:
return None
stm0 = b.get(0)
if not isinstance(stm0, Goto):
return None
label = stm0.getLabel()
return stm0