Android高效调试技巧02——反编译

今天要给大家介绍一项调试技巧——反编译。因为我们遇到的问题有时会跟第三方应用有关,对于这些我们没有代码的应用,如果不进行反编译,很难对其进行分析。

说到apk反编译不得不说代码混淆,在签名打包时启动代码混淆可以帮助我们保护代码。曾经听说有小公司发布了未做代码混淆的应用,结果被别人反编译后上架,辛辛苦苦做出的应用就这样被别人窃取了,所以保护好自己的劳动成果很重要。除了代码混淆,有些应用商店还会要求开发者进行安装包加固,以提高安全性。

反编译就是把对代码做的这些保护解开,让我们了解别人代码的部分细节。比如下面要介绍的例子,GoogleSetupWizard 引起的问题,虽然我们没有它的源码,但是通过反编译我们可以了解内部的逻辑。

反编译工具

在处理这个问题之前我用的反编译工具一直是Dex2Jar+jd-gui,用法简单,只要将 apk解压缩,把解压出的dex文件用Dex2Jar反编译,然后用jd-gui工具阅读即可,对于没有混淆的apk,阅读起来跟阅读代码体验差不多。但是在反编译 GoogleSetupWizard 的时候却出错(提示notsupport version)。咨询了别的同事建议试试apktool,解出smali文件。关于smali,这里有一份文档供大家参考Smali学习笔记

apktool的下载和使用: 将apktool.jar和apktool放到/usr/local/bin 目录(需要root权限);运行apktool d <apk-file> 进行反编译;

反编译举例

问题描述:
  在从Owner账户切换到Guest账户时失败,自动又切回Owner账户,并且状态栏无法正常下拉。

问题分析:
  该问题第一个难点是确认谁触发了切回Owner账户的动作。这里我们用android.util.Log类的Log.getStackTraceString(newThrowable()) 方法来完成。账户切换会调用ActivityManagerNative类的switchUser方法,我们在该方法中添加如下语句:

Log.i(TAG,Log.getStackTraceString(newThrowable()));

这样调用关系就一目了然了。结果如下:

ActivityManagerNative:  at android.app.ActivityManagerProxy.switchUser(ActivityManagerNative.java:5866)
ActivityManagerNative:  atcom.google.android.setupwizard.util.UserHelper.removeThisUser(UserHelper.java:43)
ActivityManagerNative:  atcom.google.android.setupwizard.lifecycle.ProvisioningFlagsLifecycleCallback.onExit
(ProvisioningFlagsLifecycleCallback.java:49)
ActivityManagerNative:  at com.google.android.setupwizard.lifecycle.LifecycleManager.notifyExit(LifecycleManager.java:115)
ActivityManagerNative:  atcom.google.android.setupwizard.util.ExitHelper.finishSetup(ExitHelper.java:45)
ActivityManagerNative:  at
**com.google.android.setupwizard.util.SetupWizardUserInitReceiver**
.onReceive (SetupWizardUserInitReceiver.java:33)

可以看出调用者是com.google.android.setupwizard应用的SetupWizardUserInitReceiver类,它是一个Google gms 应用。我们用apktool将其反编译,SetupWizardUserInitReceiver.smali
文件如下:

.superLandroid/content/BroadcastReceiver;
.source"SetupWizardUserInitReceiver.java"
#direct methods

.methodpublic constructor <init>()V
.locals0
.prologue
.line 28
invoke-direct{p0}, Landroid/content/BroadcastReceiver;-><init>()V
return-void
.endmethod
#virtual methods
.methodpublic onReceive(Landroid/content/Context;Landroid/content/Intent;)V
.locals2
.paramp1, "context" # Landroid/content/Context;
.paramp2, "intent" # Landroid/content/Intent;
.prologue
.line 31

const-string/jumbov0, "android.intent.action.USER_INITIALIZE"
invoke-virtual{p2}, Landroid/content/Intent;->getAction()Ljava/lang/String;
move-result-objectv1
invoke-virtual{v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-resultv0
if-eqzv0, :cond_0
.line 32
invoke-static{p1},Landroid/os/UserManager;->get(Landroid/content/Context;)Landroid/os/UserManager;
move-result-objectv0
invoke-virtual{v0}, Landroid/os/UserManager;->isGuestUser()Z
move-resultv0
.line 31
if-eqzv0, :cond_0
.line 33
invoke-static{p1},Lcom/google/android/setupwizard/util/ExitHelper;->finishSetup(Landroid/content/Context;)V
.line 30
:cond_0
return-void
.endmethod

这是一个BroadcastReceiver,我们要关注的是它的onReceive方法。通过网上搜索smali语法,可以判断出当为Guest用户时会调用ExitHelper的finishSetup方法。我们再看这个方法,

.methodpublic static finishSetup(Landroid/content/Context;)V
.locals2
.paramp0, "context" # Landroid/content/Context;
.prologue
.line 45

invoke-static{},Lcom/google/android/setupwizard/lifecycle/LifecycleManager;->get()Lcom/google/android/setupwizard/lifecycle/LifecycleManager;
move-result-objectv0
invoke-virtual{v0, p0},Lcom/google/android/setupwizard/lifecycle/LifecycleManager;->notifyExit(Landroid/content/Context;)V
.line 49

>new-instancev0, Landroid/content/Intent;
const-string/jumbov1, "com.google.android.setupwizard.SETUP_WIZARD_FINISHED"
invoke-direct{v0, v1}, Landroid/content/Intent;-><init>(Ljava/lang/String;)V
invoke-virtual{p0, v0},Landroid/content/Context;->sendBroadcast(Landroid/content/Intent;)V
.line 42
return-void
.endmethod

上面先拿到了一个LifecycleManager 实例,然后调用了LifecycleManager的notifyExit方法,再发送了一个com.google.android.setupwizard.SETUP_WIZARD_FINISHED广播。最终,我们跟踪到调用用户切换的代码如下:

50 const-string/jumbo v3, "device_provisioned"**
51 
52 invoke-static {v0, v3, v6},Landroid/provider/Settings$Global;>putInt(Landroid/content/ContentResolver;Ljava/lang/String;I)Z
53 
54 .line 56 
55 :cond_0 
56 :goto_0 
57 const-string/jumbo v3, "user_setup_complete" 
58 
59 invoke-static {v0, v3, v6},Landroid/provider/Settings$Secure;->putInt(Landroid/content/ContentResolver;Ljava/lang/String;I)Z
60 
61 .line 34 
62 return-void 
63 
64 .line 45 
65 :cond_1 
66 invoke-static {p1},Lcom/android/setupwizardlib/util/WizardManagerHelper;->isDeviceProvisioned(Landroid/content/Context;)Z
67 
68 move-result v3 
69 
70 if-nez v3, :cond_0 
71 
72 .line 48 
73 invoke-virtual {v2}, Landroid/os/UserManager;->getUserHandle()I 
74 
75 move-result v1 
76 
77 .line 49 
78 .local v1, "user":I 
79 invoke-static {p1},Lcom/google/android/setupwizard/util/UserHelper;->removeThisUser(Landroid/content/Context;)Z

上面会去读device_provisioned这个setting项,当为false时会退出当前账户。通过在源码下搜索,果然发现在packages/apps/Provision模块下有对应的提交,这样就定位到了问题点。

总结

反编译作为一项调试技巧,在通往高手之路上是不必可少的。虽然反编译后的代码比较难懂,但是多加练习总有一天会得心应手。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容

  • 一、前言 今天我们开始apk破解的另外一种方式:动态代码调试破解,之前其实已经在一篇文章中说到如何破解apk了: ...
    JiangWei_App阅读 3,670评论 2 29
  • 大多数APP都对API接口进行了加密,防止第三方随意调用接口,常用的方法是,设置一个key,在调用接口来发送请求时...
    vstorm阅读 8,095评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,152评论 25 707
  • 【2015/2/26】 这一把锁有个华丽的名字叫命运齿轮。 可惜它的形状并不是齿轮状,而是两片银色的金属铰合在一起...
    下不停的雨阅读 346评论 0 0
  • 情绪每个人都有允许自己的情绪和情绪在一起,有情绪不可怕接受情绪不好处理好不逃避不害怕,情绪不可怕可怕自己不接纳不理...
    下页人气阅读 1,959评论 0 0