实操了一个小实验apk,加深对smali语法的学习,并分享一下分析过程,大神请绕过~~~
实验app:freeapp.apk
这是一个程序中本身是具备完整功能的app(进入专业版界面),现在是只提供了免费版功能(只有免费版界面),当给用户提供一个拥有授权KEY的apk文件,让其安装上该授权文件,便可获得高级版功能或专业版功能(进入高级版界面或专业版界面)
各个功能版本界面如下图
现在,想在没有拿到授权KEY的情况下,使程序跳过对授权KEY的检查,直接获得软件高级版/专业版
首先是运用apktool对apk进行反编译来得到smali反汇编代码
接着找到onCreate()方法所在的文件并在文件中确定其位置,这个程序的onCreate()可以在MainAcitity.smali文件中找到,代码如下
.method publiconCreate(Landroid/os/Bundle;)V
.locals 4
.param p1, "savedInstanceState" # Landroid/os/Bundle;
.prologue
const/4 v3, 0x0
.line 24
invoke-super{p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V .line 25
const/high16 v2, 0x7f030000
invoke-virtual {p0, v2}, Lcom/droider/free/MainActivity;->setContentView(I)V
一开始这些是activity创建以及加载布局
接下来的这几行便有了第一处跳转的判断,通过执行checkappKey()方法后,对其返回值进行判断是否要进行跳转
.line 27
const/4 v0, 0x0
.line 28
.local v0, "resID":I
invoke-direct{p0}, Lcom/droider/free/MainActivity;->checkappKey()Z move-result v2
if-nez v2, :cond_2
先不具体查看checkappKey()方法是怎么检查的,当v2等于0时,就不跳转到cond_2,直接继续往下
.line29 const v0, 0x7f040001 #这里其实是字符串ID: Android安全软件免费版
#接下来的代码便是以这个字符串ID来设置为activity的标题,也就是说上一步如果没有跳转的话,便是进入免费版界面
:cond_0
:goto_0
invoke-virtual {p0, v0}, Lcom/droider/free/MainActivity;->getString(I)Ljava/lang/String;
move-result-object v1
.local v1, "titleString":Ljava/lang/String;
invoke-virtual {p0, v1}, Lcom/droider/free/MainActivity;->setTitle(Ljava/lang/CharSequence;)V
….... #这里省略部分代码,都是像下边这样对Button的布局
…...
.line 39
const v2, 0x7f070002
invoke-virtual {p0, v2},Lcom/droider/free/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v2
check-cast v2, Landroid/widget/Button;
iput-object v2, p0, Lcom/droider/free/MainActivity;->btn_pro:Landroid/widget/Button;
#上边如果一直保持一开始的没有跳转,v0还是0x7f040001:免费版
.line 50
const v2, 0x7f040002 #这里是字符串ID:Android安全软件高级版
if-ne v0, v2, :cond_3 #而由于v0还是0x7f040001所有跳转到cond_3
#如果没有跳转的话便是执行下边几行,可以看出是将高级版本按钮呈现出来
iget-objectv2, p0, Lcom/droider/free/MainActivity;->btn_advanced:Landroid/widget/Button; invoke-virtual{v2, v3}, Landroid/widget/Button;->setVisibility(I)V .line 57
#其实上边如果跳转到cond_3的话,由于v0是0x7f040001,又会跳回到下边的cond_1,也就是跳过了上边对高级版按钮的设置
:cond_1
:goto_1
iget-object v2, p0, Lcom/droider/free/MainActivity;->btn_free:Landroid/widget/Button;
new-instance v3, Lcom/droider/free/MainActivity$1;
invoke-direct {v3, p0},Lcom/droider/free/MainActivity$1;-><init>(Lcom/droider/free/MainActivity;)V
invoke-virtual{v2, v3}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V …....
…....
invoke-virtual {v2, v3},Landroid/widget/Button;->
setOnClickListener(Landroid/view/View$OnClickListener;)V .line 86
return-void
#跳回到cond_1后便是像上边这样对按钮的监听事件进行设置,然后便是return,onCreate()方法返回,这也就是免费版的执行过程
那如果从一开始就在checkappKey()方法返回结果后,成功跳转,便来到了cond_2,这里则是通过getAppKey()方法对KEY进行获取,并通过decryptAppKey()方法对KEY值进行解密
:cond_2
const v2, 0x7f030001
invoke-direct{p0, v2}, Lcom/droider/free/MainActivity;->getAppKey(I)Ljava/lang/String; move-result-object v2
invoke-direct{p0, v2}, Lcom/droider/free/MainActivity;->decryptAppKey(Ljava/lang/String;)I move-result v0 #v0的值将有可能是0/高级版字符串ID/专业版字符串ID
if-nez v0, :cond_0 #如果非0,v0的值便保持高级/专业版的ID返回到cond_0
#回到cond_0,便是像上边写的进行button的布局,并通过v0的值来判断是否能够进入到高级/专业版的按钮呈现设置
.line 33
const v0, 0x7f040001 #如果不跳转,v0的值便设为免费版字符串ID
goto :goto_0 #再跳回到cond_0下边的goto_0继续button的布局
而上边还有一处便是是否跳转到cond_3,上边是假设v0=0x7f040001,那当v0在获取KEY后,其值是0x7f040002/0x7f040003即高级/专业:
const v2, 0x7f040002 #这里是字符串ID:Android安全软件高级版
if-nev0, v2,:cond_3 #如果v0是0x7f040003(专业) 则也跳转到cond_3
#如果没有跳转的话便是执行下边几行,可以看出是将高级版本按钮呈现出来
iget-objectv2, p0, Lcom/droider/free/MainActivity;->btn_advanced:Landroid/widget/Button; invoke-virtual{v2, v3}, Landroid/widget/Button;->setVisibility(I)V
那cond_3处是这样的:
:cond_3
const v2, 0x7f040003 #字符串ID:Android安全软件专业版
if-ne v0, v2, :cond_1 #当v0=0x7f040003而非0x7f040001便不会跳转
#下边便是没有跳转执行的几行,没错,是对高级按钮以及专业按钮功能的呈现,便进入了专业版界面
.line 53
iget-object v2, p0, Lcom/droider/free/MainActivity;->btn_advanced:Landroid/widget/Button;
invoke-virtual{v2, v3}, Landroid/widget/Button;->setVisibility(I)V iget-object v2, p0, Lcom/droider/free/MainActivity;->btn_pro:Landroid/widget/Button;
invoke-virtual{v2, v3}, Landroid/widget/Button;->setVisibility(I)V goto :goto_1 #按钮呈现完好便继续回到goto_1,进行按钮监听事件的设置
.end method
这就是免费版/高级版/专业版界面进入的实现过程,主要的关键突破点便是首先在checkappKey()方法上检查通过,然后在跳转后decryptAppKey()在返回ID是返回空/高级字符串ID/专业字符串ID,也即就是最后决定v0的值为0x7f040001/0x7f040002/0x7f040003
所以想要破解该程序,使进入高级版/专业版,就是要先跳过checkappKey()方法
invoke-direct{p0}, Lcom/droider/free/MainActivity;->checkappKey()Z move-result v2
if-nez v2, :cond_2
这里直接将对方法的调用直接删除掉,然后保证v2的值不等于0,便跳转到cond_2
const v2,0x01
if-nezv2,:cond_2
然后便是来到cond_2处
:cond_2
const v2, 0x7f030001
invoke-direct{p0, v2}, Lcom/droider/free/MainActivity;->getAppKey(I) Ljava/lang/String;
move-result-object v2
invoke-direct{p0, v2}, Lcom/droider/free/MainActivity;->decryptAppKey (Ljava/lang/String;)I
move-result v0 #v0的值将有可能是0/高级版字符串ID/专业版字符串ID
if-nez v0, :cond_0 #如果非0,v0的值便保持高级/专业版的ID返回到cond_0
这里也直接将getAppKey()/decryptAppKey()两个方法的调用删除掉,然后当想进入高级版界面则将v0的值设为 0x7f040002,想进入专业版便将v0设为 0x7f040003
:cond_2
const v00x7f040002 / const v0 0x7f040003 if-nezv0, :cond_0 当代码修改完成后保存,再使用apktool进来刚刚反编译成的目录,打包为apk,在该目录下的dist目录便可找到生成的apk文件
在运用jarsigner对其重签名(前提得先创建一个keystore)
在MainActivity.smali文件也有上边三个方法调用的反汇编代码,其中checkappKey()方法中是通过调用getAppKey()方法来确定是否能获取KEY,成功返回1,否为0
而getAppKey()方法则是通过其中的createPackageContxt()方法来创建授权KEY文件的Context,然后通过这个Context来访问授权KEY文件的资源,也即就是加密的字符串,通过decryptAppKey()方法来解密返回字符串。
一个程序要通过createPackageContext()方法来获取其他程序的Context,再通过Context来访问其他程序的资源,需要拥有相同的用户ID与签名,用户ID是一个字符串标识,在AndroidManifest.xml文件的manifest标签中将属性android:sharedUserId=”xxx.xxx.xxx”,当两个程序拥有相同的用户ID,便会运行在同一个进程空间,资源可以相互访问;如果签名也相同,则还可以相互执行软件包之间的代码。
|