Thursday, December 16, 2010

Patch a Java class file !


Hi !

I'm currently working on my watermark algorithm (opensource !) on java/android applications. The idea of this watermark is in my head (and on a sheet of paper;) ), and in the coming months, I will describe it in a paper, and I will provide usefull integration with ANT.

One problem in the algorithm is to know where to insert new instructions into a method from a java class. For example, you can insert easily new instructions manually (please, you must update to latest version of the mercurial repository, and install ipython) :

pouik@camelot:~/androguard$ ./androlyze.py -s
Androlyze version ALPHA 0-update3

|2>TEST = './examples/java/test/orig/Test1.class'
|3>_a = AndroguardS( TEST )
|4>x = _a.get_vm()
|5>m = x.get_method(".init.")[0]
|6>m.show()
********************************************************************************
MethodInfo(access_flags=1, name_index=24, descriptor_index=25, attributes_count=1) init ()V
AttributeInfo(attribute_name_index=26, attribute_length=54) Code
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
LOW(max_stack=2, max_locals=1, code_length=18)
'*\xb7\x00\x01*\x10d\xb5\x00\x02*\x11\x00\xc8\xb5\x00\x03\xb1'
0 0 aload_0
1 1 invokespecial ['java/lang/Object', 'init', '()V']
2 4 aload_0
3 5 bipush [100]
4 7 putfield ['Test1', 'value', 'I']
5 10 aload_0
6 11 sipush 200
7 14 putfield ['Test1', 'value2', 'I']
8 17 return
ATTRIBUTES_COUNT 0x1
AttributeInfo(attribute_name_index=27, attribute_length=18) LineNumberTable
LINE_NUMBER_TABLE_LENGTH 0x4
LineNumberTable(start_pc=0, line_number=7)
LineNumberTable(start_pc=4, line_number=8)
LineNumberTable(start_pc=10, line_number=9)
LineNumberTable(start_pc=17, line_number=10)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
********************************************************************************
|7>c = m.get_code()
|8>c.inserts_at( c.get_relative_idx( 4 ), [['aload_0'], ['bipush', 0], ['putfield', 'value', 'I']] )
Out[8]: 6
|9>c.show()
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
LOW(max_stack=2, max_locals=1, code_length=24)
'*\xb7\x00\x01*\x10d\xb5\x00\x02*\x11\x00\xc8\xb5\x00\x03\xb1'
0 0 aload_0
1 1 invokespecial ['java/lang/Object', 'init', '()V']
2 4 aload_0
3 5 bipush [100]
4 7 putfield ['Test1', 'value', 'I']
5 10 aload_0
6 11 bipush [0]
7 13 putfield ['Test1', 'value', 'I']
8 16 aload_0
9 17 sipush 200
10 20 putfield ['Test1', 'value2', 'I']
11 23 return
ATTRIBUTES_COUNT 0x1
AttributeInfo(attribute_name_index=27, attribute_length=18) LineNumberTable
LINE_NUMBER_TABLE_LENGTH 0x4
LineNumberTable(start_pc=0, line_number=7)
LineNumberTable(start_pc=4, line_number=8)
LineNumberTable(start_pc=10, line_number=9)
LineNumberTable(start_pc=17, line_number=10)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



In this case, it's a static context and you know where to insert instructions without breaking anything. But if you would like to do the same thing automatically, it's more difficult. So I implemented a module called "analysis" which performs an automatic analysis of the class to search automatically correct offsets by using the stack context.
|11>y = analysis( x )
|12>for method in x.get_methods() :
....: g = y.hmethods[ method ]
....: print method.get_class_name(), method.get_name(), method.get_descriptor()
....: for i in g.basic_blocks.get() :
....: print "\t %s %x %x" % (i.name, i.start, i.end), i.ins[-1].get_name(), '[ CHILDS = ', ', '.join( "%x-%x-%s" % (j[0], j[1], j[2].get_name()) for j in i.childs ), ']', '[ FATHERS = ', ', '.join( j[2].get_name() for j in i.fathers ), ']', i.free_blocks_offsets

Test1 init ()V
init-BB@0x0 0 18 return [ CHILDS = ] [ FATHERS = ] [0, 4, 10, 16, 23]
Test1 test_base (II)I
test_base-BB@0x0 0 35 if_icmpge [ CHILDS = 35-36-test_base-BB@0x35, 35-5b-test_base-BB@0x5b ] [ FATHERS = test_base-BB@0x35 ] [0, 2, 42, 45]
test_base-BB@0x35 35 5b goto [ CHILDS = 5b-2d-test_base-BB@0x0 ] [ FATHERS = test_base-BB@0x0 ] [53, 65, 78, 85, 88]
test_base-BB@0x5b 5b 62 ifle [ CHILDS = 62-63-test_base-BB@0x62, 62-67-test_base-BB@0x62 ] [ FATHERS = test_base-BB@0x0 ] [91]
test_base-BB@0x62 62 7c lookupswitch [ CHILDS = 7c-84-test_base-BB@0x7c, 7c-7c-test_base-BB@0x7c ] [ FATHERS = test_base-BB@0x5b, test_base-BB@0x5b ] [98, 103]
test_base-BB@0x7c 7c ac tableswitch [ CHILDS = ac-c4-test_base-BB@0xac, ac-ac-test_base-BB@0xac, ac-b4-test_base-BB@0xac, ac-bc-test_base-BB@0xac ] [ FATHERS = test_base-BB@0x62, test_base-BB@0x62 ] [124, 132, 140]
test_base-BB@0xac ac c6 ireturn [ CHILDS = ] [ FATHERS = test_base-BB@0x7c, test_base-BB@0x7c, test_base-BB@0x7c, test_base-BB@0x7c ] [172, 180, 188, 196]
Test1 pouet ()I
pouet-BB@0x0 0 7 ireturn [ CHILDS = ] [ FATHERS = ] [0, 5]
Test1 pouet2 ()I
pouet2-BB@0x0 0 3 ireturn [ CHILDS = ] [ FATHERS = ] [0]
Test1 pouet3 ()I
pouet3-BB@0x0 0 3 ireturn [ CHILDS = ] [ FATHERS = ] [0]
Test1 test1 (I)I
test1-BB@0x0 0 f ireturn [ CHILDS = ] [ FATHERS = ] [0, 3]
Test1 go ()I
go-BB@0x0 0 22 ireturn [ CHILDS = ] [ FATHERS = ] [0, 32]


So, you can see that you have the CFG and each basic block you can access to the field "free_block_offsets" to have the list of available insertions !! For example, in the "init" method, you have [0, 4, 10, 16, 23].

Now you know where to patch your java class file !

This module is under development but you can use it, and I will describe in next posts how to use it.

That's all folks !

No comments:

Post a Comment