Disassembly - A Simple javap CloneΒΆ
Javap is the defacto Java disassembler and is included when you installed the Oracle JDK. We can make a simple clone very easily using Jawa:
import click
from jawa.classloader import ClassLoader
from jawa.util.bytecode import OperandTypes
@click.command()
@click.option('--class-path', multiple=True, type=click.Path(exists=True))
@click.argument('classes', nargs=-1)
def main(class_path, classes):
loader = ClassLoader(*class_path)
for class_ in classes:
cf = loader[class_]
# The constant pool.
print('; {0:->60}'.format(' constant pool'))
print('; {0:->60}'.format(
' total: {0}'.format(len(cf.constants))
))
for constant in cf.constants:
print('; {0:04}: {1!r}'.format(constant.index, constant))
# The fields table.
print('; {0:->60}'.format(' fields'))
print('; {0:->60}'.format(
' total: {0}'.format(len(cf.fields))
))
for field in cf.fields:
print('; {0!r}'.format(field))
# The methods table.
print('; {0:->60}'.format(' methods'))
print('; {0:->60}'.format(
' total: {0}'.format(len(cf.methods))
))
for method in cf.methods:
# Find all enabled flags and print them out (such as acc_public
# and acc_private)
flags = method.access_flags.to_dict()
flags = [k for k, v in flags.items() if v]
print('{0} {1} {2}({3}) {{'.format(
' '.join(flags),
method.returns.name,
method.name.value,
', '.join(a.name + ('[]' * a.dimensions) for a in method.args)
).strip())
r = []
if method.code:
for ins in method.code.disassemble():
line = [
f'{ins.pos:04}',
f'[0x{ins.opcode:02X}]',
f'{ins.mnemonic:>15} <-'
]
for operand in ins.operands:
if isinstance(operand, dict):
line.append(f'JT[{operand!r}]')
continue
line.append({
OperandTypes.CONSTANT_INDEX: f'C[{operand.value}]',
OperandTypes.BRANCH: f'J[{operand.value}]',
OperandTypes.LITERAL: f'#[{operand.value}]',
OperandTypes.LOCAL_INDEX: f'L[{operand.value}]',
OperandTypes.PADDING: 'P'
}[operand.op_type])
print(' ' + ' '.join(line))
print('}')
if __name__ == '__main__':
main()
If we try this out on a HelloWorld class, our output will look like:
$ python disassemble.py --class-path ../tests/data HelloWorld develop
; ---------------------------------------------- constant pool
; -------------------------------------------------- total: 21
; 0001: <UTF8(index=1, value='HelloWorld'>)
; 0002: <ConstantClass(index=2, name=<UTF8(index=1, value='HelloWorld'>))>
...
; ----------------------------------------------------- fields
; --------------------------------------------------- total: 0
; ---------------------------------------------------- methods
; --------------------------------------------------- total: 1
acc_public acc_static void main(java/lang/String[]) {
0000 [0xB2] getstatic <- C[13]
0003 [0x12] ldc <- C[15]
0005 [0xB6] invokevirtual <- C[21]
0008 [0xB1] return <-
}