一、代码跟踪的介绍&使用工具

代码跟踪常用于调试程序中,跟踪并了解程序的执行轨迹和执行逻辑。这样来说,对Java这样的高级语言来说,我们容易理解也容易调试。但是像一些低级语言,例如ASM、Smali来说难度就很大了,因为反汇编这样的语言,我们需要一次性记住很多乱七八糟的关键词才能成功,因而代码跟踪的技术就诞生了,并且在一些大程序中的运用更为广泛,同时也是现在逆向一些大而复杂的Android -app极为重要的一种手段。
相信很多人都玩过PC端,对OD的动态调试也应该特别熟悉。OD很强大,其中的F2(断点),F8(单步走)使得调试程序事半功倍,我相信用过得没有说不好的吧。而Android-app与PC software的性质是不同的。在Android-app里是把Apk文件Dump成字节码,分析,修改,再重新编译。有一个很大的不同点就是Android-app但是正是由于开源,使得Dump出来的字节码是“死”的,这是Java的一个特性。幸运的是,Apktool很好的解决了这个问题,它的出现使得Android-app的调试得到了转机,受益的是大家。
既然不能进行完全的调试,想到的办法是用系统的log函数来呈现程序运行到关键时候的关键信息来达到相应的效果。比如说,你想知道程序运行到某个时候某个变量里装的是什么,就可以用log的方式显示。
获取Android程序的log信息一共有几种方式。最好的选择当时是用DDMS,因为它和Android SDK是一伙的!它可以显示很多系统的log信息以及一些其他的乱七八糟的信息。同时,可以设置过滤器来进行标签过滤、log等级(error,warning,info,debug,verbose)过滤、进程过滤,也能结束进程。相当强大。如图:

二、跟踪方法

1.Logging大法
因为Apps没有控制台所以它们只能通过Android 的API中的log函数来进行输出。
关于android/util/Log的介绍如下:
Log extends Object
一共有5个方法:Log.v() Log.d() Log.i() Log.w() and Log.e(),它们分别对应:
int  ASSERT  Priority constant for the println method.
int  DEBUG  Priority constant for the println method; use Log.d.
int  ERROR  Priority constant for the println method; use Log.e.
int  INFO  Priority constant for the println method; use Log.i.
int  VERBOSE  Priority constant for the println method; use Log.v.
int  WARN  Priority constant for the println method; use Log.w
Log.d一般来说是用的最多的。(本来log.v是最多的,但是log.v并不参加编译)
写一个测试类来分析Java代码如下:

代码:
public class Log {public Log() {}public static void Log(String msg){Log(msg, "Log");}public static void Log(Object XX){Log("Log", XX.toString());}public static void Log(Object XX, String tag){Log(tag, XX.toString());}public static void Log(String tag, String msg){Log.d(tag, msg);}public static void ToastLog(Context contx, String msg, int duration){Toast.makeText(contx, msg, duration).show();}
}

反汇编成Smali代码如下:

代码:
# direct methods
.method public constructor <init>()V.registers 1.prologue.line 8invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method.method public static Log(Ljava/lang/Object;)V.registers 3.parameter "XX".prologue.line 17const-string v0, "Log"invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;move-result-object v1invoke-static {v0, v1}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V.line 18return-void
.end method.method public static Log(Ljava/lang/Object;Ljava/lang/String;)V.registers 3.parameter "XX".parameter "tag".prologue.line 22invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;move-result-object v0invoke-static {p1, v0}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V.line 23return-void
.end method.method public static Log(Ljava/lang/String;)V.registers 2.parameter "msg".prologue.line 12const-string v0, "Log"invoke-static {v0, p0}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V.line 13return-void
.end method.method public static Log(Ljava/lang/String;Ljava/lang/String;)V.registers 2.parameter "tag".parameter "msg".prologue.line 28invoke-static {p0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I.line 29return-void
.end method.method public static ToastLog(Landroid/content/Context;Ljava/lang/String;)V.registers 3.parameter "contx".parameter "msg".prologue.line 33const/4 v0, 0x5invoke-static {p0, p1, v0}, Lcom/Ericky/Log;->ToastLog(Landroid/content/Context;Ljava/lang/String;I)V.line 34return-void
.end method.method public static ToastLog(Landroid/content/Context;Ljava/lang/String;I)V.registers 4.parameter "contx".parameter "msg".parameter "duration".prologue.line 40invoke-virtual {p0}, Landroid/content/Context;->getApplicationContext()Landroid/content/Context;.line 41invoke-static {p0, p1, p2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;move-result-object v0invoke-virtual {v0}, Landroid/widget/Toast;->show()V.line 42return-void
.end method

取出第一个方法:

代码:
.method public static Log(Ljava/lang/Object;)V.registers 3
.parameter "XX".prologue.line 17const-string v0, "Log"invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;move-result-object v1invoke-static {v0, v1}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V.line 18return-void
.end method

在这个方法里的第一个参数就是标签,在DDMS中是可以过滤的。因此,可以利用这个特点插入我们要加的信息,从而轻松的定位到关键的地方。在实际操作的过程中,可以打开android API的文档,对着看。通常的做法是用Log.d,还要用到toString()这个API,同时需要2个变量,一个变量存放你的标签以及另一个存放你的对象。值得注意的是,即使你根本不用这个2个变量,也需要将.locals和.registers的数量增加。修改完了之后记得检查好代码,至少我们自己能读懂它。
还有一点需要我们注意,当你改完之后最好在根目录下运行,否则记得还要改class的路径。不然的话有可能出现ClassDefNotFound错误,程序强制退出。下面是修改的代码:
const-string v0, "Ericky tag"
const-string v1, "important log message"
invoke-static {v0, v1}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V
这是一个简单的例子,但是有的时候我们是需要显示更多的字符串,那我们应该怎么办呢?需要用到一个StringBuilder类。StringBuilder类的用法在我之前的一篇文章中已经有用到了,很容易理解。 http://bbs.pediy.com/showthread.php?t=194955
2.栈跟踪法

有时候破解的时候,我们会找一些关键的注册字符串,例如“Register、failed、limit、pro”等等,但是很多程序其实在刚启动的时候就检测完了用户的合法性,所以可能造成定位不准确。一些混淆过的程序一般都是这样的。这时候我们就可以用栈跟踪法了,可以查看堆栈,能看到它所调用的方法,查看堆栈代码:
invoke-static {}, Ljava/lang/Thread;->dumpStack()V
它的标签为:System.err  实际效果:

代码:
java.lang.Throwable: stack dumpat java.lang.Thread.dumpStack(Thread.java:612)at com.lohan.testbed.SomeClass.superFun(SomeClass.java:113)at com.lohan.testbed.Main.onCreate(Main.java:48)at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)at android.app.ActivityThread.access$2300(ActivityThread.java:125)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loop(Looper.java:123)at android.app.ActivityThread.main(ActivityThread.java:4627)at java.lang.reflect.Method.invokeNative(Native Method)at java.lang.reflect.Method.invoke(Method.java:521)at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)at dalvik.system.NativeStart.main(Native Method)

从第三行可以知道dumpStack实际上是在SomeClass类中的superFun()函数里。然后superFun()函数又是在onCreate()函数里。以此类推。不过值得一提的是,如果app被混淆了,并且debugging 信息被删除了或者是被重命名了,会出现“Unknown source”,但是还是可以判断的。
3.线程和处理器
很多时候app用了大量线程之间的通信导致了栈跟踪法变得不是那么有效了,这个时候如果你还是打印出堆栈的信息的话,只会追溯到最后的那个线程的消息处理器,而原始的那个线程就被隐藏了。当然可以通过手工来追溯,方法是根据代码中的一些函数类似于sendMessage()等等。这里有个更简单的方式:

代码:
// 在handleMessage() 中的smali代码
invoke-static {v0}, Lsample/thread/messaging/ThreadMessaging;->access$0(Lsample/thread/messaging/ThreadMessaging;)Landroid/os/Handler;
move-result-object v2
invoke-virtual {v2}, Landroid/os/Handler;->obtainMessage()Landroid/os/Message;
move-result-object v1
//假设v2是你的处理器
invoke-virtual {v2}, Landroid/os/Handler;->getLooper()Landroid/os/Looper;
move-result-object v2
invoke-virtual {v2}, Landroid/os/Looper;->getThread()Ljava/lang/Thread;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/Thread;->getName()Ljava/lang/String;
move-result-object v2

但是2个线程如果同时在运行,这个方法就不好使了,原因就留给大家去思考了。
4.“遗留下的宝藏”

这个方法虽然说跟运气脱不了干系,但在实际的运用中还是比较广泛的。因为程序员是人,是人就会有疏忽。在编写程序的时候,程序员会用很多的log来调试他自己写的程序是否正确,因此就给逆向人员留下了一笔宝贵的财富。当然,很有可能有一个Boolean类型调试变量来控制是否输出log信息,类似下面:

代码:
public class MyClass {private Context myContext;private boolean DebugMode;public MyClass(Context c){myContext = c;DebugMode = false;}public void someMethod(){SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(myContext);if ( DebugMode )Log.d("someMethod", "running someMethod now!");SharedPreferences.Editor editor = settings.edit();try {editor.putString("mobileID", getMobileID());}catch (Exception e) {e.printStackTrace();}    editor.commit();}
}

那我们只需要找到DebugMode的位置,把它置成“1”即可。
5.SmaliDebugging(APKtools)
如果log信息都被程序员删除了,那怎么办?此时此刻,我们不得不拿出神器Apktools来调试一段一段的smali语句了,根据官方介绍,还可以单步走。是不是很诱人呢?具体可以看:http://code.google.com/p/android-apktool/wiki/SmaliDebugging
6.JDB神器
这是一个很强大的工具,应该是类似GNU或者GDB调试器。打开DDMS获取端口,然后连接调试器。
在Linux下的命令:
jdb -attach localhost:8616
在Windows下的命令:
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8616
出现这样的文字说明成功了:

代码:
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...

三、总结
当然还有更多更好的方法,希望大牛们继续补充。学习Android已经1个多星期了,菜鸟一枚。很多地方难免有疏漏之处或者错误,欢迎各位童鞋指正,也欢迎多多交流,共同学习进步。
By Ericky

2014.12.4

原文地址: http://www.kanxue.com/bbs/showthread.php?t=195202

安卓代码跟踪方式学习笔记相关推荐

  1. 从滚动条到画布的几个代码文件——Python学习笔记之十七

    从滚动条到画布的几个代码文件--Python学习笔记之十七 这一章的继续学习,比在前的照片涉及代码要轻松一些,代码打完后的预期结果总能实现.特别让人欣慰的一点,文本上的文字理解好像上了一个台阶,不用多 ...

  2. OpenCV学习笔记(三十六)——Kalman滤波做运动目标跟踪 OpenCV学习笔记(三十七)——实用函数、系统函数、宏core OpenCV学习笔记(三十八)——显示当前FPS OpenC

    OpenCV学习笔记(三十六)--Kalman滤波做运动目标跟踪 kalman滤波大家都很熟悉,其基本思想就是先不考虑输入信号和观测噪声的影响,得到状态变量和输出信号的估计值,再用输出信号的估计误差加 ...

  3. java mvc框架代码_JAVA技术学习笔记:SpringMVC框架(内附入门程序开发代码)

    原标题:JAVA技术学习笔记:SpringMVC框架(内附入门程序开发代码) JavaEE体系结构包括四层,从上到下分别是应用层.Web层.业务层.持久层.Struts和SpringMVC是Web层的 ...

  4. Python自带又好用的代码调试工具Pdb学习笔记

    返璞归真 这几天项目有一个linux下部署数据库的操作,数据库使用python进行初始化安装.然后问题来了,由于linux服务器涉及安全要求,除了代码以来的Python3.6版本外不允许安装其他插件与 ...

  5. 无人驾驶车辆路径规划及轨迹跟踪控制学习笔记(2)

    目录 汇总 学习笔记 汇总 在关键交通场景中,轨迹规划和轨迹跟踪控制是自动驾驶车辆避免碰撞的两个关键.它不仅需要系统功能,而且需要强大的实时性. 我们集成了自动驾驶汽车的轨迹规划器和跟踪控制器,通过轨 ...

  6. XGB建模流程化代码—仅作学习笔记

    XGB建模流程化代码-仅作个人学习笔记 以下绝大部分出自网络,因为不知道具体作者是谁...代码部分针对自己学习使用修改了一下 建模的要点80%在数据,我是真的理解到了,心痛.头疼☠ 本篇主要是把xgb ...

  7. c语言float二进制输出代码_C语言学习笔记——学前知识概述

    将我大一学习C语言时做的笔记拿来与大家分享,内容比较浅显,比较适合初学者,如有错误还请见谅,提出改正,谢谢! 前言:此C语言笔记是本人在自学时记录的一些重点或初学者常犯的错误,希望我的这本笔记能够对大 ...

  8. 安卓开发Android studio学习笔记12:读取解析XML(案例演示)

    Android studio学习笔记 第一步:配置Student.XML 第二步:配置activity_main.xml 第三步:配置student.xml 第四步:配置Student用户类 第五步: ...

  9. core java 9 代码_Java Core 学习笔记——3.char/Unicode/代码点/代码单元

    通用字符集(UCS) UCS是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所制定的标准字符集. UCS包括了其他所有的字符集(包含了已知语言的所以字符). ISO/IEC 1 ...

最新文章

  1. 常用MySQL函数存储过程_解析MySQL存储过程、常用函数代码
  2. mysql主备模型,MySQL数据同步【双主热备】
  3. 车辆行人检测数据集_开源 | 相机和激光雷达融合的行人车辆检测跟踪
  4. 财务大数据比赛有python吗-Python去做大数据开发,真的有前景吗?
  5. 已解决:k8s集群启动后,默认创建哪些namespace?
  6. spring19:AspectJ的初步介绍
  7. 「敏捷开发」适合什么样的团队?
  8. 信号与系统——微分方程与差分方程——编辑中
  9. 从桌面向手机移植Silverlight应用
  10. lol韩服游戏内设置_lol韩服游戏内设置界面翻译
  11. ruby 将日期转化为时间_Ruby中的日期和时间类
  12. 桌面虚拟云终端技术研究
  13. 百度和bing的背景肤色图片的保存
  14. 线性规划问题求解(Excel、python和手工求解)
  15. android ios mp4格式转换,ios 开发 视频格式转换、mov转MP4
  16. 测量电流传感器的放大倍数
  17. HUAWEI 机试题:黑板上的数涂颜色
  18. stm32f4有重映射么_STM32单片机重映射USART设计
  19. 东方日升再夺印度第一光伏供应商桂冠
  20. “旅行青蛙”游戏外挂藏风险 苹果:或至个人ID泄露

热门文章

  1. POJ 1753 Flip Game DFS枚举
  2. 驱动开发 - WDK 调试及 SVN 环境搭建[转]
  3. Unity 配置:typeConverter的使用
  4. circshift 函数详解
  5. who whoami who am i的区别
  6. 不同版本GCC编译器之间的切换
  7. 专业的LaTeX: 在Linux下编写高质量的文档
  8. Python 脚本错误:IndentationError: unindent does not match any outer indentation level
  9. 高斯混合模型 GMM
  10. 【Leetcode】二分法左侧边界右侧边界模板