夫天地者,万物之逆旅;光阴者,百代之过客。

0%

apk 反编译 重打包 重签名 (SWPU-2019-CTF)

前言

SWPU的比赛把狗儿整自闭喽~

(后来想了想其实是我对这个比赛的定位搞错啦~我最初以为就是swpu面向他们学校新手的比赛,没想到天河、LinE等等好多大佬也来了。)

MISC不想做,于是先拿RE试了试水,结果一道题没搞出来。四道RE,没有一道题是加壳混淆的,就是最基本的算法逆向,然而还是看不懂。

然后看了看Mobile,先看的Mobile2(此题截止到今晚依旧零解),思路感觉很清晰,但就是不对。然后现学了一下smali和回编译,虽然学会了新知识,但是对解题没有任何帮助。不过借助这些新学的知识把Mobile1做了出来。

下面就写一下mobile1的做题经过。

所以,这篇博文或许应该改名为:android动态调试初探。

参考:

反编译

apktool.jar d -s app1.apk

生成app1的文件夹。

apktool.jar d app1.apk -o app1_smali

生成app1_smali文件夹

不带有-s时,反编译生成多个smali文件,带有-s时,会将多个smali文件整合成一个classes.dex.

java代码

dex2jar classes.dex

将classes.dex转化成.jar文件,并用jd-gui查看。

1575648970957

输入的文本和调用native层的Encrypt()返回的string比较。

修改smali代码

参考:

1575649101362

经过和java代码的对比,我们确认比较字符串的代码在MainActivity$1.smali.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
.class Lcom/example/ndktest2/MainActivity$1;
.super Ljava/lang/Object;
.source "MainActivity.java"

# interfaces
.implements Landroid/view/View$OnClickListener;


# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
value = Lcom/example/ndktest2/MainActivity;->onCreate(Landroid/os/Bundle;)V
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation


# instance fields
.field final synthetic this$0:Lcom/example/ndktest2/MainActivity;

.field final synthetic val$password:Landroid/widget/EditText;


# direct methods
.method constructor <init>(Lcom/example/ndktest2/MainActivity;Landroid/widget/EditText;)V
.locals 0
.param p1, "this$0" # Lcom/example/ndktest2/MainActivity;

.line 28
iput-object p1, p0, Lcom/example/ndktest2/MainActivity$1;->this$0:Lcom/example/ndktest2/MainActivity;

iput-object p2, p0, Lcom/example/ndktest2/MainActivity$1;->val$password:Landroid/widget/EditText;

invoke-direct {p0}, Ljava/lang/Object;-><init>()V

return-void
.end method


# virtual methods
.method public onClick(Landroid/view/View;)V
.locals 3
.param p1, "view" # Landroid/view/View;

.line 31
iget-object v0, p0, Lcom/example/ndktest2/MainActivity$1;->val$password:Landroid/widget/EditText;

invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

move-result-object v0

invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;

move-result-object v0

iget-object v1, p0, Lcom/example/ndktest2/MainActivity$1;->this$0:Lcom/example/ndktest2/MainActivity;

invoke-virtual {v1}, Lcom/example/ndktest2/MainActivity;->Encrypt()Ljava/lang/String;

move-result-object v1

invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_0

.line 32
iget-object v0, p0, Lcom/example/ndktest2/MainActivity$1;->this$0:Lcom/example/ndktest2/MainActivity;

const/4 v1, 0x1

const-string v2, "\u767b\u5f55\u6210\u529f"

invoke-static {v0, v2, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object v0

.line 33
invoke-virtual {v0}, Landroid/widget/Toast;->show()V

goto :goto_0

.line 36
:cond_0
iget-object v0, p0, Lcom/example/ndktest2/MainActivity$1;->this$0:Lcom/example/ndktest2/MainActivity;

const/4 v1, 0x0

const-string v2, "\u767b\u5f55\u5931\u8d25"

invoke-static {v0, v2, v1}, 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

.line 39
:goto_0
return-void
.end method

1575649454112

native层中返回的字符串放入寄存器v1.

是否相等的bool量放入寄存器v0.

if-eqz跳转。

失败分支:

1575649646146

Toast是一个悬浮文字控件,第二个参数是输出字符串。

所以我们把第二个参数v2改成存储着native层中返回的字符串的v1,还要把90行对v1的再次赋值改成对v2赋值,然后删掉92行,然后第94行调用的第三个参数改成v2.

这样就可以实现输出v1的字符串。

把90行对v1的赋值改成对v2赋值,然后删掉92行,然后第94行调用的第三个参数改成v2这一步一是为了防止v1中的字符串被覆写,二是toast的第三个参数不可缺省,且为0或1(0或1存疑))

修改后:

1575650111023

原来是输出登录失败的字符串,现在经过我们的修改后,输出flag.

apktool打包

来到apk文件夹的所在目录

java -jar D:\计算机\CTF\工具\逆向\apktool.jar b app1_smali -o app1_fixed.apk

注意一定要用java -jar的方式调用apktool.jar

b是build,d是decompile

生成了app1_fixed.apk。但是会安装失败,因为尚未签名。

1575651044644

jarsigner.exe签名

参考:

安装了jdk之后,在jdk/bin下有个jarsigner.exe,可以用来给apk签名。

powershell来到bin目录下,并将apk放入此目录

jarsigner -verbose -keystore debug.keystore -storepass android -keypass android -signedjar app1_fixed_signed.apk app1_fixed.apk androiddebugkey

-verbose:签名命令标识符。
-keystore:后面跟着的是你签名使用的密钥文件(keystore)的绝对路径。
-signedjar:此后有三个参数:
参数一:签名后生成的apk文件所要存放的路径。
参数二:未签名的apk文件的存放路径。
参数三:你的证书名称,通俗点说就是你keystore文件的别名,就是在你eclipse进行签名打包时的Alias的值。

我这里用的是默认的私钥,debug.keystore 一般位于 ~/.android 目录下。要复制到jdk/bin目录下。

1575650694741

运行

随便输入一些字符,点击登录。

1575650763897

写在后面

这比赛虽然给我整自闭了,但还是藉此学到了一些东西的。

放个彩蛋:

7DB052DFEADF63A9DE5FE6DA61D05CBF