Smali best place to inject code

2.3k views Asked by At

I am making a python script to add a trace to each method to be able to get a runtime method call in logcat.

My application crashes all the time, don't ask me to copy you the error log because that is not the point of my question. Actually I try to inject my code just right after the register declaration: .locals

The first time I used the .registers directives but got errors because of the aliasing of local and parameters registers.

I thought then that I could use the .locals directive instead but it's exactly the same.

The different test that I made were the following ones:

  • If the difference between the local and the parameters registers were greater than 2 I use v0 and v1.
  • Else I incremented the .locals directive by 2 and I used v0 and v1.

But I keep getting VFY errors.

Why sometimes .locals equals 0 but there is parameters like p0 for example. p0 should be aliased by v0 but .locals is 0, so why if I change this by .locals 2 and just use v0 and v1 I still get VFY?

I am thinking to add my code just before the return directive at least it will not matter if I change a local variable as long as it is not the return variable

edit: @JesusFreke thank you for your good comments.

I am currently trying to improve my python script with your suggestion. So I created a CustomClass that I copy in the root folder but the fact is that I loop throughout all the methods in the root folder, I get the class and method name that I store into variables then I change the value of the parameters of this function and I invoke it inside each method.

But the fact is that it cannot work because value of the parameters of the static function change each time I enter a new method and at the end it will only keep the value of the last method I entered in.

In this case I will need to generate as many static functions as I have methods in the smali folder which is around 40.000...

This is part of my code:

def edit_custom_class(custom_class_path, my_tag, my_message):
with open(custom_class_path, "r+") as file: 
    for line in file:
        if ('const-string p0' in line):
            file.write('\tconst-string p0, "{0}" \n' .format(my_tag))

        elif ('const-string p1' in line):
            file.write('\tconst-string p1, "{0}" \n' .format(my_message))

        else:
            file.write(line + '\n')


def process_file(file_path, custom_class_path, my_tag, file_metadata):
is_inside = False
valid_registers = []

with open(file_path, "r+") as file: 
    for line in file:
        # we get the data concerning the method and mark it as a treated method
        if (('.method' in line) and (helper.is_valid_class_or_method_directive(line)) and (is_inside == False)):
            is_inside = True
            method_data = get_method_data(helper.get_class_or_method_name(line), file_metadata)
            my_message= (method_data[0] + '->' + method_data[1])
            file.write(line + '\n')

        elif (('return' in line) and (is_inside == True) and (method_data[4] == False)):    
            edit_custom_class(custom_class_path, my_tag, my_message)
            file.write('\t# has been edited by smali-rmc-interceptor on {0} \n' .format(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())))
            file.write('\t# start editing \n')
            file.write('\tinvoke-static, {0};->e(Ljava/lang/String;Ljava/lang/String;)I \n' .format(custom_class_path))
            file.write('\t# end editing \n')
            file.write(line + '\n') 

        elif (('.end method' in line) and (is_inside == True) and (method_data[4] == False)):
            is_inside = False
            method_data = []
            file.write(line + '\n')

        else:
            file.write(line + '\n') 

And my CustomClass content:

.class public LCustomClass;
.source "CustomClass.java"

.method public static add_trace()V
    .locals 0
    .parameter "tag"
    .parameter "message"

    .prologue   
    .line 10
    const-string p0, "my_tag"

    const-string p1, "my_message"

    .line 15
    invoke-static {p0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    .line 18
    return-void
.end method
1

There are 1 answers

4
JesusFreke On

In general, it's easiest to avoid having to allocate new registers in an existing method. This introduces a whole slew of problems due to the register limitations of many instructions.

Your best bet is to create a separate static helper method that accepts some values and prints them out or whatever it is you're wanting to do, and then simply inject a static method call in the method that you want to instrument, without allocating any new registers.