本帖最后由 PoJie_小雨 于 2014-12-4 14:04 编辑
分享一下学习过程中的心得,主要献给和我一样起步晚又对安卓产生浓烈兴趣的童鞋们!
一、代码跟踪的介绍&使用工具
代码跟踪常用于调试程序中,跟踪并了解程序的执行轨迹和执行逻辑。这样来说,对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 | | Priority constant for the println method. | | | Priority constant for the println method; use Log.d. | | | Priority constant for the println method; use Log.e. | | | Priority constant for the println method; use Log.i. | | | Priority constant for the println method; use Log.v. | | | Priority constant for the println method; use Log.w |
Log.d一般来说是用的最多的。(本来log.v是最多的,但是log.v并不参加编译) 写一个测试类来分析Java代码如下: [Asm] 纯文本查看 复制代码 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代码如下: [Asm] 纯文本查看 复制代码 # direct methods
.method public constructor <init>()V
.registers 1
.prologue
.line 8
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static Log(Ljava/lang/Object;)V
.registers 3
.parameter "XX"
.prologue
.line 17
const-string v0, "Log"
invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object v1
invoke-static {v0, v1}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V
.line 18
return-void
.end method
.method public static Log(Ljava/lang/Object;Ljava/lang/String;)V
.registers 3
.parameter "XX"
.parameter "tag"
.prologue
.line 22
invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object v0
invoke-static {p1, v0}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V
.line 23
return-void
.end method
.method public static Log(Ljava/lang/String;)V
.registers 2
.parameter "msg"
.prologue
.line 12
const-string v0, "Log"
invoke-static {v0, p0}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V
.line 13
return-void
.end method
.method public static Log(Ljava/lang/String;Ljava/lang/String;)V
.registers 2
.parameter "tag"
.parameter "msg"
.prologue
.line 28
invoke-static {p0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 29
return-void
.end method
.method public static ToastLog(Landroid/content/Context;Ljava/lang/String;)V
.registers 3
.parameter "contx"
.parameter "msg"
.prologue
.line 33
const/4 v0, 0x5
invoke-static {p0, p1, v0}, Lcom/Ericky/Log;->ToastLog(Landroid/content/Context;Ljava/lang/String;I)V
.line 34
return-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 40
invoke-virtual {p0}, Landroid/content/Context;->getApplicationContext()Landroid/content/Context;
.line 41
invoke-static {p0, p1, p2}, 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 42
return-void
.end method 取出第一个方法: [Asm] 纯文本查看 复制代码 .method public static Log(Ljava/lang/Object;)V
.registers 3
.parameter "XX"
.prologue
.line 17
const-string v0, "Log"
invoke-virtual {p0}, Ljava/lang/Object;->toString()Ljava/lang/String;
move-result-object v1
invoke-static {v0, v1}, Lcom/Ericky/Log;->Log(Ljava/lang/String;Ljava/lang/String;)V
.line 18
return-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 还有一个方法就是大家搜集一些没有加密的app的smali源码,这就是我们自己的图书馆啊!谁用谁知道! 1.栈跟踪法
有时候 破解的时候,我们会找一些关键的注册字符串,例如“ Register、failed、limit、pro”等等,但是很多程序其实在刚启动的时候就检测完了用户的合法性,所以可能造成定位不准确。一些混淆过的程序一般都是这样的。这时候我们就可以用栈跟踪法了,可以查看堆栈,能看到它所调用的方法,查看堆栈代码:invoke-static {}, Ljava/lang/Thread;->dumpStack()V 它的标签为:System.err 实际效果: [Asm] 纯文本查看 复制代码 java.lang.Throwable: stack dump
at 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”,但是还是可以判断的。 1.线程和处理器 很多时候app用了大量线程之间的通信导致了栈跟踪法变得不是那么有效了,这个时候如果你还是打印出堆栈的信息的话,只会追溯到最后的那个线程的消息处理器,而原始的那个线程就被隐藏了。当然可以通过手工来追溯,方法是根据代码中的一些函数类似于sendMessage()等等。这里有个更简单的方式: [Asm] 纯文本查看 复制代码 // 在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个线程如果同时在运行,这个方法就不好使了,原因就留给大家去思考了。
1.“遗留下的宝藏”
这个方法虽然说跟运气脱不了干系,但在实际的运用中还是比较广泛的。因为程序员是人,是人就会有疏忽。在编写程序的时候,程序员会用很多的log来调试他自己写的程序是否正确,因此就给逆向人员留下了一笔宝贵的财富。当然,很有可能有一个Boolean类型调试变量来控制是否输出log信息,类似下面: [Asm] 纯文本查看 复制代码 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)
6.JDB神器 这是一个很强大的工具,应该是类似GNU或者GDB调试器。打开DDMS获取端口,然后连接调试器。 在Linux下的命令: jdb -attach localhost:8616 在Windows下的命令: jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8616 出现这样的文字说明成功了: [Asm] 纯文本查看 复制代码
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ... 三、总结 当然还有更多更好的方法,希望大牛们继续补充。学习Android已经1个多星期了,菜鸟一枚。很多地方难免有疏漏之处或者错误,欢迎各位童鞋指正,也欢迎多多交流,共同学习进步。 By Ericky 2014.12.4
|