Home Development for Android Learning to count in hex,or reverse-engineering an alarm clock

Learning to count in hex,or reverse-engineering an alarm clock

by admin

Introduction

Recently Ihad the ideaof learning to count in hexadecimal.As I am a lazy man, such ways, how to learn the multiplication table did not suit me.After thinking about it I remembered that every morning I solve (sometimes more than once)a simple example, to disable alarm clock.It helps little;over time, I started solving almost without waking up.So why not combine useful with useful?
It remains to choose a way of implementation.Since I don’t have the faintest idea about android development (and I’m not familiar with Java at all), and to write my own application for the sake of such a trifle was to shoot birds with a cannon, I decided to modify an existing alarm clock.
Learning to count in hex,or reverse-engineering an alarm clock
Under the roll you will find a description of the tools, the process and the result of translating the example to hex.And also an explanation of the syntax of the smali code (opcode language for the dalvik virtual machine).There are hardly any pictures and lots of text.

A little about the .apk structure

An .apk application is a zip packed file. Inside it contains the resource files, AndroidManifest.xml and classes.dex. The second one is the file which contains the basic information of the app, such as the main Activity, the list of rights requested for the installation and other information which is not important for our purpose. classes.dex is the byte code compiled for the dalvik virtual machine. We can translate it into jar and get more or less readable Java code. This is what we will do now.

Getting a readable code

So, let’s take our application file (in my case it is AlarmDroid )and unzip it. There are many ways to do this, I used Apk Manager which I will talk about below. Next, you need to convert classes.dex to jar. To do thiswe have dex2jar Drag and drop the file to dex2jar.bat. Now we have to learn how to read the resulting jar file. To do thiswe use a program called jd-gui (there is a plugin with the same namefor Eclipse, by the way).
So, we’ve got almost readable code, but decompiler makes some errors here and there.

Decompiling and reading .smali files

Since the java code is read-only, you have to fix it in the opcodes of the virtual machine. There are only a little less 256 pieces For decompiling you can use the above mentioned Apk Manager or the more console based apktool However, the first one is just a nice wrapper for the second one, with some additional features (like unpacking and selecting the compression level). After decompiling we get a folder with files with the extension .smali. These are the opcodes. They can be read with any text editor. But it would be nice to have some kind of highlighting. I used the highlighting for Notepad++ but there is also one for vim
Well, the tools are finally sorted out, it’s time to analyze the code.

First Blood

To start with, it would be nice to make the number generation up to 15 instead of 10. So let’s look through the code and find a methodto generate an example. We will have to look for it, there are a lot of classes in trees. But since the names of the methods are preserved, it’s not too hard.
Find the methodwe are looking for in the RingerActivity class.
Learning to count in hex,or reverse-engineering an alarm clock
That’s pretty much it, just generate 3 numbers and 2 signs and put the result in this.x (we need it by the way).
Now we find the same method in smali code (unfortunately we have no smali code highlighters online and I’m too lazy to write my own, so the code will be in screenshots).
Learning to count in hex,or reverse-engineering an alarm clock
(The method code goes on.)
The code is incomprehensible, but you can clearly see that the constants are set once in the beginning. So change 0xa to 0xf and try to see the changes to know that we are on the right track. Using the Apk Manager, build, sign, download to the phone (you can test it on the emulator, if you have the Android SDK), install, set the alarm for a minute ahead, wait, watch. This procedure will have to repeat a large number of times. The numbers on the screen increased, no errors occur. Good! Now it’s time to understand a little bit of the incomprehensible smali code.

Working out the smali code

Basically everything is pretty well described here I’m just going to tell you a little bit about it in Russian.

Types, methods, fields

There are two types in dalvik opcodes:primitive and reference. The primitive types have one letter (like I for int, Vfor void). here
Reference types are objects and arrays, or in other words, everything but primitive types. In code, they are denoted as

Lpackage / name / ObjectName ;

where L at the beginning means that it is an object and;is the end of the name. This entry will be equivalent to

package name ObjectName

in Java.
Arrays look like [I is a one-dimensional array of int[](in Java it would be int[]). For more dimensions, just add square brackets at the beginning. [[[I ==int[][][](the maximum number of measurements is 255). Of course, you can create arrays of reference types, an array of strings, for example, would look like this :

[ Ljava / lang / String ;

Methods are defined in a long way that includes the object containing the method, the nameof the method, the types of all parameters passed, and the type of the return value.

Lpackage / name / ObjectName ;-> MethodName ( III ) Z

In this example, Lpackage/name/ObjectName;is the object containing the method, MethodName is the name of the method, (III)is the types of parameters passed (3 ints in this case), and Z is the type of value returned (bool in this case).
Here’s another more complicated example.

method ( I [ [ IILjava / lang / String ; [ Ljava / lang / Object ; ) Ljava / lang / String ;

In Java this code would look like this :

String method ( int , int [ ] [ ] , int , String , Object [ ] )

The class fields are defined as follows :

Lpackage / name / ObjectName ;-> FieldName : Ljava / lang / String ;

It’s pretty much self-explanatory here.

Registers

In smali code all registers are 32 bit and can contain any type of value. For 64 bit values 2 registers are used.

Determining the number of registers in a method

There are two ways to define the number of registers. The .registers directive defines the total number of registers, while .locals defines the number of registers that are not method parameters.

Parameter Transfer

When you call a method, the arguments go into the last registers. That is, if a method is called with 2 arguments and 5 registers(v0-v4), the arguments go into registers v3 and v4.
The first parameter in non-static methods is always the object from which the method was called. So if you call a non-static method

LMyObject ;-> callMe ( II ) V

it passes LMyObject; before both ints, and it turns out that there are 3 parameters in the method. And if you set the total number of registers in the method to 5 (or a local number of 2), then v2 will get the object, v3 will get the first int, and v4 will get the second int.

Naming registers

There are 2 register naming schemes : a general v-scheme, and a p-scheme for the parameter registers. In the above example we will have 5 general v0-v4 registers, and 3 parameter registers p0-p2, and as mentioned above p0 and v2 (as well as p1 and v3, p2 and v4) denote the same register.
In general there are some more subtleties, but they are not important for us to change the alarm clock.

Example to hex

Now that we’ve got the smali code figured out, it would be nice to translate the example into hex. I didn’t want to write my own function, so I went to Google. And I found method Integer.toHexString().
So, let’s find the method we need

private void updateProblemView ( )
{
TextView localTextView = this problem ;
String str1 = String valueOf ( this a ) ;
StringBuilderlocalStringBuilder1 = new StringBuilder ( str1 ) append ( "" ) ;
int i = this p ;
String str2 = op2text ( i ) ;
StringBuilderlocalStringBuilder2 = localStringBuilder1. append ( str2 ) append ( "" ) ;
int j = this b ;
StringBuilderlocalStringBuilder3 = localStringBuilder2. append ( j ) append ( "" ) ;
int k = this q ;
String str3 = op2text ( k ) ;
StringBuilderlocalStringBuilder4 = localStringBuilder3. append ( str3 ) append ( "" ) ;
int m = this c ;
String str4 = m + "= " ;
localTextView. setText ( str4 ) ;
}

and its also in the smali code

method private updateProblemView ( ) V
locals 3
prologue
line 520
iget object v0, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> problem : Landroid / widget / TextView ;
new instance v1, Ljava / lang / StringBuilder ;
iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> a : I
invoke static { v2 } , Ljava / lang / String ;-> valueOf ( I ) Ljava / lang / String ;
move result object v2
invoke direct { v1, v2 } Ljava / lang / StringBuilder ;->< init > ( Ljava / lang / String ; ) V
const string v2, ""
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> p : I
invoke direct { p0, v2 } Lcom / splunchy / android / alarmclock / RingerActivity ;-> op2text ( I ) Ljava / lang / String ;
move result object v2
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
const string v2, " "
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> b : I
invoke virtual { v1, v2 } Ljava / lang / StringBuilder ;-> append ( I ) Ljava / lang / StringBuilder ;
move result object v1
const string v2, ""
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> q : I
invoke direct { p0, v2 } Lcom / splunchy / android / alarmclock / RingerActivity ;-> op2text ( I ) Ljava / lang / String ;
move result object v2
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
const string v2, ""
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> c : I
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( I ) Ljava / lang / StringBuilder ;
move result object v1
const string v2, "= "
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1
invoke virtual { v1 } , Ljava / lang / StringBuilder ;-> toString ( ) Ljava / lang / String ;
move result object v1
invoke virtual { v0, v1 } Landroid / widget / TextView ;-> setText ( Ljava / lang / CharSequence ; ) V
line 521
return void
end method

For the first number in the example, everything is simple. Replace

invoke static { v2 } , Ljava / lang / String ;-> valueOf ( I ) Ljava / lang / String ;

on

invoke static { v2 } , Ljava / lang / Integer ;-> toHexString ( I ) Ljava / lang / String ;

And that’s the end of it. The other two are a bit more complicated, they are added to the resultwithout translating to String. So let’s replace the

iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> b : I
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( I ) Ljava / lang / StringBuilder ;
move result object v1

on

iget v2, p0, Lcom / splunchy / android / alarmclock / RingerActivity ;-> b : I
invoke static { v2 } , Ljava / lang / Integer ;-> toHexString ( I ) Ljava / lang / String ;
move result object v2
invoke virtual { v1, v2 } , Ljava / lang / StringBuilder ;-> append ( Ljava / lang / String ; ) Ljava / lang / StringBuilder ;
move result object v1

(note that the type of the passed value has changed).Do the same with the third number.
When we run it, we see small letters.But we want big letters! No problem, add it before adding it to the line

invoke virtual { v2 } , Ljava / lang / String ;-> toUpperCase ( ) Ljava / lang / String ;
move result object v2

for all three numbers.

Changing input field

In general, we could stop here, counting has become an order of magnitude more complicated.But now the hex-to-dec translation is being practiced, and we keep counting in decimal.This is not what we want.So we need to convert the input field.
The first thing to finish with the update method is to add "0x" before the input field. Well, it’s really easy, just change the

const string v2, "="

on

const string v2, "=0x"

Next you need to change the input field type from numeric to text (numeric hex, unfortunately, does not provide). Such properties are stored in xml files. Here I was helped by a file search using the keywords "arithmetic", "math" and similar. In res/layout/ringer.xml we find the line

 <EditTextandroid:textSize="36.0dip"android:gravity="left"android:id="@id/math_solution"android:visibility="invisible"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@null"android:inputType="phone"/> 

google, find the list of inputType values, change it to "text".
Now we just need to learn how to convert input so that it fits with output generated during generation (RingerActivity.x, remember?). Look forand find thiscode

label1621 : label1912 : for ( boolean bool15 = false ; ; bool15 = true )
{
boolean bool16 = bool15 ;
this arithmeticProblemSolved = bool16 ;
RingerActivitylocalRingerActivity14 = this ;
int i9 = 2131099787 ;
TextViewlocalTextView4 = ( TextView ) localRingerActivity14. findViewById ( i9 ) ;
this problem = localTextView4 ;
RingerActivitylocalRingerActivity15 = this ;
int i10 = 2131099788 ;
EditText localEditText1 = ( EditText ) localRingerActivity15. findViewById ( i10 ) ;
this solution = localEditText1 ;
RingerActivitylocalRingerActivity16 = this ;
String str7 = "input_method" ;
InputMethodManagerlocalInputMethodManager = ( InputMethodManager ) localRingerActivity16. getSystemService ( str7 ) ;
this imm = localInputMethodManager ;
EditText localEditText2 = this solution ;
9 local91 = new com / splunchy / android / alarmclock / RingerActivity$ 9 ;
9 local92 = local91 ;
RingerActivity localRingerActivity17 = this ;
ImageButton localImageButton5 = localImageButton3 ;
ImageButton localImageButton6 = localImageButton1 ;
local92. < init > ( localRingerActivity17, localImageButton5, localImageButton6 ) ;
localEditText2. setOnEditorActionListener ( local91 ) ;

It’s pretty hard to understand anything, but we see some connection between solution and com/splunchy/android/alarmclock/RingerActivity$9. It turns out the latter is a smali file, which apparently jd-gui couldn’t display adequately. Go there and find the comparison method we need!

iget object v1, p0, Lcom / splunchy / android / alarmclock / RingerActivity$ 9 ;-> this$ 0 : Lcom / splunchy / android / alarmclock / RingerActivity ;
invoke static { v1 } Lcom / splunchy / android / alarmclock / RingerActivity ;-> access$ 10 ( Lcom / splunchy / android / alarmclock / RingerActivity ; ) Landroid / widget / EditText ;
move result object v1
invoke virtual { v1 } Landroid / widget / EditText ;-> getText ( ) Landroid / text / Editable ;
move result object v1
invoke interface { v1 } Landroid / text / Editable ;-> toString ( ) Ljava / lang / String ;
move result object v1
new instance v2, Ljava / lang / Integer ;
iget object v3, p0, Lcom / splunchy / android / alarmclock / RingerActivity$ 9 ;-> this$ 0 : Lcom / splunchy / android / alarmclock / RingerActivity ;
iget v3, v3, Lcom / splunchy / android / alarmclock / RingerActivity ;-> x : I
invoke direct { v2, v3 } , Ljava / lang / Integer ;->< init > ( I ) V
invoke virtual { v2 } , Ljava / lang / Integer ;-> toString ( ) Ljava / lang / String ;
move result object v2
invoke virtual { v1, v2 } , Ljava / lang / String ;-> equals ( Ljava / lang / Object ; ) Z
move resultv1

So, let’s add an uppercase translation to the line read from the input field, luckily we already learned how to do that. After that, we replace

iget v3, v3, Lcom / splunchy / android / alarmclock / RingerActivity ;-> x : I
invoke direct { v2, v3 } , Ljava / lang / Integer ;->< init > ( I ) V
invoke virtual { v2 } , Ljava / lang / Integer ;-> toString ( ) Ljava / lang / String ;
move result object v2

on

iget v2, v3, Lcom / splunchy / android / alarmclock / RingerActivity ;-> x : I
invoke static { v2 } , Ljava / lang / Integer ;-> toHexString ( I ) Ljava / lang / String ;
move result object v2

And here we also add an uppercase translation. Assemble, watch. Hurrah! Everything works.

Conclusion

A fairy tale is told sooner rather than later. Because of the lack of a debugger, testing is very difficult, it takes a couple of minutes to run an application and check its functionality, and a lot of annoying activities.
Also really wanted to redo the numeric layout of my favorite keyboard Swype For hex input. But their encrypted layout files became an insurmountable obstacle for me. =(
Here is the final version alarm clock Unfortunately it stopped updating after the modification. I have no problems with its current functionality.
Thank you for your attention, any criticism is welcome!
P.S. Hexadecimal multiplication table

You may also like