Since TowelRoot's code is not publicly available, I resorted to decompile the latest APK, downloaded from the TowelRoot website, in order to investigate the app's need for the INTERNET permission.
Relevant notes
Upon successful decompilation, it turned out that neither the activities nor the code itself be obfuscated. The developer certainly had anticipated that this app would've been investigated, yet he had left the code to be as much transparent as possible.
The most important file we have at this point is TowelRoot.smali
, which describes all the workings of this app.
Note: depending on your particular version of ApkTool, the line numbers may vary.
Initial structure
In lines 13-22
, the code initializes a field called fingerprint
. The relevant code follows:
.field fingerprint:Ljava/util/List;
.annotation system Ldalvik/annotation/Signature;
value = {
"Ljava/util/List",
"<",
"Lorg/apache/http/NameValuePair;",
">;"
}
.end annotation
.end field
which is a Java list composed of key-value pairs, to be used when collecting the relevant info from the phone.
Data collected
In lines 56-215
, the code describes a method called fillInFingerprint
, which is responsible for retrieving the appropriate data to be sent to the TowelRoot website, in order to ascertain whether the device can be rooted via the homonymous exploit. This method makes an extensive use of the abovementioned fingerprint
field. The collected data follows.
Device model
The visible device model. Lines 70-80
:
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "model"
sget-object v7, Landroid/os/Build;->MODEL:Ljava/lang/String;
invoke-direct {
v5, v6, v7
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
Device fingerprint
An unique identifier for the OS build. Lines 83-93
:
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "fingerprint"
sget-object v7, Landroid/os/Build;->FINGERPRINT:Ljava/lang/String;
invoke-direct {
v5, v6, v7
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
Device hardware
Likely the processor board. Lines 96-106
:
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "hardware"
sget-object v7, Landroid/os/Build;->HARDWARE:Ljava/lang/String;
invoke-direct {
v5, v6, v7
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
Device serial
Device's unique serial number. Lines 109-119
:
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "serial"
sget-object v7, Landroid/os/Build;->SERIAL:Ljava/lang/String;
invoke-direct {
v5, v6, v7
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
Device kernel version
Vital since the exploit is likely to have been patched in the recent kernels. Lines 122-134
:
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "kernel"
invoke-virtual {
p0
}
, Lcom/geohot/towelroot/TowelRoot;->javaSucksAssReadTheKernelVersion()Ljava/lang/String;
move-result-object v7
invoke-direct {
v5, v6, v7
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
TowelRoot version
Lines 168-180
:
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "appversion"
invoke-direct {
p0
}
, Lcom/geohot/towelroot/TowelRoot;->getSoftwareVersion()Ljava/lang/String;
move-result-object v7
invoke-direct {
v5, v6, v7
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
Modstring
The string modstring
. Lines 202-211
:
.local v1, "modstring":Ljava/lang/String;
iget-object v4, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
new-instance v5, Lorg/apache/http/message/BasicNameValuePair;
const-string v6, "modstring"
invoke-direct {
v5, v6, v1
}
, Lorg/apache/http/message/BasicNameValuePair;-><init>(Ljava/lang/String;Ljava/lang/String;)V
invoke-interface {
v4, v5
}
, Ljava/util/List;->add(Ljava/lang/Object;)Z
The server request
In lines 227-329
the code describes the method queryServer
, which queries the server's database in order to find a match with the collected data, and handles the possible exceptions in the code. Such data is transmitted to the URL https://towelroot.appspot.com/report/
via SSL and a POST request (lines 263-275
):
.local v2, "httppost":Lorg/apache/http/client/methods/HttpPost;
new-instance v5, Lorg/apache/http/client/entity/UrlEncodedFormEntity;
iget-object v6, p0, Lcom/geohot/towelroot/TowelRoot;->fingerprint:Ljava/util/List;
invoke-direct {
v5, v6
}
, Lorg/apache/http/client/entity/UrlEncodedFormEntity;-><init>(Ljava/util/List;)V
invoke-virtual {
v2, v5
}
, Lorg/apache/http/client/methods/HttpPost;->setEntity(Lorg/apache/http/HttpEntity;)V
.line 88
invoke-interface {
v1, v2
}
, Lorg/apache/http/client/HttpClient;->execute(Lorg/apache/http/client/methods/HttpUriRequest;)Lorg/apache/http/HttpResponse;
move-result-object v3
make it ra1n
The method buttonClicked
(lines 333-426
) contains the code that makes the app work when you push the button. Amongst others, it calls the methods fillInFingerprint
to gather the required info, and queryServer
to see if the device can be rooted.
It then proceeds by analyzing the server's response. If said response is empty, TowelRoot prompts you about the necessity of an Internet connection (lines 369-378
):
invoke-virtual {
v0, v2
}
, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v2
if-eqz v2, :cond_1
.line 115
const-string v2, "Please ensure you are connected to the internet"
invoke-virtual {
v1, v2
}
, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V
If the response is not empty, then TowelRoot proceeds by checking if the abovementioned equals the string nyet
(lines 391-402
):
const-string v2, "nyet"
invoke-virtual {
v0, v2
}
, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v2
if-eqz v2, :cond_2
.line 117
const-string v2, "This phone isn\'t currently supported"
invoke-virtual {
v1, v2
}
, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V
If the response equals nyet
, then the phone isn't officially supported.
Finally, if the response is both non-empty and different from nyet
, TowelRoot starts the rooting procedure by calling the native method rootTheShit
, defined at line 448
and probably contained inside the library libexploit.so
.
For the brave ones
TowelRoot includes a special mode for users with unsupported devices. This mode, whose code is defined inside the method titleClicked
(lines 451-504
), is accessible by tapping the red welcome to towelroot v3
title inside the app, and will allow the user to try different modstrings in order to try to root the device anyway.