先日のblog articleで署名を突破できないという指摘を受けました。当然の指摘です。署名を検証するアプリケーション配布サイトですと問題はないのでしょう。
結局は自分が浅はかであることを実感してソースコードがないandroid アプリをいじることができるかどうか,試してみました。ただ,既に知られている情報をただ単に追っただけなので,新規情報量はないです。
参考
まずは,"Push Me"というボタンを押すと,Toastで"Hello World"というmessageを表示するアプリケーションを用意しました。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button)findViewById(R.id.Button01);
}
private class ButtonOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (v.getId() == R.id.Button01) {
Toast.makeText(getApplicationContext(), "Hello World!", Toast.LENGTH_LONG).show();
}
}
}
一方,"Push Me"というボタンを押すと,Toastで"Hello World"というmessageを表示すると同時に,call_log を読むアプリケーションを用意しました。同様に,キーとなるコード部分を下に記しておきます。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button b = (Button)findViewById(R.id.Button01);
b.setOnClickListener(new ButtonOnClickListener());
}
private class ButtonOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (v.getId() == R.id.Button01) {
Toast.makeText(getApplicationContext(), "Hello World!", Toast.LENGTH_LONG).show();
queryContentProvidersInfo();
}
}
private void queryContentProvidersInfo() {
String[] projections = new String[] {
CallLog.Calls._ID,
CallLog.Calls.NUMBER,
CallLog.Calls.TYPE,
CallLog.Calls.DURATION,
CallLog.Calls.DATE,
CallLog.Calls.CACHED_NAME,
};
Uri calls = CallLog.Calls.CONTENT_URI;
ContentResolver cr = getContentResolver();
Cursor c = cr.query(calls, projections, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);
if (c != null) {
getColumnData(c);
c.close();
}
}
private void getColumnData(Cursor c) {
if (c.moveToFirst()) {
String number;
int numberColumn = c.getColumnIndex(CallLog.Calls.NUMBER);
String cashed_name;
int cashed_nameColumn = c.getColumnIndex(CallLog.Calls.NUMBER);
do {
number = c.getString(numberColumn);
cashed_name = c.getString(cashed_nameColumn);
Log.d(TAG, "number: " + number);
if (cashed_name != null) {
Log.d(TAG, "cashed_name: " + cashed_name);
}
} while (c.moveToNext());
}
}
}
apkからbaksmaliを用いて,この部分の処理をdiassembleしたsmali型式コードを以下に貼り付けます。
単にmessageを表示する箇所。
# virtual methods
.method public final onClick(Landroid/view/View;)V
.registers 5
invoke-virtual {p1}, Landroid/view/View;->getId()I
move-result v0
const/high16 v1, 0x7f05
if-ne v0, v1, :cond_9b
iget-object v0, p0, Lcom/damburisoft/app/android/helloworld/a;->a:Lcom/damburisoft/app/android/helloworld/HelloWorldActivity;
invoke-virtual {v0}, Lcom/damburisoft/app/android/helloworld/HelloWorldActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v0
const-string v1, "Hello World!"
const/4 v2, 0x1
invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
:cond_9b
return-void
.end method
call_logを読む箇所。
# virtual methods
.method public final onClick(Landroid/view/View;)V
.registers 10
const/4 v3, 0x0
const/4 v4, 0x1
const-string v7, "ReadInfo"
const-string v6, "number"
invoke-virtual {p1}, Landroid/view/View;->getId()I
move-result v0
const/high16 v1, 0x7f05
if-ne v0, v1, :cond_9b
iget-object v0, p0, Lcom/damburisoft/android/app/readinfo/a;->a:Lcom/damburisoft/android/app/readinfo/ReadInfo;
invoke-virtual {v0}, Lcom/damburisoft/android/app/readinfo/ReadInfo;->getApplicationContext()Landroid/content/Context;
move-result-object v0
const-string v1, "Hello World!"
invoke-static {v0, v1, v4}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
const/4 v0, 0x6
new-array v2, v0, [Ljava/lang/String;
const/4 v0, 0x0
const-string v1, "_id"
aput-object v1, v2, v0
const-string v0, "number"
aput-object v6, v2, v4
const/4 v0, 0x2
const-string v1, "type"
aput-object v1, v2, v0
const/4 v0, 0x3
const-string v1, "duration"
aput-object v1, v2, v0
const/4 v0, 0x4
const-string v1, "date"
aput-object v1, v2, v0
const/4 v0, 0x5
const-string v1, "name"
aput-object v1, v2, v0
sget-object v1, Landroid/provider/CallLog$Calls;->CONTENT_URI:Landroid/net/Uri;
iget-object v0, p0, Lcom/damburisoft/android/app/readinfo/a;->a:Lcom/damburisoft/android/app/readinfo/ReadInfo;
invoke-virtual {v0}, Lcom/damburisoft/android/app/readinfo/ReadInfo;->getContentResolver()Landroid/content/ContentResolver;
move-result-object v0
const-string v5, "date DESC"
move-object v4, v3
invoke-virtual/range {v0 .. v5}, Landroid/content/ContentResolver;->query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
move-result-object v0
if-eqz v0, :cond_9b
invoke-interface {v0}, Landroid/database/Cursor;->moveToFirst()Z
move-result v1
if-eqz v1, :cond_98
const-string v1, "number"
invoke-interface {v0, v6}, Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v1
const-string v2, "number"
invoke-interface {v0, v6}, Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v2
:cond_60
invoke-interface {v0, v1}, Landroid/database/Cursor;->getString(I)Ljava/lang/String;
move-result-object v3
invoke-interface {v0, v2}, Landroid/database/Cursor;->getString(I)Ljava/lang/String;
move-result-object v4
const-string v5, "ReadInfo"
new-instance v5, Ljava/lang/StringBuilder;
const-string v6, "number: "
invoke-direct {v5, v6}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V
invoke-virtual {v5, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v3
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v3
invoke-static {v7, v3}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
if-eqz v4, :cond_92
const-string v3, "ReadInfo"
new-instance v3, Ljava/lang/StringBuilder;
const-string v5, "cashed_name: "
invoke-direct {v3, v5}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V
invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v3
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v3
invoke-static {v7, v3}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
:cond_92
invoke-interface {v0}, Landroid/database/Cursor;->moveToNext()Z
move-result v3
if-nez v3, :cond_60
:cond_98
invoke-interface {v0}, Landroid/database/Cursor;->close()V
:cond_9b
return-void
.end method
上記のsmali型式のコードを比較して,call_logの内容を読む処理を前者の単にToastでmessageを表示するアプリケーションに追加してみます。追加した結果のsmali型式ファイルは以下の通りです。
# virtual methods
.method public final onClick(Landroid/view/View;)V
.registers 10
const/4 v3, 0x0
const/4 v4, 0x1
const-string v7, "ReadInfo"
const-string v6, "number"
invoke-virtual {p1}, Landroid/view/View;->getId()I
move-result v0
const/high16 v1, 0x7f05
if-ne v0, v1, :cond_1b
iget-object v0, p0, Lcom/damburisoft/app/android/helloworld/a;->a:Lcom/damburisoft/app/android/helloworld/HelloWorldActivity;
invoke-virtual {v0}, Lcom/damburisoft/app/android/helloworld/HelloWorldActivity;->getApplicationContext()Landroid/content/Context;
move-result-object v0
const-string v1, "Hello World!"
const/4 v2, 0x1
invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
const/4 v0, 0x6
new-array v2, v0, [Ljava/lang/String;
const/4 v0, 0x0
const-string v1, "_id"
aput-object v1, v2, v0
const-string v0, "number"
aput-object v6, v2, v4
const/4 v0, 0x2
const-string v1, "type"
aput-object v1, v2, v0
const/4 v0, 0x3
const-string v1, "duration"
aput-object v1, v2, v0
const/4 v0, 0x4
const-string v1, "date"
aput-object v1, v2, v0
const/4 v0, 0x5
const-string v1, "name"
aput-object v1, v2, v0
sget-object v1, Landroid/provider/CallLog$Calls;->CONTENT_URI:Landroid/net/Uri;
iget-object v0, p0, Lcom/damburisoft/app/android/helloworld/a;->a:Lcom/damburisoft/app/android/helloworld/HelloWorldActivity;
invoke-virtual {v0}, Lcom/damburisoft/app/android/helloworld/HelloWorldActivity;->getContentResolver()Landroid/content/ContentResolver;
move-result-object v0
const-string v5, "date DESC"
move-object v4, v3
invoke-virtual/range {v0 .. v5}, Landroid/content/ContentResolver;->query(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
move-result-object v0
if-eqz v0, :cond_1b
invoke-interface {v0}, Landroid/database/Cursor;->moveToFirst()Z
move-result v1
if-eqz v1, :cond_18
const-string v1, "number"
invoke-interface {v0, v6}, Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v1
const-string v2, "number"
invoke-interface {v0, v6}, Landroid/database/Cursor;->getColumnIndex(Ljava/lang/String;)I
move-result v2
:cond_60
invoke-interface {v0, v1}, Landroid/database/Cursor;->getString(I)Ljava/lang/String;
move-result-object v3
invoke-interface {v0, v2}, Landroid/database/Cursor;->getString(I)Ljava/lang/String;
move-result-object v4
const-string v5, "ReadInfo"
new-instance v5, Ljava/lang/StringBuilder;
const-string v6, "number: "
invoke-direct {v5, v6}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V
invoke-virtual {v5, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v3
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v3
invoke-static {v7, v3}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
if-eqz v4, :cond_92
const-string v3, "ReadInfo"
new-instance v3, Ljava/lang/StringBuilder;
const-string v5, "cashed_name: "
invoke-direct {v3, v5}, Ljava/lang/StringBuilder;->(Ljava/lang/String;)V
invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v3
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v3
invoke-static {v7, v3}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
:cond_92
invoke-interface {v0}, Landroid/database/Cursor;->moveToNext()Z
move-result v3
if-nez v3, :cond_60
:cond_18
invoke-interface {v0}, Landroid/database/Cursor;->close()V
:cond_1b
return-void
.end method
さて,この編集した.smaliファイルでclasses.dexをアセンブルして,HelloWorld.apkのclasses.dexに入れ替えます。
% java -Xmx1G -jar ~/lib/smali-1.2.6.jar -o classes.dex out % zip ~/HelloWorldApp.apk classes.dexclassed.dexを入れ替えたHelloWorld.apkをもう一度署名しなおして,emulatorにインストールしてみました。実行した結果,SecurityExceptionが発生しました。
E/AndroidRuntime( 251): java.lang.SecurityException: Permission Denial: reading com.android.providers.contacts.CallLogProvider uri content://call_log/calls from pid=251, uid=10041 requires android.permission.READ_CONTACTS
E/AndroidRuntime( 251): at android.os.Parcel.readException(Parcel.java:1247)
E/AndroidRuntime( 251): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:160)
E/AndroidRuntime( 251): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:114)
E/AndroidRuntime( 251): at android.content.ContentProviderProxy.bulkQueryInternal(ContentProviderNative.java:330)
E/AndroidRuntime( 251): at android.content.ContentProviderProxy.query(ContentProviderNative.java:366)
E/AndroidRuntime( 251): at android.content.ContentResolver.query(ContentResolver.java:245)
E/AndroidRuntime( 251): at com.damburisoft.app.android.helloworld.a.onClick(Unknown Source)
E/AndroidRuntime( 251): at android.view.View.performClick(View.java:2408)
E/AndroidRuntime( 251): at android.view.View$PerformClick.run(View.java:8816)
E/AndroidRuntime( 251): at android.os.Handler.handleCallback(Handler.java:587)
E/AndroidRuntime( 251): at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime( 251): at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 251): at android.app.ActivityThread.main(ActivityThread.java:4627)
E/AndroidRuntime( 251): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 251): at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime( 251): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
E/AndroidRuntime( 251): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
E/AndroidRuntime( 251): at dalvik.system.NativeStart.main(Native Method)
AndroidManifest.xmlを編集していませんので,当然の結果です。ただ,Content Provider CallLogs.Callにアクセスする機能を追加できたのではないかと思われます。今回は,パッケージに入っているクラスをsmali型式のファイルを通して編集することによって,動作を変更いたしましたが,編集ではなく新規にクラスを追加して実行させることも可能ではないのでしょうか。 いろいろと検索してみますと,disassembleしてアプリをハックする方法がいろいろとありますので,そんな難しいことではないのかもしれません。
No comments:
Post a Comment