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
andv1
. - Else I incremented the
.locals
directive by 2 and I usedv0
andv1
.
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
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.