diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..49bd478
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+build
+*.so
+*.pyc
+*.log
diff --git a/.project b/.project
new file mode 100644
index 0000000..6cb7e0c
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ AndBug
+
+
+
+
+
+ org.python.pydev.PyDevBuilder
+
+
+
+
+
+ org.python.pydev.pythonNature
+
+
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 0000000..0ebadbb
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,8 @@
+
+
+
+/${PROJECT_DIR_NAME}/src
+
+python 2.7
+Default
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..e8b6c0e
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,10 @@
+eclipse.preferences.version=1
+encoding//lib/andbug/cmd/break-detail.py=utf-8
+encoding//lib/andbug/cmd/break.py=utf-8
+encoding//lib/andbug/cmd/inspect.py=utf-8
+encoding//lib/andbug/cmd/method_detail.py=utf-8
+encoding//lib/andbug/log.py=utf-8
+encoding//lib/andbug/proto.py=utf-8
+encoding//lib/andbug/util.py=utf-8
+encoding//lib/andbug/vm.py=utf-8
+encoding/andbug=utf-8
diff --git a/andbug b/andbug
index a53cbcb..498bb0f 100755
--- a/andbug
+++ b/andbug
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -16,6 +17,7 @@
'this script executes command modules found in andbug.commands'
import os, os.path, sys, traceback, atexit
+
def panic(why, exit=True, exc=False):
sys.stderr.write("!! %s\n" % (why,))
sys.stderr.flush()
@@ -24,6 +26,19 @@ def panic(why, exit=True, exc=False):
if exit:
sys.exit(-1)
+def cur_file_dir():
+ #获取脚本路径
+ path = sys.path[0]
+ #判断为脚本文件还是py2exe编译后的文件,如果是脚本文件,则返回的是脚本的目录,
+ #如果是py2exe编译后的文件,则返回的是编译后的文件路径
+ if os.path.isdir(path):
+ return path
+ elif os.path.isfile(path):
+ return os.path.dirname(path)
+
+sys.path.append(cur_file_dir()+"/lib")
+#sys.path.append("/home/anbc/work_folder/andbug_work/andbug/lib")
+
def main(args):
import andbug, andbug.cmd, andbug.command
diff --git a/data/monitor_fun.conf b/data/monitor_fun.conf
new file mode 100644
index 0000000..9b25689
--- /dev/null
+++ b/data/monitor_fun.conf
@@ -0,0 +1,332 @@
+#监控内容:
+#测试使用
+
+
+in android.widget.Toast.makeText
+######################################
+#文件相关的监控项
+######################################
+#in out inout 监控的类型,包括:in函数调用时监控; out 函数调用完成时监控; inout函数调用时和调用完成后两个点都监控
+in java.io.File.
+in java.io.File.createTempFile
+out java.io.File.listFiles
+out java.io.File.list
+in java.io.File.delete()
+in java.io.File.deleteOnExit()
+out java.io.File.mkdir()
+out java.io.File.mkdirs()
+in java.io.File.renameTo(File dest)
+#
+in java.io.FileInputStream.
+out java.io.FileInputStream.read
+#
+in java.io.FileOutputStream.
+in java.io.FileOutputStream.write
+
+#################################
+#TelephonyManage相关行为监控
+#################################
+#异常出错
+in android.content.Context.getSystemService
+#目前暂时不支持函数返回位置监控(out模式),暂时设为(in模式)
+in android.telephony.TelephonyManager.getAllCellInfo
+in android.telephony.TelephonyManager.getCallState
+in android.telephony.TelephonyManager.getCellLocation
+in android.telephony.TelephonyManager.getDataActivity
+in android.telephony.TelephonyManager.getDataState
+#获取设备ID
+in android.telephony.TelephonyManager.getDeviceId
+in android.telephony.TelephonyManager.getDeviceSoftwareVersion
+in android.telephony.TelephonyManager.getGroupIdLevel1
+#获取手机号
+in android.telephony.TelephonyManager.getLine1Number
+in android.telephony.TelephonyManager.getNeighboringCellInfo
+in android.telephony.TelephonyManager.getNetworkCountryIso
+in android.telephony.TelephonyManager.getNetworkOperator
+#获得运营商名称
+in android.telephony.TelephonyManager.getNetworkOperatorName
+in android.telephony.TelephonyManager.getNetworkType
+in android.telephony.TelephonyManager.getPhoneType
+in android.telephony.TelephonyManager.getSimCountryIso
+in android.telephony.TelephonyManager.getSimOperator
+in android.telephony.TelephonyManager.getSimOperatorName
+#取出IMEI编号
+in android.telephony.TelephonyManager.getSimSerialNumber
+in android.telephony.TelephonyManager.getSimState
+#获得IMSI运营商编号,移动:46000、46002;联通:46001;电信:46003
+in android.telephony.TelephonyManager.getSubscriberId
+in android.telephony.TelephonyManager.getVoiceMailAlphaTag
+in android.telephony.TelephonyManager.getVoiceMailNumber
+in android.telephony.TelephonyManager.hasIccCard
+in android.telephony.TelephonyManager.isNetworkRoaming
+in android.telephony.TelephonyManager.listen
+
+
+###################################################
+#GPS位置信息函数
+######################################################
+in android.location.LocationManager.addGpsStatusListener
+in android.location.LocationManager.addNmeaListener
+in android.location.LocationManager.addProximityAlert
+in android.location.LocationManager.getAllProviders
+in android.location.LocationManager.getBestProvider
+in android.location.LocationManager.getGpsStatus
+in android.location.LocationManager.getLastKnownLocation
+in android.location.LocationManager.getProvider
+in android.location.LocationManager.getProviders
+
+#########################################################
+#网络相关信息的获取
+##########################################################
+#获取网络的连接情况,wifi网络:ConnectivityManager.TYPE_WIFI;3G网络:ConnectivityManager.TYPE_MOBILE
+in android.net.ConnectivityManager.getActiveNetworkInfo
+in android.net.ConnectivityManager.getAllNetworkInfo
+in android.net.ConnectivityManager.getBackgroundDataSetting
+in android.net.ConnectivityManager.getNetworkInfo
+in android.net.ConnectivityManager.getNetworkPreference
+in android.net.ConnectivityManager.isActiveNetworkMetered
+#判断网络是否有效
+in android.net.ConnectivityManager.isNetworkTypeValid
+
+#######################################################
+#wifi相关信息获取
+########################################################
+in android.net.wifi.WifiManager.getConfiguredNetworks
+in android.net.wifi.WifiManager.getConnectionInfo
+in android.net.wifi.WifiManager.getDhcpInfo
+in android.net.wifi.WifiManager.getScanResults
+in android.net.wifi.WifiManager.getWifiState
+in android.net.wifi.WifiManager.isScanAlwaysAvailable
+in android.net.wifi.WifiManager.isWifiEnabled
+
+#####################################################
+#操作通讯录等sqllite数据的接口
+#####################################################
+#异常出错
+#in android.content.Context.getContentResolver()
+in android.content.ContentResolver.query
+in android.content.ContentResolver.insert
+in android.content.ContentResolver.delete
+in android.content.ContentResolver.update
+in android.app.Activity.managedQuery
+
+#############################################################
+#sqllit数据库操作函数
+#############################################################
+#异常出错
+#in android.database.sqlite.SQLiteOpenHelper.onCreate
+in android.database.sqlite.SQLiteOpenHelper.onUpgrade
+in android.database.sqlite.SQLiteOpenHelper.deleteDatabase
+in android.database.sqlite.SQLiteOpenHelper.getDatabaseName
+#以读的方式打开数据库
+in android.database.sqlite.SQLiteOpenHelper.getReadableDatabase
+#以写的方式打开数据库
+in android.database.sqlite.SQLiteOpenHelper.getWritableDatabase
+in android.database.sqlite.SQLiteDatabase.execSQL
+in android.database.sqlite.SQLiteDatabase.query
+in android.database.sqlite.SQLiteDatabase.insert
+in android.database.sqlite.SQLiteDatabase.delete
+in android.database.sqlite.SQLiteDatabase.update
+
+###############################################################
+#启动新的activity Intent.ACTION_SENDTO发送短信/Intent.ACTION_CALL拨打电话
+#######################################################################
+in android.content.Intent.
+in android.app.Activity.startActivity
+
+#发送短信
+in android.telephony.gsm.SmsManager.SendDataMessage
+#接收短信
+in android.telephony.SmsMessage.createFromPdu
+
+
+###################################################################
+#临时测试使用
+###################################################################
+
+
+
+###################################################################
+#网络通讯
+###################################################################
+##ServerSocket
+##
+in java.net.ServerSocket.
+in java.net.ServerSocket.accept
+in java.net.ServerSocket.bind
+in java.net.ServerSocket.close
+#Returns this socket's ServerSocketChannel, if one exists.
+in java.net.ServerSocket.getChannel
+#Gets the local IP address of this server socket or null if the socket is unbound.
+in java.net.ServerSocket.getInetAddress
+#Gets the local port of this server socket or -1 if the socket is unbound.
+in java.net.ServerSocket.getLocalPort
+#Gets the local socket address of this server socket or null if the socket is unbound.
+in java.net.ServerSocket.getLocalSocketAddress
+#Returns this socket's receive buffer size.
+in java.net.ServerSocket.getReceiveBufferSize
+#Gets the value of the socket option SocketOptions.SO_REUSEADDR.
+in java.net.ServerSocket.getReuseAddress
+#Gets the socket accept timeout.
+in java.net.ServerSocket.getSoTimeout
+#Returns whether this server socket is bound to a local address and port or not.
+in java.net.ServerSocket.sBound
+#Returns whether this server socket is closed or not.
+in java.net.ServerSocket.isClosed
+#Sets performance preferences for connection time, latency and bandwidth.
+in java.net.ServerSocket.setPerformancePreferences
+#Sets this socket's receive buffer size.
+in java.net.ServerSocket.setReceiveBufferSize
+#Sets the value for the socket option SocketOptions.SO_REUSEADDR.
+in java.net.ServerSocket.setReuseAddress
+#Sets the accept timeout in milliseconds for this socket.
+in java.net.ServerSocket.setSoTimeout
+#Sets the server socket implementation factory of this instance.
+in java.net.ServerSocket.setSocketFactory
+
+##Socket
+##
+in java.net.Socket.
+in java.net.Socket.bind
+in java.net.Socket.close
+in java.net.Socket.connect
+in java.net.Socket.getChannel
+in java.net.Socket.getInetAddress
+in java.net.Socket.getInputStream
+in java.net.Socket.getKeepAlive
+in java.net.Socket.getLocalAddress
+in java.net.Socket.getLocalPort
+in java.net.Socket.getLocalSocketAddress
+in java.net.Socket.getOOBInline
+in java.net.Socket.getOutputStream
+in java.net.Socket.getPort
+in java.net.Socket.getReceiveBufferSize
+in java.net.Socket.getRemoteSocketAddress
+in java.net.Socket.getReuseAddress
+in java.net.Socket.getSendBufferSize
+in java.net.Socket.getSoLinger
+in java.net.Socket.getSoTimeout
+in java.net.Socket.getTcpNoDelay
+in java.net.Socket.getTrafficClass
+in java.net.Socket.isBound
+in java.net.Socket.isClosed
+in java.net.Socket.isConnected
+#Returns whether the incoming channel of the socket has already been closed.
+in java.net.Socket.isInputShutdown
+in java.net.Socket.isOutputShutdown
+in java.net.Socket.sendUrgentData
+in java.net.Socket.setKeepAlive
+#Sets this socket's SO_OOBINLINE option.
+in java.net.Socket.setOOBInline
+in java.net.Socket.setPerformancePreferences
+in java.net.Socket.setReceiveBufferSize
+in java.net.Socket.setReuseAddress
+in java.net.Socket.setSendBufferSize
+in java.net.Socket.setSoLinger
+in java.net.Socket.setSoTimeout
+in java.net.Socket.setSocketImplFactory
+in java.net.Socket.setTcpNoDelay
+in java.net.Socket.setTrafficClass
+in java.net.Socket.shutdownInput
+in java.net.Socket.shutdownOutput
+
+#
+#URLConnection
+#
+in java.net.URLConnection.
+#Adds the given property to the request header.
+in java.net.URLConnection.addRequestProperty
+in java.net.URLConnection.connect
+in java.net.URLConnection.getAllowUserInteraction
+in java.net.URLConnection.getContentEncoding
+in java.net.URLConnection.getContentLength
+in java.net.URLConnection.getContentType
+in java.net.URLConnection.getDate
+in java.net.URLConnection.getDefaultAllowUserInteraction
+in java.net.URLConnection.getDefaultRequestProperty
+in java.net.URLConnection.getDefaultUseCaches
+in java.net.URLConnection.getDoInput
+in java.net.URLConnection.getExpiration
+in java.net.URLConnection.getFileNameMap
+in java.net.URLConnection.getHeaderField
+in java.net.URLConnection.getHeaderFieldDate
+in java.net.URLConnection.getHeaderFieldInt
+in java.net.URLConnection.getHeaderFieldKey
+in java.net.URLConnection.getHeaderFields
+in java.net.URLConnection.getIfModifiedSince
+in java.net.URLConnection.getInputStream
+in java.net.URLConnection.getLastModified
+in java.net.URLConnection.getOutputStream
+in java.net.URLConnection.getPermission
+in java.net.URLConnection.getReadTimeout
+in java.net.URLConnection.getRequestProperties
+in java.net.URLConnection.getRequestProperty
+in java.net.URLConnection.getURL
+in java.net.URLConnection.getUseCaches
+in java.net.URLConnection.guessContentTypeFromName
+in java.net.URLConnection.guessContentTypeFromStream
+in java.net.URLConnection.setAllowUserInteraction
+in java.net.URLConnection.setConnectTimeout
+in java.net.URLConnection.setContentHandlerFactory
+in java.net.URLConnection.setDefaultAllowUserInteraction
+in java.net.URLConnection.setDefaultRequestProperty
+in java.net.URLConnection.setDefaultUseCaches
+in java.net.URLConnection.setDoInput
+in java.net.URLConnection.setDoOutput
+in java.net.URLConnection.setFileNameMap
+in java.net.URLConnection.setIfModifiedSince
+in java.net.URLConnection.setReadTimeout
+in java.net.URLConnection.setRequestProperty
+in java.net.URLConnection.setUseCaches
+
+
+#SocketImpl 使用资料不详
+
+#
+#HttpURLConnection
+#
+in java.net.HttpURLConnection.
+in java.net.HttpURLConnection.disconnect
+in java.net.HttpURLConnection.getContentEncoding
+in java.net.HttpURLConnection.getErrorStream
+in java.net.HttpURLConnection.getFollowRedirects
+in java.net.HttpURLConnection.getHeaderFieldDate
+in java.net.HttpURLConnection.getInstanceFollowRedirects
+in java.net.HttpURLConnection.getPermission
+in java.net.HttpURLConnection.getRequestMethod
+in java.net.HttpURLConnection.getResponseCode
+in java.net.HttpURLConnection.getResponseMessage
+in java.net.HttpURLConnection.setChunkedStreamingMode
+in java.net.HttpURLConnection.setFixedLengthStreamingMode
+in java.net.HttpURLConnection.setFollowRedirects
+in java.net.HttpURLConnection.setInstanceFollowRedirects
+in java.net.HttpURLConnection.setRequestMethod
+in java.net.HttpURLConnection.usingProxy
+
+#
+#HttpClient
+#
+in org.apache.http.client.HttpClient.execute
+in android.net.http.AndroidHttpClient.
+in android.net.http.AndroidHttpClient.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224Andbug\344\271\213\345\237\272\346\234\254\346\223\215\344\275\234.doc" "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224Andbug\344\271\213\345\237\272\346\234\254\346\223\215\344\275\234.doc"
new file mode 100644
index 0000000..4834a3f
Binary files /dev/null and "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224Andbug\344\271\213\345\237\272\346\234\254\346\223\215\344\275\234.doc" differ
diff --git "a/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224andbug\345\270\270\350\247\201\351\227\256\351\242\230\346\261\207\346\200\273.doc" "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224andbug\345\270\270\350\247\201\351\227\256\351\242\230\346\261\207\346\200\273.doc"
new file mode 100644
index 0000000..fcabe6c
Binary files /dev/null and "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224andbug\345\270\270\350\247\201\351\227\256\351\242\230\346\261\207\346\200\273.doc" differ
diff --git "a/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224Andbug\346\211\251\345\261\225\345\212\237\350\203\275.doc" "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224Andbug\346\211\251\345\261\225\345\212\237\350\203\275.doc"
new file mode 100644
index 0000000..d45f521
Binary files /dev/null and "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224Andbug\346\211\251\345\261\225\345\212\237\350\203\275.doc" differ
diff --git "a/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\345\233\233\357\274\211\342\200\224\342\200\224Andbug\350\241\245\345\205\205\350\260\203\350\257\225\345\212\237\350\203\275.doc" "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\345\233\233\357\274\211\342\200\224\342\200\224Andbug\350\241\245\345\205\205\350\260\203\350\257\225\345\212\237\350\203\275.doc"
new file mode 100644
index 0000000..1775957
Binary files /dev/null and "b/doc/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\345\233\233\357\274\211\342\200\224\342\200\224Andbug\350\241\245\345\205\205\350\260\203\350\257\225\345\212\237\350\203\275.doc" differ
diff --git a/doc/readme.txt b/doc/readme.txt
new file mode 100644
index 0000000..f73b652
--- /dev/null
+++ b/doc/readme.txt
@@ -0,0 +1,7 @@
+andbug
+1. can't get the argument and var when the apk was processed by ProGuard tool.
+ [problem]:debug the andbug program found that client sent "call jdwp 0x06 05" to get the function table information,but return
+the result is that the count of arg is 1 and the count of var is zero.
+ [sulution]:
+ (1).find the method of PrgGuard tool to process apk.
+ (2).get dalvik machine detail for jdwp protocal.
\ No newline at end of file
diff --git "a/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224Andbug\344\271\213\345\237\272\346\234\254\346\223\215\344\275\234.pdf" "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224Andbug\344\271\213\345\237\272\346\234\254\346\223\215\344\275\234.pdf"
new file mode 100644
index 0000000..c125eea
Binary files /dev/null and "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\200\357\274\211\342\200\224\342\200\224Andbug\344\271\213\345\237\272\346\234\254\346\223\215\344\275\234.pdf" differ
diff --git "a/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224andbug\345\270\270\350\247\201\351\227\256\351\242\230\346\261\207\346\200\273.pdf" "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224andbug\345\270\270\350\247\201\351\227\256\351\242\230\346\261\207\346\200\273.pdf"
new file mode 100644
index 0000000..cb32633
Binary files /dev/null and "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\270\211\357\274\211\342\200\224\342\200\224andbug\345\270\270\350\247\201\351\227\256\351\242\230\346\261\207\346\200\273.pdf" differ
diff --git "a/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224Andbug\346\211\251\345\261\225\345\212\237\350\203\275.pdf" "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224Andbug\346\211\251\345\261\225\345\212\237\350\203\275.pdf"
new file mode 100644
index 0000000..7269ca7
Binary files /dev/null and "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\344\272\214\357\274\211\342\200\224\342\200\224Andbug\346\211\251\345\261\225\345\212\237\350\203\275.pdf" differ
diff --git "a/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\345\233\233\357\274\211\342\200\224\342\200\224Andbug\350\241\245\345\205\205\350\260\203\350\257\225\345\212\237\350\203\275.pdf" "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\345\233\233\357\274\211\342\200\224\342\200\224Andbug\350\241\245\345\205\205\350\260\203\350\257\225\345\212\237\350\203\275.pdf"
new file mode 100644
index 0000000..21f6e8c
Binary files /dev/null and "b/info/Android\345\212\250\346\200\201\351\200\206\345\220\221\345\210\206\346\236\220\345\267\245\345\205\267\357\274\210\345\233\233\357\274\211\342\200\224\342\200\224Andbug\350\241\245\345\205\205\350\260\203\350\257\225\345\212\237\350\203\275.pdf" differ
diff --git a/lib/andbug/__init__.py b/lib/andbug/__init__.py
index da98295..883fc64 100644
--- a/lib/andbug/__init__.py
+++ b/lib/andbug/__init__.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,6 +15,9 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
+
+
+
'''
AndBug is a debugger targeting the Android platform's Dalvik virtual machine
intended for reverse engineers and developers. It uses the same interfaces
@@ -20,6 +26,7 @@
examine process state, and even perform changes.
'''
+
import andbug.jdwp
import andbug.proto
import andbug.log
diff --git a/lib/andbug/cmd/__init__.py b/lib/andbug/cmd/__init__.py
index 97cca29..56fbbd8 100644
--- a/lib/andbug/cmd/__init__.py
+++ b/lib/andbug/cmd/__init__.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
'''
this package contains andbug commands used by the andbug interface
'''
\ No newline at end of file
diff --git a/lib/andbug/cmd/break-detail.py b/lib/andbug/cmd/break-detail.py
new file mode 100644
index 0000000..b151ce5
--- /dev/null
+++ b/lib/andbug/cmd/break-detail.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+## Copyright 2011, IOActive, Inc. All rights reserved.
+##
+## AndBug is free software: you can redistribute it and/or modify it under
+## the terms of version 3 of the GNU Lesser General Public License as
+## published by the Free Software Foundation.
+##
+## AndBug is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with AndBug. If not, see .
+
+'implementation of the "break" command'
+
+import andbug.command, andbug.screed, andbug.options
+from Queue import Queue
+from andbug import log
+
+'''
+该命令用来跟踪触发断点时各参数的信息
+'''
+
+def parse_frame_detail(frame):
+ '''
+ 函数功能:解析一个堆栈帧的详细情况
+ '''
+ all_var_infor = frame.values
+ for var_name in all_var_infor:
+ #print "%s = %s" %(var_name, all_var_infor[var_name])
+ andbug.screed.item(var_name + ":" + str(all_var_infor[var_name]))
+
+def report_hit(t):
+ '''
+ 处理METHOD_ENTRY事件回调函数,
+ t有两个参数,分别 t[0] thread
+ t[1] Location
+ '''
+ t = t[0] #t是一个Thread类型的变量
+ with andbug.screed.section("Breakpoint hit in %s, process suspended." % t):
+ t.sess.suspend() #暂停当前线程
+ for f in t.frames: #t.frames是返回当前的堆栈信息
+ name = str(f.loc)
+ if f.native: #判断堆栈中函数的类型,是否是内部函数。如dalvik.system.NativeStart.main([Ljava/lang/String;)V
+ name += ' '
+ with andbug.screed.refer(name):
+ log.debug("study", "function name:" + str(name) )
+ parse_frame_detail(f)
+
+
+def cmd_break_methods(ctxt, cpath, mpath):
+ for c in ctxt.sess.classes(cpath):
+ for m in c.methods(mpath):
+ l = m.firstLoc #这里会调用jdwp的命令
+ if l.native: #等于true,无法设置断点
+ andbug.screed.item('Could not hook native %s' % l)
+ continue
+ l.hook(func = report_hit) #调用断点设置函数
+ andbug.screed.item('Hooked %s' % l)
+
+def cmd_break_classes(ctxt, cpath):
+ for c in ctxt.sess.classes(cpath): #c为vm.py中,Class类的一个对象
+ c.hookEntries(func = report_hit)
+ andbug.screed.item('Hooked %s' % c)
+
+@andbug.command.action(
+ ' []', name='break-detail', aliases=('b',), shell=True
+)
+def cmd_break(ctxt, cpath, mquery=None):
+ 'suspends the process when a method is called'
+ cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery)
+ #print "cpath=" + cpath + "\t mname=" + mname + "\t mjni=" + mjni
+ #输出的结果是:cpath=Lcom/example/test/MainActivity$1; mname=onClick mjni=(Landroid/view/View;)V
+ with andbug.screed.section('Setting Hooks'):
+ if mname is None:
+ cmd_break_classes(ctxt, cpath)
+ else:
+ cmd_break_methods(ctxt, cpath, mname)
+
+ ctxt.block_exit()
diff --git a/lib/andbug/cmd/break.py b/lib/andbug/cmd/break.py
index 50dfb05..856e893 100644
--- a/lib/andbug/cmd/break.py
+++ b/lib/andbug/cmd/break.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -17,28 +20,43 @@
import andbug.command, andbug.screed, andbug.options
from Queue import Queue
+def parse_frame_detail(frame):
+ '''
+ 函数功能:解析一个堆栈帧的详细情况
+ '''
+ all_var_infor = frame.values
+ for var_name in all_var_infor:
+ #print "%s = %s" %(var_name, all_var_infor[var_name])
+ andbug.screed.item(var_name + ":" + str(all_var_infor[var_name]))
+
def report_hit(t):
- t = t[0]
+ '''
+ 处理METHOD_ENTRY事件回调函数,
+ t有两个参数,分别 t[0] thread
+ t[1] Location
+ '''
+ t = t[0] #t是一个Thread类型的变量
with andbug.screed.section("Breakpoint hit in %s, process suspended." % t):
- t.sess.suspend()
- for f in t.frames:
+ #t.sess.suspend() #暂停当前线程
+ count = 10 if len(t.frames) > 10 else len(t.frames)
+ for f in t.frames[0: count]:
name = str(f.loc)
- if f.native:
+ if f.native: #判断堆栈中函数的类型,是否是内部函数。如dalvik.system.NativeStart.main([Ljava/lang/String;)V
name += ' '
andbug.screed.item(name)
def cmd_break_methods(ctxt, cpath, mpath):
for c in ctxt.sess.classes(cpath):
for m in c.methods(mpath):
- l = m.firstLoc
- if l.native:
+ l = m.firstLoc #这里会调用jdwp的命令
+ if l.native: #等于true,无法设置断点
andbug.screed.item('Could not hook native %s' % l)
continue
h = l.hook(func = report_hit)
andbug.screed.item('Hooked %s' % h)
def cmd_break_classes(ctxt, cpath):
- for c in ctxt.sess.classes(cpath):
+ for c in ctxt.sess.classes(cpath): #c为vm.py中,Class类的一个对象
h = c.hookEntries(func = report_hit)
andbug.screed.item('Hooked %s' % h)
@@ -67,6 +85,8 @@ def cmd_break_line(ctxt, cpath, mpath, line):
def cmd_break(ctxt, cpath, mquery=None, line=None):
'set breakpoint'
cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery)
+ #print "cpath=" + cpath + "\t mname=" + mname + "\t mjni=" + mjni
+ #输出的结果是:cpath=Lcom/example/test/MainActivity$1; mname=onClick mjni=(Landroid/view/View;)V
if line is not None:
if line != 'show':
line = int(line)
diff --git a/lib/andbug/cmd/break_list.py b/lib/andbug/cmd/break_list.py
index aaa70b6..eea0d2c 100644
--- a/lib/andbug/cmd/break_list.py
+++ b/lib/andbug/cmd/break_list.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, Felipe Barriga Richards .
## All rights reserved.
##
@@ -17,7 +20,7 @@
import andbug.command, andbug.screed
-@andbug.command.action('', name='break-list', shell=True)
+@andbug.command.action('', name='break-list', aliases=('bl',), shell=True)
def break_list(ctxt):
'list active breakpoints/hooks'
with andbug.screed.section('Active Hooks'):
diff --git a/lib/andbug/cmd/class_detail.py b/lib/andbug/cmd/class_detail.py
new file mode 100644
index 0000000..5da7609
--- /dev/null
+++ b/lib/andbug/cmd/class_detail.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+## Copyright 2011, IOActive, Inc. All rights reserved.
+##
+## AndBug is free software: you can redistribute it and/or modify it under
+## the terms of version 3 of the GNU Lesser General Public License as
+## published by the Free Software Foundation.
+##
+## AndBug is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with AndBug. If not, see .
+
+'implementation of the "classes" command'
+
+import andbug.command, andbug.screed
+import types
+
+@andbug.command.action('[]')
+def class_detail(ctxt, class_name=None):
+ 'lists loaded classes. if no partial class name supplied, list all classes.'
+ with andbug.screed.section('Loaded Class-detail'):
+
+ #classesInfor = ctxt.sess.classes()
+ #print type(classesInfor) classesInfor 的类型是
+ for c in ctxt.sess.classes(): #ctxt.sess.classes()函数来获取类的信息
+ #print type(c) 返回的类型是
+ n = c.jni #获取类中的jni成员变量
+ if n.startswith('L') and n.endswith(';'):
+ n = n[1:-1].replace('/', '.')
+ else:
+ continue
+
+ if n==class_name:
+ andbug.screed.item(n)
+
+ show_method_infor(c)
+
+ show_static_infor(c)
+
+ show_field_infor(c)
+
+def show_method_infor(class_infor):
+ '''
+ 展示指定类中方法的信息
+ '''
+ andbug.screed.section('Methods Infor:')
+ for m in class_infor.methods():
+ andbug.screed.item(str(m))
+
+def show_static_infor(class_infor):
+ '''
+ 展示类中静态变量的信息
+ '''
+ andbug.screed.section('Statics Infor:')
+ for k, v in class_infor.statics.iteritems():
+ andbug.screed.item("%s = %s" % (k, v)) #在这里会到用vm.String类中的__str__函数,进而调用data(self)发起"call jdwp 0x0A 01"命令
+
+def show_field_infor(classinfor):
+ '''
+ 展示类中成员变量的信息
+ '''
+ andbug.screed.section('Fields Infor:')
+ for field in classinfor.fieldList:
+ #print field.get_property()
+ #print field.jni
+ #print field.name
+ #field_str = field.get_property() + " " + str(field.jni) + " "+ str(field.name)
+ andbug.screed.item(str(field))
+
+
+
\ No newline at end of file
diff --git a/lib/andbug/cmd/class_trace.py b/lib/andbug/cmd/class_trace.py
index 6bae26e..a4fe0c3 100644
--- a/lib/andbug/cmd/class_trace.py
+++ b/lib/andbug/cmd/class_trace.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
diff --git a/lib/andbug/cmd/classes.py b/lib/andbug/cmd/classes.py
index 3179d15..99eaceb 100644
--- a/lib/andbug/cmd/classes.py
+++ b/lib/andbug/cmd/classes.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -15,21 +18,27 @@
'implementation of the "classes" command'
import andbug.command, andbug.screed
+import types
@andbug.command.action('[]')
def classes(ctxt, expr=None):
- 'lists loaded classes. if no partial class name supplied, list all classes.'
+ 'lists loaded classes. if no partial class name supplied, list all classes.展示一个类的详情'
with andbug.screed.section('Loaded Classes'):
- for c in ctxt.sess.classes():
- n = c.jni
- if n.startswith('L') and n.endswith(';'):
- n = n[1:-1].replace('/', '.')
- else:
- continue
+
+ #classesInfor = ctxt.sess.classes()
+ #print type(classesInfor) classesInfor 的类型是
+ for c in ctxt.sess.classes(): #ctxt.sess.classes()函数来获取类的信息
+ #print type(c) 返回的类型是
+ n = c.jni #获取类中的jni成员变量
+ if n.startswith('L') and n.endswith(';'):
+ n = n[1:-1].replace('/', '.')
+ else:
+ continue
- if expr is not None:
- if n.find(expr) >= 0:
- andbug.screed.item(n)
- else:
+ if expr is not None:
+ #通过正则判断要输出的类信息
+ if n.find(expr) >= 0:
andbug.screed.item(n)
-
+ else:
+ andbug.screed.item(n)
+
diff --git a/lib/andbug/cmd/dump.py b/lib/andbug/cmd/dump.py
index 042afb5..5b6299a 100644
--- a/lib/andbug/cmd/dump.py
+++ b/lib/andbug/cmd/dump.py
@@ -1,4 +1,7 @@
-## Copyright 2011, IOActive, Inc. All rights reserved.
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+ ## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
## the terms of version 3 of the GNU Lesser General Public License as
@@ -25,24 +28,33 @@ def find_last_method_line(source, first_line):
@andbug.command.action(' []')
def dump(ctxt, cpath, mquery=None):
'dumps methods using original sources or apktool sources'
+
cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery)
for method in ctxt.sess.classes(cpath).methods(name=mname, jni=mjni):
source = False
- klass = method.klass.name
+ #print "method.firstLoc.line="+ str(method.firstLoc.line)
+ klass = method.klass.name
- first_line = method.firstLoc.line
+
+ first_line = method.firstLoc.line #对于com.example.test.MainActivity.test() first_line的值是33
if first_line is None:
print '!! could not determine first line of', method
continue
- source = andbug.source.load_source(klass)
+ source = andbug.source.load_source(klass) #用一个map类型保存源代码
if not source:
print '!! could not find source for', klass
continue
last_line = method.lastLoc.line or find_last_method_line(source, first_line)
+ if last_line is False:
+ if method.lineTable!=None:
+ lineTable=sorted(method.lineTable.iteritems(), key=lambda asd:asd[0], reverse=True)
+
+ last_line = lineTable[0][0]
+
if last_line is False:
print '!! could not determine last line of', method
continue
- andbug.source.dump_source(source[first_line:last_line], str(method))
\ No newline at end of file
+ andbug.source.dump_source(source[first_line:last_line], str(method)) #将源代码输出
\ No newline at end of file
diff --git a/lib/andbug/cmd/exit.py b/lib/andbug/cmd/exit.py
index 4b640e4..453f960 100644
--- a/lib/andbug/cmd/exit.py
+++ b/lib/andbug/cmd/exit.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
diff --git a/lib/andbug/cmd/frame_value.py b/lib/andbug/cmd/frame_value.py
new file mode 100644
index 0000000..7d0e9d9
--- /dev/null
+++ b/lib/andbug/cmd/frame_value.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+## Copyright 2011, IOActive, Inc. All rights reserved.
+##
+## AndBug is free software: you can redistribute it and/or modify it under
+## the terms of version 3 of the GNU Lesser General Public License as
+## published by the Free Software Foundation.
+##
+## AndBug is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with AndBug. If not, see .
+
+'implementation of the "frame_value" command'
+
+import andbug.command, andbug.screed, andbug.options
+
+@andbug.command.action('', aliases=('fv',))
+def frame_value(ctxt, threadName, frameName):
+ '''
+ 函数功能:根据指定的线程名称,堆栈位置,获取相应堆栈中参数的信息
+ '''
+
+ thread = ctxt.sess.threads(threadName)
+ frames = thread.frames() #!!!!!!调用失败,还没找到原因
+ for f in thread.frames: #t.frames是返回当前的堆栈信息
+ name = str(f.loc)
+ if name.find(frameName)==-1:
+ continue
+ if f.native: #判断堆栈中函数的类型,是否是内部函数。如dalvik.system.NativeStart.main([Ljava/lang/String;)V
+ name += ' '
+ with andbug.screed.refer(name):
+ for var_name in f:
+ andbug.screed.item(var_name + ":" + str(f[var_name]))
+
+
+
diff --git a/lib/andbug/cmd/help.py b/lib/andbug/cmd/help.py
index b13b712..7183176 100644
--- a/lib/andbug/cmd/help.py
+++ b/lib/andbug/cmd/help.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
diff --git a/lib/andbug/cmd/inspect.py b/lib/andbug/cmd/inspect.py
index e661c20..4020ac1 100644
--- a/lib/andbug/cmd/inspect.py
+++ b/lib/andbug/cmd/inspect.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, Felipe Barriga Richards .
## All rights reserved.
##
@@ -16,13 +19,15 @@
'implementation of the "inspect" command'
import andbug.command, andbug.screed
+from andbug import log
def find_object(ctxt, oid):
for t in ctxt.sess.threads():
for f in t.frames:
for k, v in f.values.items():
+ log.debug("study", "In inspect.find_object k=" + str(k) + "\t v=" + str(v))
if type(v) is andbug.vm.Object and v.oid == oid:
- return (v, t)
+ return (v, t) #v:变量的值, t:所在的线程信息
return None
@andbug.command.action('')
@@ -39,7 +44,9 @@ def inspect(ctxt, oid):
obj, thread = rtval
with andbug.screed.section('object <%s> %s in %s'
% (str(obj.oid), str(obj.jni), str(thread))):
+ log.debug("study", "In inspect obj.oid=" + str(obj.oid) + "\t jni=" + str(obj.jni) + "\t thread=" + str(thread))
for k, v in obj.fields.items():
+ log.debug("study", "In inspect k=" + str(k) + "\t v=" + str(v) + "\t type=" + type(v).__name__)
andbug.screed.item('%s=%s <%s>' % (k, v, type(v).__name__))
except ValueError:
print('!! error, invalid oid param. expecting and got <%s>.'
diff --git a/lib/andbug/cmd/method_detail.py b/lib/andbug/cmd/method_detail.py
new file mode 100644
index 0000000..a2d3d0e
--- /dev/null
+++ b/lib/andbug/cmd/method_detail.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+## Copyright 2011, IOActive, Inc. All rights reserved.
+##
+## AndBug is free software: you can redistribute it and/or modify it under
+## the terms of version 3 of the GNU Lesser General Public License as
+## published by the Free Software Foundation.
+##
+## AndBug is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with AndBug. If not, see .
+
+'implementation of the "methods-detail" command'
+
+import andbug.command, andbug.options
+from andbug import log
+
+def show_method_access_flag(methodInfo):
+ '''
+ 函数功能:显示方法的访问属性
+ '''
+
+ accessFlag = ""
+ if methodInfo.public!=0:
+ accessFlag += "public" + "\t"
+ if methodInfo.private!=0:
+ accessFlag += "private" + "\t"
+ if methodInfo.protected!=0:
+ accessFlag += "protected" + "\t"
+ if methodInfo.static!=0:
+ accessFlag += "static" + "\t"
+ if methodInfo.final!=0:
+ accessFlag += "final" + "\t"
+ if methodInfo.synchronized!=0:
+ accessFlag += "synchronized" + "\t"
+ if methodInfo.bridge!=0:
+ accessFlag += "bridge" + "\t"
+ if methodInfo.varargs!=0:
+ accessFlag += "varargs" + "\t"
+ if methodInfo.native!=0:
+ accessFlag += "native" + "\t"
+ if methodInfo.abstract!=0:
+ accessFlag += "abstract" + "\t"
+ if methodInfo.strict!=0:
+ accessFlag += "strict" + "\t"
+ if methodInfo.synthetic!=0:
+ accessFlag += "synthetic" + "\t"
+ with andbug.screed.section("ACCESS_FLAG:"):
+ if len(accessFlag) >0:
+ andbug.screed.item("%s"%(accessFlag))
+
+
+def show_method_location(methodInfo):
+ '''
+ 函数功能:展示函数的loc和line信息
+ '''
+ if methodInfo.abstract!=0:
+ #纯需函数,没有任何代码实现
+ return
+
+ lineTable=sorted(methodInfo.lineTable.iteritems(), key=lambda asd:asd[0], reverse=False)
+ with andbug.screed.section("LOCATION lineTable:"):
+ for lineItem in lineTable:
+ andbug.screed.item("line=%s loc=%s" %(lineItem[0], lineItem[1].loc ))
+
+
+def show_method_slot(methodInfo):
+ '''
+ 函数功能:展示函数的参数和自变量信息
+ '''
+ if methodInfo.abstract!=0:
+ #纯需函数,没有任何代码实现
+ andbug.screed.section("ARGUMENT:")
+ andbug.screed.section("VARIABLE:")
+ return
+
+
+ thisIndex=0
+ for arg in methodInfo.slots:
+ thisIndex= thisIndex+1
+ if arg.name=="this":
+ break
+
+ thisIndex= thisIndex-1
+ with andbug.screed.section("ARGUMENT:"):
+ for i in range(thisIndex, methodInfo.slot_cnt):
+ arg = methodInfo.slots[i]
+ if arg.jni[0]=='L':
+ andbug.screed.item(arg.jni[1:-1]+ " " +arg.name)
+ elif arg.jni[0]=='[':
+ andbug.screed.item(show_type(arg.jni)+ " " +arg.name)
+ else:
+ andbug.screed.item(andbug.vm.get_variable_type(arg.jni) + " " +arg.name)
+
+ with andbug.screed.section("VARIABLE:"):
+ for i in range(0, thisIndex):
+ arg = methodInfo.slots[i]
+ #print arg.jni
+ if arg.jni[0]=='L':
+ andbug.screed.item(arg.jni[1:-1]+ " " +arg.name)
+ elif arg.jni[0]=='[':
+ andbug.screed.item(show_type(arg.jni)+ " " +arg.name)
+ else:
+ andbug.screed.item(andbug.vm.get_variable_type(arg.jni) + " " +arg.name)
+
+def show_type(jni):
+ showTypeList=[]
+ for item in jni:
+ if item=="[":
+ showTypeList.insert(0,"[]")
+ else:
+ showTypeList.insert(0,andbug.vm.get_variable_type(item))
+ showType= ""
+ for item in showTypeList:
+ showType += item
+ return showType
+
+@andbug.command.action(' []')
+def method_detail(ctxt, cpath, mquery=None):
+ '获取一个指定成员函数的详细信息'
+ cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery) #cpath=Lcom/example/test/MainActivity; mname=onCreate [函数名] mjni=None[可能是参数信息不确定]
+
+
+ if mname==None:
+ andbug.screed.item("please Input methods name")
+ return
+
+ title = "Methods " + ((cpath + "->" + mquery) if mquery else (cpath))
+ with andbug.screed.section(title):
+ for m in ctxt.sess.classes(cpath).methods(name=mname, jni=mjni):
+ with andbug.screed.section('Method Detail:'):
+ andbug.screed.item(str(m))
+
+ show_method_access_flag(m)
+ show_method_slot(m)
+ show_method_location(m)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/andbug/cmd/method_trace.py b/lib/andbug/cmd/method_trace.py
index 96d7ab9..cbc416d 100644
--- a/lib/andbug/cmd/method_trace.py
+++ b/lib/andbug/cmd/method_trace.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -48,6 +51,9 @@ def method_trace(ctxt, mpath):
'reports calls to specific dalvik method'
cpath, mname, mjni = andbug.options.parse_mquery(".".join(mpath.split('.')[0:-1]), mpath.split('.')[-1])
+ print "cpath=" + cpath
+ print "mname=" + mname
+ print "mjni=" + mjni
with andbug.screed.section('Setting Hooks'):
cmd_hook_methods(ctxt, cpath, mname)
diff --git a/lib/andbug/cmd/methods.py b/lib/andbug/cmd/methods.py
index c07c80c..bbf4117 100644
--- a/lib/andbug/cmd/methods.py
+++ b/lib/andbug/cmd/methods.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -15,12 +18,17 @@
'implementation of the "methods" command'
import andbug.command, andbug.options
+from andbug import log
-@andbug.command.action(' []')
+@andbug.command.action(' []', aliases=('m',))
def methods(ctxt, cpath, mquery=None):
'lists the methods of a class'
- cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery)
+ cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery) #cpath=Lcom/example/test/MainActivity; mname=onCreate [函数名] mjni=None[可能是参数信息不确定]
+ infor = "cpath="+ str(cpath) + "\t mname="+ str(mname) + "\t mjni=" + str(mjni);
+ log.debug("study", infor);
+
+
title = "Methods " + ((cpath + "->" + mquery) if mquery else (cpath))
with andbug.screed.section(title):
- for m in ctxt.sess.classes(cpath).methods(name=mname, jni=mjni):
+ for m in ctxt.sess.classes(cpath).methods(filtername=mname, jni=mjni):
andbug.screed.item(str(m))
diff --git a/lib/andbug/cmd/monitor.py b/lib/andbug/cmd/monitor.py
new file mode 100644
index 0000000..f903dc8
--- /dev/null
+++ b/lib/andbug/cmd/monitor.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+## Copyright 2011, IOActive, Inc. All rights reserved.
+##
+## AndBug is free software: you can redistribute it and/or modify it under
+## the terms of version 3 of the GNU Lesser General Public License as
+## published by the Free Software Foundation.
+##
+## AndBug is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with AndBug. If not, see .
+
+'implementation of the "mtrace" command'
+
+'''
+文件功能:该模块用于对android应用实现函数粒度的监控功能
+'''
+
+import logging
+import shlex
+import json
+
+
+import andbug.command, andbug.screed, andbug.options, andbug.vm
+import andbug.config
+from andbug import log
+
+
+
+BANNER = 'AndBug (C) 2011 Scott W. Dunlop '
+'''
+logFilePath = 'myapp.log'
+logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
+ datefmt='%a, %d %b %Y %H:%M:%S',
+ filename=logFilePath,
+ filemode='w')
+'''
+
+
+
+def report_hit(t):
+ t = t[0]
+ try:
+ with andbug.screed.section("trace-monitor %s" % t):
+ #获取函数信息
+ funInfor={}
+ f = t.frames[0]
+ name = str(f.loc)
+ funInfor["thread"] = str(t)
+ funInfor["name"] = name
+ funInfor["is_native"] = f.native
+
+ with andbug.screed.item(name):
+ #获取函数的参数信息
+ args={}
+ for k, v in f.values.items():
+ andbug.screed.item( "%s=%s" %(k, v))
+ valueType = str(type(v))
+ index = valueType.find("class")
+ if index==-1:
+ #主类型变量,int long等
+ args[k]=v
+ else:
+ #对象类型变量
+ pass
+ args[k]=v.genJson()
+
+
+
+
+ funInfor["args"]= args
+
+ monitor_infor = json.dumps(funInfor)
+ logging.debug(monitor_infor)
+ #log.debug("stdu", monitor_infor)
+ finally:
+ t.resume()
+
+#cmd_hook_methods(ctxt, monitorType, cpath, mname)
+def cmd_hook_methods(ctxt, monitorType, cpath, mpath):
+
+ classesInfor = ctxt.sess.classes(cpath)
+ if len(classesInfor)==0:
+ return False
+
+ for c in classesInfor:
+ print "classInfor:" + str(c)
+ for m in c.methods(mpath):
+ print "method:" + str(m)
+
+ loc = m.firstLoc
+ if loc==None:
+ print "firstLoc is None"
+ continue
+
+ andbug.screed.item('Hooked [%s] %s'%(monitorType, loc))
+ if loc.native:
+ andbug.screed.item('Could not hook native %s' % loc)
+ continue
+
+
+ if monitorType=="in":
+ loc.hook(func = report_hit)
+
+ elif monitorType == "out":
+ print "hook out"
+ loc.hookOut(func = report_hit)
+ else:
+ loc.hook(func = report_hit)
+ loc.hookOut(func = report_hit)
+
+ return True
+
+def HookGoGoGo(ctxt, hookFailedInfor):
+ '''
+ 函数:将hook失败的函数继续进行hook
+ 参数:hookFailedInfor 记录hook失败的函数列表
+ 返回值:
+ '''
+ if hookFailedInfor==None:
+ return False, None
+
+ newHookFailedInfor=[]
+
+
+ for funInfor in hookFailedInfor:
+ monitorType = funInfor["monitorType"]
+ cpath = funInfor["cpath"]
+ mname = funInfor["mname"]
+ flag=cmd_hook_methods(ctxt, monitorType, cpath, mname)
+ if flag==False:
+ newHookFailedInfor.append(funInfor)
+
+ if len(newHookFailedInfor)==0:
+ print "hook all function"
+ return True, None
+ else:
+ return False, newHookFailedInfor
+
+def ParseMonitorConfItem(configInforItem):
+ '''
+ 函数功能:用来解析一条监控配置信息
+ 参数:一条监控配置信息
+ 返回值:
+ in java.io.File.
+ '''
+ configInforItem = configInforItem.strip()
+ if configInforItem==None or len(configInforItem)==0 or configInforItem[0]=='#':
+ #如果是注释行或是空行,返回None
+ return False, None, None, None, None
+ confInfor = configInforItem.split(' ')
+ monitorType = confInfor[0]
+ #对于正常配置信息,返回一个list,第一个元素是中断类型(in,out,inout),第二个元素是中断函数的位置。
+ cpath, mname, mjni = andbug.options.parse_mquery(".".join(confInfor[1].split('.')[0:-1]), confInfor[1].split('.')[-1])
+ return True, monitorType, cpath, mname, mjni
+
+
+
+def input():
+ return raw_input('>> ')
+
+'''
+@andbug.command.action(
+ '', name='monitor', aliases=('mo'), shell=True
+)
+'''
+@andbug.command.action(
+ '', name='monitor', aliases=('mo',)
+)
+def monitor(ctxt, monitor_log_file_path="1111", monitor_file_md5="00000", task_file_path=andbug.config.g_Date_File_Path):
+ '''
+ 函数功能:对指定的函数调用情况进行监控
+ 参数: monitor_log_file_path 保存监控内容的文件路径
+ monitor_file_md5 被监控apk文件的md5值
+ task_file_path 监控规则配置文件
+
+ '''
+
+ logFilePath = monitor_log_file_path
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
+ #datefmt='%a, %d %b %Y %H:%M:%S',
+ filename=logFilePath,
+ filemode='w')
+ print "task_file_path:" + task_file_path
+
+
+ file = open(task_file_path) # 返回一个文件对象
+ line = file.readline() # 调用文件的 readline()方法
+
+ unhookFunList=[]
+ with andbug.screed.section('Setting Hooks'):
+ while line:
+
+ flag, monitorType, cpath, mname, mjni = ParseMonitorConfItem(line)
+ print "flag=%s, monitorType=%s, cpath=%s, mname=%s, mjni=%s "%(flag,monitorType, cpath, mname, mjni)
+ print "line=%s"%(line)
+ if flag==False:
+ line = file.readline()
+ continue
+ try:
+ flag=cmd_hook_methods(ctxt, monitorType, cpath, mname)
+ if flag==False:
+ unhookFun = {}
+ unhookFun["monitorType"]=monitorType
+ unhookFun["cpath"]=cpath
+ unhookFun["mname"]=mname
+ unhookFunList.append(unhookFun)
+
+ except Exception:
+ print "RequestError "+ cpath + "-" + mname
+
+ line = file.readline()
+
+ file.close()
+
+ #输出hook失败的函数:
+ for fun in unhookFunList:
+ print "unhookFun: %s/%s "%(fun["cpath"],fun["mname"])
+
+ for i in range(0,5):
+ flag, newUnhookFunList = HookGoGoGo(ctxt, unhookFunList)
+ #print "i=%d flag=%s len=%d"%(i, flag, len(newUnhookFunList))
+ unhookFunList = newUnhookFunList
+
+ andbug.screed.section('Setting Hooks sucessful')
+
+ if not ctxt.shell:
+ try:
+ import readline
+ except:
+ readline = None
+ ctxt.shell = True
+ andbug.screed.section(BANNER)
+
+ while True:
+ try:
+ cmd = shlex.split(input())
+ except EOFError:
+ return
+ andbug.screed.pollcap()
+ if cmd:
+ andbug.command.run_command(cmd, ctxt=ctxt) #在这里接收控制台输入的命令,实现具体的调试工作。
+
diff --git a/lib/andbug/cmd/navi.py b/lib/andbug/cmd/navi.py
index d7f6a8b..6e1f9ff 100644
--- a/lib/andbug/cmd/navi.py
+++ b/lib/andbug/cmd/navi.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
## TODO: expand the forest to use , ,
## TODO: add browser
@@ -7,7 +8,8 @@
## TODO: add close button to popouts
## TODO: add static class list
-import andbug, os.path, json, subprocess, threading
+import andbug, os.path, cgi, json, subprocess, threading
+from urllib2 import quote as urlquote
import re
try:
@@ -288,7 +290,7 @@ def view_slot(tid, fid, key, path=None):
# frame stacks. This is consolidated into one data structure to reduce
# round trip latency.
#############################################################################
-
+#seq_frame、seq_thread、seq_process实现数据展示的几个函数
#TODO: INSULATE
def seq_frame(frame, url):
if not url.endswith('/'):
@@ -313,6 +315,8 @@ def seq_process():
seq_thread(threads[i], '/t/%s/' % i) for i in range(len(threads))
)
+
+#处理tt页面
@bottle.route('/tt')
def json_process():
data = json.dumps(seq_process())
@@ -325,6 +329,7 @@ def json_process():
# forest for efficiency.
#############################################################################
+#处理首页面的函数
@bottle.route('/')
def frontend():
return bottle.template('frontend', forest=json.dumps(seq_process()))
diff --git a/lib/andbug/cmd/resume.py b/lib/andbug/cmd/resume.py
index f580a37..8c4c4e4 100644
--- a/lib/andbug/cmd/resume.py
+++ b/lib/andbug/cmd/resume.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -19,12 +22,14 @@
@andbug.command.action('[]', shell=True)
def resume(ctxt, name=None):
'resumes threads in the process'
+ #针对整个虚拟机
if name is None:
ctxt.sess.resume()
return andbug.screed.section('Process Resumed')
elif name == '*':
name = None
+ #针对单个线程
with andbug.screed.section('Resuming Threads'):
for t in ctxt.sess.threads(name):
t.resume()
diff --git a/lib/andbug/cmd/shell.py b/lib/andbug/cmd/shell.py
index 1b97277..88a21e4 100644
--- a/lib/andbug/cmd/shell.py
+++ b/lib/andbug/cmd/shell.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -22,6 +25,7 @@
def input():
return raw_input('>> ')
+#在这里接收用户出入的每一个命令
def completer(text, state):
available_commands = andbug.command.ACTION_MAP.keys()
options = [x for x in available_commands if x.startswith(text)]
@@ -29,8 +33,6 @@ def completer(text, state):
return options[state]
except IndexError:
return None
-
-
@andbug.command.action('')
def shell(ctxt):
'starts the andbug shell with the specified process'
@@ -52,4 +54,4 @@ def shell(ctxt):
return
andbug.screed.pollcap()
if cmd:
- andbug.command.run_command(cmd, ctxt=ctxt)
+ andbug.command.run_command(cmd, ctxt=ctxt) #在这里接收控制台输入的命令,实现具体的调试工作。
diff --git a/lib/andbug/cmd/source.py b/lib/andbug/cmd/source.py
index e95ed14..46c63b3 100644
--- a/lib/andbug/cmd/source.py
+++ b/lib/andbug/cmd/source.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -14,7 +17,8 @@
'implementation of the "methods" command'
-import andbug.source, os.path
+import os.path
+import andbug.source, andbug.screed
@andbug.command.action('')
def source(ctxt, srcdir):
@@ -23,6 +27,7 @@ def source(ctxt, srcdir):
if os.path.isdir(srcdir):
if os.path.isdir(os.path.join(srcdir, "smali")):
srcdir = os.path.join(srcdir, "smali")
+ andbug.screed.section('Add source folder: %s' % srcdir)
andbug.source.add_srcdir(srcdir)
else:
print '!! directory not found:', repr(srcdir)
diff --git a/lib/andbug/cmd/statics.py b/lib/andbug/cmd/statics.py
index 31bbc8e..5c7d08d 100644
--- a/lib/andbug/cmd/statics.py
+++ b/lib/andbug/cmd/statics.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -20,7 +23,7 @@
def statics(ctxt, cpath):
'lists the methods of a class'
cpath = andbug.options.parse_cpath(cpath)
- for c in ctxt.sess.classes(cpath):
+ for c in ctxt.sess.classes(cpath): #c的类型为vm.Class类,它的父类型为RefType
andbug.screed.section("Static Fields, %s" % c)
for k, v in c.statics.iteritems():
- andbug.screed.item("%s = %s" % (k, v))
+ andbug.screed.item("%s = %s" % (k, v)) #在这里会到用vm.String类中的__str__函数,进而调用data(self)发起"call jdwp 0x0A 01"命令
diff --git a/lib/andbug/cmd/step.py b/lib/andbug/cmd/step.py
new file mode 100644
index 0000000..9c2d6ca
--- /dev/null
+++ b/lib/andbug/cmd/step.py
@@ -0,0 +1,102 @@
+#add by sq.luo
+import andbug.command, andbug.screed
+import andbug.vm
+
+def stepComplete(t):
+ t = t[0]
+ with andbug.screed.section("Single step complete in %s, suspended." % t):
+ showCallStack(t, 1)
+
+def showCallStack(t, count = 0):
+ if count >= len(t.frames) or count <= 0:
+ count = len(t.frames)
+ for f in t.frames[0:count]:
+ name = str(f.loc)
+ f.loc.method.firstLoc
+ if f.native:
+ name += ' '
+ andbug.screed.item(name)
+
+def printValues(dist, name = None):
+ if name == None:
+ for key in dist.keys():
+ print key + ' : ' + str(dist[key])
+ else :
+ if dist[name] != None:
+ print name + ' : ' + str(dist[name])
+ if (isinstance(dist[name], andbug.vm.Object)):
+ print "{"
+ printValues(dist[name].fields)
+ print "}"
+ else:
+ print 'not found \"' + name + '\" variable'
+
+@andbug.command.action('', aliases=('vs',))
+def values(ctxt, name = None):
+ 'if you suspend, you print the values.'
+ with andbug.screed.section('values'):
+ if ctxt.sess.getSuspendState().isSuspend:
+ t = ctxt.sess.getSuspendState().getThread()
+ printValues(t.frames[0].values, name)
+
+ else :
+ print 'Not suspend, you can\'t print values'
+
+@andbug.command.action(' ', aliases=('set', 'sv', ))
+def setValues(ctxt, name = None, value = None):
+ 'if you suspend, you can set the values.'
+ if name == None or value == None:
+ print 'parameter not enough'
+ return
+ with andbug.screed.section('values'):
+ if ctxt.sess.getSuspendState().isSuspend:
+ t = ctxt.sess.getSuspendState().getThread()
+ t.frames[0].setValue(name, value)
+
+ else :
+ print 'Not suspend, you can\'t print values'
+
+@andbug.command.action('[]', aliases=('bt',))
+def backtrace(ctxt, count = None):
+ 'if you suspend, you print the backtrace.'
+ with andbug.screed.section('Back Trace'):
+ if ctxt.sess.getSuspendState().isSuspend:
+ t = ctxt.sess.getSuspendState().getThread()
+ if count == 'all' or count == None:
+ showCallStack(t)
+ if count.isdigit():
+ showCallStack(t, int(count))
+
+ else :
+ print 'Not suspend, you can\'t print backtrace'
+
+@andbug.command.action('', aliases=('s',))
+def stepover(ctxt, expr=None):
+ 'if you suspend, you can step over.'
+ with andbug.screed.section('Step Over'):
+ if ctxt.sess.getSuspendState().isSuspend:
+ t = ctxt.sess.getSuspendState().getThread()
+ t.singleStep(func = stepComplete)
+ else :
+ print 'Not suspend, you can\'t step'
+
+@andbug.command.action('', aliases=('si',))
+def stepinto(ctxt, expr=None):
+ 'if you suspend, you can step into.'
+ with andbug.screed.section('Step Into'):
+ if ctxt.sess.getSuspendState().isSuspend:
+ t = ctxt.sess.getSuspendState().getThread()
+ t.singleStep(func = stepComplete, stepdepth = 0)
+ else :
+ print 'Not suspend, you can\'t step into'
+
+@andbug.command.action('', aliases=('so',))
+def stepout(ctxt, expr=None):
+ 'if you suspend, you can step out.'
+
+ with andbug.screed.section('Step Out'):
+ if ctxt.sess.getSuspendState().isSuspend:
+ t = ctxt.sess.getSuspendState().getThread()
+ t.singleStep(func = stepComplete, stepdepth = 2)
+ else :
+ print 'Not suspend, you can\'t step out'
diff --git a/lib/andbug/cmd/suspend.py b/lib/andbug/cmd/suspend.py
index a2a2d49..2617362 100644
--- a/lib/andbug/cmd/suspend.py
+++ b/lib/andbug/cmd/suspend.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -17,15 +20,17 @@
import andbug.command, andbug.screed
@andbug.command.action('[]', shell=True)
-def suspend(ctxt, name=None):
+def suspend(ctxt, name=None): #ctxt 是Context类
'suspends threads in the process'
+ #没有指定线程名称,则将虚拟机中所有进程挂起
if name is None:
- ctxt.sess.suspend()
+ ctxt.sess.suspend()
return andbug.screed.section('Process Suspended')
elif name == '*':
name = None
-
+
+ #指定了线程的名称,则挂起相应的线程
with andbug.screed.section('Suspending Threads'):
- for t in ctxt.sess.threads(name):
- t.suspend()
+ for t in ctxt.sess.threads(name): #t是一个Thread类型的对象
+ t.suspend() #将对应的线程暂停
andbug.screed.item('suspended %s' % t)
diff --git a/lib/andbug/cmd/threads.py b/lib/andbug/cmd/threads.py
index ab6289f..d89a7a6 100644
--- a/lib/andbug/cmd/threads.py
+++ b/lib/andbug/cmd/threads.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -44,10 +47,17 @@ def parse_verbosity(param):
if param is None or param[:8] != 'verbose=':
return False
- verbosity = int(param[8:])
+ verbosity = int(param[8:]) #获取'verbose='后面的值
return verbosity
- def parse_args(arg1, arg2):
+ def parse_args(arg1, arg2):
+ '''
+ 函数功能:对参数进行解析,
+ 注: 如果arg1为name,arg2为verbose,则返回(name, verbose)
+ 如果arg1为name,arg2为None,则返回(name, 0)
+ 如果arg1为verbose,arg2为None,则返回(None, verbose)
+ 如果arg1为None,arg2为None,则返回(None, 0)
+ '''
if arg1 is None:
return (None, 0)
@@ -67,12 +77,12 @@ def parse_args(arg1, arg2):
ctxt.sess.suspend()
try:
- threads = sorted(ctxt.sess.threads(name).items, key=threadId)
+ threads = sorted(ctxt.sess.threads(name).items, key=threadId) #对线程数据按照线程Id的值进行排序
- for t in threads:
+ for t in threads: #逐个取出线程
with andbug.screed.section(str(t)):
if verbose > 0:
- thread_methods(t, verbose)
+ thread_methods(t, verbose) #用以展示各线程当前的堆栈信息
finally:
ctxt.sess.resume()
diff --git a/lib/andbug/cmd/vm_cap.py b/lib/andbug/cmd/vm_cap.py
new file mode 100644
index 0000000..60f878e
--- /dev/null
+++ b/lib/andbug/cmd/vm_cap.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+## Copyright 2011, IOActive, Inc. All rights reserved.
+##
+## AndBug is free software: you can redistribute it and/or modify it under
+## the terms of version 3 of the GNU Lesser General Public License as
+## published by the Free Software Foundation.
+##
+## AndBug is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+## FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+## more details.
+##
+## You should have received a copy of the GNU Lesser General Public License
+## along with AndBug. If not, see .
+
+'implementation of the "methods-detail" command'
+
+import andbug.command, andbug.options, andbug.screed
+from andbug import log
+
+@andbug.command.action('', name='vm-cap', shell=True)
+def vm_cap(ctxt):
+ '获取目标虚拟机所支持的功能'
+ vmcap = ctxt.sess.vmCapability()
+ with andbug.screed.section('java vm capability:'):
+ for (cap_name, cap_value) in vmcap.vm_cap.items():
+ andbug.screed.item(cap_name + ":" + str(cap_value))
+
+ '''
+
+ self.canWatchFieldModification = newCapabilityBuf.unpackU8()
+ self.canWatchFieldAccess = newCapabilityBuf.unpackU8()
+ self.canGetBytecodes = newCapabilityBuf.unpackU8()
+ self.canGetSyntheticAttribute = newCapabilityBuf.unpackU8()
+ self.canGetOwnedMonitorInfo = newCapabilityBuf.unpackU8()
+ self.canGetCurrentContendedMonitor = newCapabilityBuf.unpackU8()
+ self.canGetMonitorInfo = newCapabilityBuf.unpackU8()
+ self.canRedefineClasses = newCapabilityBuf.unpackU8()
+ self.canAddMethod = newCapabilityBuf.unpackU8()
+ self.canUnrestrictedlyRedefineClasses = newCapabilityBuf.unpackU8()
+ self.canPopFrames = newCapabilityBuf.unpackU8()
+ self.canUseInstanceFilters = newCapabilityBuf.unpackU8()
+ self.canGetSourceDebugExtension = newCapabilityBuf.unpackU8()
+ self.canRequestVMDeathEvent = newCapabilityBuf.unpackU8()
+ self.canSetDefaultStratum = newCapabilityBuf.unpackU8()
+ cpath, mname, mjni = andbug.options.parse_mquery(cpath, mquery) #cpath=Lcom/example/test/MainActivity; mname=onCreate [函数名] mjni=None[可能是参数信息不确定]
+ infor = "cpath="+ str(cpath) + "\t mname="+ str(mname) + "\t mjni=" + str(mjni);
+ log.debug(infor);
+
+
+ title = "Methods " + ((cpath + "->" + mquery) if mquery else (cpath))
+ with andbug.screed.section(title):
+ for m in ctxt.sess.classes(cpath).methods(name=mname, jni=mjni):
+ #str(m) 类似如下信息com.example.test.MainActivity.onCreate(Landroid/os/Bundle;)V m值的类型是
+ m.load_bytecodes ()
+ andbug.screed.item(str(m))
+ '''
\ No newline at end of file
diff --git a/lib/andbug/command.py b/lib/andbug/command.py
index a115c33..13928a3 100644
--- a/lib/andbug/command.py
+++ b/lib/andbug/command.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,6 +15,8 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
+
+
'''
The andbug.command module underpins the andbug command system by providing
context and a central registry to command modules in the andbug.cmd package.
@@ -28,7 +33,7 @@ def sample(ctxt, used, extra=None):
'''
import os, os.path, sys, getopt, inspect
-import andbug.vm, andbug.cmd, andbug.source, andbug.util
+import andbug.vm, andbug.cmd, andbug.source, andbug.util,andbug.screed
import traceback
from time import sleep
from andbug.errors import *
@@ -52,56 +57,85 @@ def __init__(self):
self.sess = None
self.pid = None
self.dev = None
- self.shell = False
+ self.shell = False #标识现在是否可以在andbug下运行shell
def connect(self):
'connects using vm.connect to the process if not already connected'
if self.sess is not None: return
self.sess = andbug.vm.connect(self.pid, self.dev)
+ #对传入特定命令的参数进行解析
def parseOpts(self, args, options=OPTIONS, proc=True):
'parse command options in OPTIONS format'
- short_opts = ''.join(opt[0][0] + ':' for opt in options)
- long_opts = list(opt[0] + '=' for opt in options)
+ short_opts = ''.join(opt[0][0] + ':' for opt in options) #str: p:d:s:
+ long_opts = list(opt[0] + '=' for opt in options) #list: ['pid=', 'dev=', 'src=']
opt_table = {}
+ #opt_table的值为:
+ # --dev str: dev
+ # --pid str: pid
+ # --src str: src
+ # -d str: dev
+ # -p str: pid
+ # -s str: src
for opt in options:
opt_table['-' + opt[0][0]] = opt[0]
opt_table['--' + opt[0]] = opt[0]
+ #分解参数opts保存 参数名参数值的对, args保存剩余的参数之
+ #"xxx.py -h -o file --help --output=out file1 file2" 命令解析后
+ #opts的值 [('-h',''),('-o','file'),('--help',''),('--output','out')]
+ #args的值 ['file1','file2']
opts, args = getopt.gnu_getopt(args, short_opts, long_opts)
- opts = list((opt_table[k], v) for k, v in opts)
+ opts = list((opt_table[k], v) for k, v in opts) #短命令或长命令变成命令全称
t = {}
for k, v in opts:
- if k == 'src':
+ if k == 'src': #处理src命令,猜测是源代码命令
andbug.source.add_srcdir(v)
else:
t[k] = v
if proc:
- pid = t.get('pid')
- dev = t.get('dev')
-
+ pid = t.get('pid') #获取当前要调试程序的pid的值
+ dev = t.get('dev') #获取当前要调试程序的dev的值
+
self.findDev(dev)
self.findPid(pid)
+
return args, opts
def findDev(self, dev=None):
'determines the device for the command based on dev'
- if self.dev is not None: return
+ if self.dev is not None: return #如果Context对象中dev已经有有效的值了,就不去要再调用获取dev的函数andbug.util.find_dev
self.dev = andbug.util.find_dev(dev)
def findPid(self, pid=None):
'determines the process id for the command based on dev, pid and/or name'
- if self.pid is not None: return
- self.pid = andbug.util.find_pid(pid, self.dev)
+ if self.pid is not None: return ##如果Context对象中pid已经有有效的值了,就不去要再调用获取pid的函数andbug.util.find_pid
+ cur_pid = andbug.util.find_pid(pid, self.dev)
+ count =0
+ while cur_pid == None:
+ if count%4==0:
+ andbug.screed.item("wait......")
+
+ sleep(1) #sleep 1秒中后再重新获取pid的值
+ count=count+1
+ cur_pid = andbug.util.find_pid(pid, self.dev)
+
+ if count==60:
+ raise OptionError('could not find process ' + str(pid))
+ self.pid = cur_pid
+
+
+ #判断命令是否可以执行?作用是如果该命令的shell属性为true,那么这个命令就可以直接跟在andbug后
+ #作为一个命令直接运行
def can_perform(self, act):
'uses the act.shell property to determine if it makes sense'
if self.shell:
- return act.shell != False
- return act.shell != True
+ return act.shell != False #值不等于true返回false
+ return act.shell != True #值不等于false,返回true
def block_exit(self):
'prevents termination outside of shells'
@@ -117,28 +151,33 @@ def block_exit(self):
# allows the main thread to perceive CTRL-C.
sleep(3600)
+
+ #函数功能:执行具体命令
+ #参数: cmd 具体要执行的命令
+ # args 命令的参数
def perform(self, cmd, args):
'performs the named command with the supplied arguments'
- act = ACTION_MAP.get(cmd)
+ act = ACTION_MAP.get(cmd) #获取对应命令的处理函数
if not act:
perr('!! command not supported: "%s."' % cmd)
return False
- if not self.can_perform(act):
- if ctxt.shell:
+ if not self.can_perform(act): #函数返回false退出
+ if self.shell:
perr('!! %s is not available in the shell.' % cmd)
else:
perr('!! %s is only available in the shell.' % cmd)
return False
+ #具体解析,当前命令的参数
args, opts = self.parseOpts(args, act.opts, act.proc)
argct = len(args) + 1
- if argct < act.min_arity:
+ if argct < act.min_arity: #如果小于最小的参数个数,退出
perr('!! command "%s" requires more arguments.' % cmd)
return False
- elif argct > act.max_arity:
+ elif argct > act.max_arity:#如果大于最大的参数个数,退出
perr('!! too many arguments for command "%s."' % cmd)
return False
@@ -168,6 +207,7 @@ def dump_exc(exc):
ACTION_LIST = []
ACTION_MAP = {}
+#具体实现将命令信息保存到List、Map中
def bind_action(name, fn, aliases):
ACTION_LIST.append(fn)
ACTION_MAP[name] = fn
@@ -179,34 +219,40 @@ def action(usage, opts = (), proc = True, shell = None, name = None, aliases=())
def bind(fn):
fn.proc = proc
fn.shell = shell
- fn.usage = usage
- fn.opts = OPTIONS[:] + opts
- fn.keys = list(opt[0] for opt in opts)
- fn.aliases = aliases
- spec = inspect.getargspec(fn)
+ fn.usage = usage #使用方法
+ fn.opts = OPTIONS[:] + opts #当前命令的参数有哪些
+ fn.keys = list(opt[0] for opt in opts) #保存的短命令的值
+ fn.aliases = aliases #当前命令的别名
+ spec = inspect.getargspec(fn) #功能是获取函数fn所定义的参数名称,对fn进行解析,计算出参数的个数,和有默认值参数的个数,从而计算出每个命令最多需要多少个参数,最少需要多少个参数
+ #仅用于方法,获取方法声明的参数,返回元组,分别是(普通参数名的列表, *参数名, **参数名, 默认值元组)。如果没有值,将是空列表和3个None。如果是2.6以上版本,将返回一个命名元组(Named Tuple),即除了索引外还可以使用属性名访问元组中的元素。
defct = len(spec.defaults) if spec.defaults else 0
argct = len(spec.args) if spec.args else 0
- fn.min_arity = argct - defct
- fn.max_arity = argct
+ fn.min_arity = argct - defct #当前命令,允许的最小参数个数
+ fn.max_arity = argct ##当前命令,允许的最大参数个数
fn.name = name or fn.__name__.replace('_', '-')
bind_action(fn.name, fn, aliases)
return bind
-CMD_DIR_PATH = os.path.abspath(os.path.join( os.path.dirname(__file__), "cmd" ))
+CMD_DIR_PATH = os.path.abspath(os.path.join( os.path.dirname(__file__), "cmd" )) #该变量保存的路径为"~/andbug/lib/andbug/cmd"
+#将cmd目录下,所有*.py文件都import进来,动态加载模块的程序中的一个过程
def load_commands():
- 'loads commands from the andbug.cmd package'
+ 'loads commands from the andbug.cmd package'
for name in os.listdir(CMD_DIR_PATH):
if name.startswith( '__' ):
continue
if name.endswith( '.py' ):
name = 'andbug.cmd.' + name[:-3]
try:
- __import__( name )
+ __import__( name ) #动态导入其他模块,这是会执行,指定python文件中的@andbug.command.action函数
except andbug.errors.DependencyError:
pass # okay, okay..
+
+#执行调试命令,每个调试命令都从这个函数开始
+#参数: args 命令的名称以及相关参数
+# ctxt = None 用于支持命令执行的类成员,细节未知
def run_command(args, ctxt = None):
'runs the specified command with a new context'
if ctxt is None:
@@ -219,6 +265,40 @@ def run_command(args, ctxt = None):
return ctxt.perform(args[0], args[1:])
+#定义本文件向其他文件可以到处的函数、变量的名称
__all__ = (
'run_command', 'load_commands', 'action', 'Context', 'OptionError'
)
+
+
+
+'''
+inspect.getargspec(fn) 函数的使用
+
+在example.py文件中
+def module_level_function(arg1, arg2='default', *args, **kwargs):
+ #This function is declared in the module.
+ local_variable = arg1
+
+在use_inspect文件中
+import inspect
+import example
+
+argspec = inspect.getargspec(example.module_level_function)
+print 'Names', argsec[0]
+print '*:', argspec[1]
+print '**:', argspec[2]
+print 'default:', argspec[3]
+arg_with_defaults= argspec[0][-len(argspec[3]):]
+print 'arg & defaults:', zip(arg_with_defaults, argspec[3])
+
+运行后的结果:
+Names: ['arg1', 'arg2']
+*: args
+**: kwargs
+default: ('default',)
+arg & defaults: [('arg2', 'default')]
+
+
+
+'''
diff --git a/lib/andbug/config.py b/lib/andbug/config.py
new file mode 100644
index 0000000..fae3848
--- /dev/null
+++ b/lib/andbug/config.py
@@ -0,0 +1,7 @@
+#! /usr/bin/env python
+#coding=utf-8
+
+
+
+g_Date_File_Path = "/home/anbc/work_folder/andbug_work/andbug/data/monitor_fun.conf"
+
diff --git a/lib/andbug/data.py b/lib/andbug/data.py
index e5d1e93..8b57401 100644
--- a/lib/andbug/data.py
+++ b/lib/andbug/data.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,6 +15,11 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
+
+#这个模块可以单独测试以搞清楚实现原理
+
+
+
from threading import Lock
class multidict(dict):
@@ -28,8 +36,8 @@ def put(self, key, val):
v = view()
v.append(val)
dict.__setitem__(self, key, v)
-
- def __setitem__(self, key, val):
+
+ def __setitem__(self, key, val): #用于对应字典的[]操作符
self.put(key, val)
def __getitem__(self, key):
@@ -37,7 +45,7 @@ def __getitem__(self, key):
class pool(object):
'''
- a pool of singleton objects such that, for any combination of constructor
+ a pool of singleton[单独的] objects such that, for any combination[联合体] of constructor
and 1 or more initializers, there may be zero or one objects; attempting
to reference a nonexisted object causes it to be created.
@@ -52,11 +60,13 @@ def t(a): return [a,0]
def __init__(self):
self.pools = {}
self.lock = Lock()
- def __call__(self, *ident):
+
+ #调用方式:return pool(classitem, self.cid)
+ def __call__(self, *ident): #调用方式:m1 = pool(methoditem, 'c1', 'm1')
with self.lock:
pool = self.pools.get(ident)
if pool is None:
- pool = ident[0](*ident[1:])
+ pool = ident[0](*ident[1:]) #这里会出现什么样的运算结果还不清楚
self.pools[ident] = pool
return pool
@@ -69,7 +79,7 @@ class view(object):
def __init__(self, items = []):
self.items = list(items)
- def __repr__(self):
+ def __repr__(self): #字符串重复
return '(' + ', '.join(str(item) for item in self.items) + ')'
def __len__(self):
return len(self.items)
@@ -77,7 +87,7 @@ def __getitem__(self, index):
return self.items[index]
def __iter__(self):
return iter(self.items)
- def __getattr__(self, key):
+ def __getattr__(self, key): #当属性没有找到时调用
def poolcall(*args, **kwargs):
t = tuple(
getattr(item, key)(*args, **kwargs) for item in self.items
@@ -88,7 +98,12 @@ def poolcall(*args, **kwargs):
return view(flatten(t))
poolcall.func_name = '*' + key
return poolcall
+
+
def get(self, key):
+ '''
+ descript:通过对list中的每个成员调用getattr函数,实现将所有信息检索出来
+ '''
return view(getattr(item, key) for item in self.items)
def set(self, key, val):
for item in self.items:
@@ -96,14 +111,17 @@ def set(self, key, val):
def append(self, val):
self.items.append(val)
+#函数功能:将二维的数组,展开成一维数组
def flatten(seq):
for ss in seq:
for s in ss:
yield s
-
+
+#?推迟延期
+#具体调用方式是:first = defer(load_line_table, 'first')
def defer(func, name):
'''
- a property decorator that, when applied, specifies a property that relies
+ a property【性质】 decorator【装饰者】 that, when applied, specifies a property that relies
on the execution of a costly function for its resolution; this permits the
deferral of evaluation until the first time it is needed.
@@ -112,7 +130,7 @@ def defer(func, name):
'''
def fget(obj, type=None):
try:
- return obj.props[name]
+ return obj.props[name] #按照指定的要求取元素,一旦获取失败就处罚异常,于是执行下面的func(obj)函数
except KeyError:
pass
except AttributeError:
@@ -128,10 +146,12 @@ def fset(obj, value):
except AttributeError:
obj.props = {name : value}
- fget.func_name = 'get_' + name
+ fget.func_name = 'get_' + name #func_name 没能找到相关资料
fset.func_name = 'set_' + name
- return property(fget, fset)
+ return property(fget, fset) #设置当获取值时调用fget函数,当设置值是调用fset函数
+
+#下面应该都是用于测试的代码
if __name__ == '__main__':
pool = pool()
@@ -154,7 +174,7 @@ def load_line_table(self):
self.first = 1
self.last = 1
self.lines = []
- def trace(self):
+ def trace(self): #跟踪的意思
print "TRACE", self.cid, self.mid
first = defer(load_line_table, 'first')
diff --git a/lib/andbug/errors.py b/lib/andbug/errors.py
index f3786af..4adeb7e 100644
--- a/lib/andbug/errors.py
+++ b/lib/andbug/errors.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
diff --git a/lib/andbug/log.py b/lib/andbug/log.py
index aff53d9..d4c7e96 100644
--- a/lib/andbug/log.py
+++ b/lib/andbug/log.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,16 +15,28 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
+
+#文件功能:实现日志记录的一些类功能:
+#对外提供一下函数接口:
+#def error(tag, meta, data = None) 输出错误信息
+#def info(tag, meta, data = None) 输出普通信息
+#def read_log(path=None, file=None) 读取日志信息
+
+
import os, sys, time
from cStringIO import StringIO
+
def blocks(seq, sz):
ofs = 0
lim = len(seq)
while ofs < lim:
- yield seq[ofs:ofs+sz]
+ yield seq[ofs:ofs+sz] #关键是yield的用法,现在理解的还不是很深入
ofs += sz
-
+
+#函数功能:
+#注释:censor 审查,检查
+#将传入的字符编程可打印字符,对于传入的不可打印字符,用“.”表示
def censor(seq):
for ch in seq:
if ch < '!':
@@ -31,16 +46,18 @@ def censor(seq):
else:
yield ch
+
+#函数功能:以十六制形式输出数据【猜测】
def format_hex(data, indent="", width=16, out=None):
if out == None:
- out = StringIO()
+ out = StringIO() #StringIO经常被用来作为字符串的缓存,应为StringIO有个好处,他的有些接口和文件操作是一致的,也就是说用同样的代码,可以同时当成文件操作或者StringIO操作
strout = True
else:
strout = False
indent += "%08x: "
ofs = 0
- for block in blocks(data, width):
+ for block in blocks(data, width): #将一个字符串按照指定的长度分成多个字符串段
out.write(indent % ofs)
out.write(' '.join(map(lambda x: x.encode('hex'), block)))
if len(block) < width:
@@ -61,7 +78,7 @@ def parse_hex(dump, out=None):
strout = False
for row in dump.splitlines():
- row = row.strip().split(' ')
+ row = row.strip().split(' ') #strip函数用于去掉字符串中开始和结束的字符
block = row[1].strip().split(' ')
block = ''.join(map(lambda x: chr(int(x, 16)), block))
out.write(block)
@@ -88,6 +105,7 @@ def __init__(self, file=sys.stdout):
def writeEvent(self, evt):
self.file.write(str(evt))
+ self.file.flush()
class LogReader(object):
def __init__(self, file=sys.stdin):
@@ -96,7 +114,7 @@ def __init__(self, file=sys.stdin):
def readLine(self):
if self.last is None:
- line = self.file.readline().rstrip()
+ line = self.file.readline().rstrip() #rstrip()用来在字符串末尾删除某个字符
else:
line = self.last
self.last = None
@@ -130,17 +148,41 @@ def readEvent(self):
return LogEvent(time, tag, meta, data)
-stderr = LogWriter(sys.stderr)
-stdout = LogWriter(sys.stdout)
+def cur_file_dir():
+ #获取脚本路径
+ path = sys.path[0]
+ #判断为脚本文件还是py2exe编译后的文件,如果是脚本文件,则返回的是脚本的目录,
+ #如果是py2exe编译后的文件,则返回的是编译后的文件路径
+ if os.path.isdir(path):
+ return path
+ elif os.path.isfile(path):
+ return os.path.dirname(path)
+
+cur_path = cur_file_dir()
+err_file = open(cur_path + os.path.sep + "err.log", "a")
+infor_file = open(cur_path + os.path.sep + "infor.log", "a")
+debug_file = open(cur_path + os.path.sep + "debug.log", "a")
+stderr = LogWriter(err_file)
+stdout = LogWriter(infor_file)
+stdebug = LogWriter(debug_file)
def error(tag, meta, data = None):
now = int(time.time())
stderr.writeEvent(LogEvent(now, tag, meta, data))
def info(tag, meta, data = None):
- now = int(time.time())
+ #now = int(time.time())
+ now = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
+ now +="(%f)" %(time.time())
stdout.writeEvent(LogEvent(now, tag, meta, data))
+def debug(tag, meta, data=None):
+ now = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
+ now +="(%f)" %(time.time())
+ #now = int(time.time())
+ stdebug.writeEvent(LogEvent(now, tag, meta, data))
+
+
def read_log(path=None, file=None):
if path is None:
if file is None:
@@ -148,3 +190,26 @@ def read_log(path=None, file=None):
else:
reader = LogReader(file)
return reader
+
+
+def debug_infor (infor, filepath="/home/anbc/workspace/log/debug_infor.txt"):
+ '''
+ add by anbc for debug the program
+ '''
+ file_object = open(filepath, 'a')
+ file_object.write(infor+"\r\n")
+ file_object.close( )
+
+
+def main():
+ print 'start'
+
+ debug("test", "infor")
+
+
+ print 'end'
+
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/lib/andbug/options.py b/lib/andbug/options.py
index 0e9358f..d75cce3 100644
--- a/lib/andbug/options.py
+++ b/lib/andbug/options.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,6 +15,8 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
+import sys
+
class ParseError(Exception):
def __init__(self, reason, option):
self.reason = reason
@@ -21,11 +26,14 @@ def __str__(self):
return '%s: %r.' % (self.reason, self.option)
def parse_cpath(path):
- if path.startswith('L') and path.endswith(';') and ('.' not in path):
+ '''
+ 函数功能:对类路径进行解析
+ '''
+ if path.startswith('L') and path.endswith(';') and ('.' not in path):
return path
- elif path.startswith('L') or path.endswith(';') or ('/' in path):
+ elif path.startswith('L') or path.endswith(';') or ('/' in path):
raise ParseError('could not determine if path is a JNI or logical class path', path)
- else:
+ else:
return'L' + path.replace('.', '/') + ';'
def parse_mspec(mspec):
diff --git a/lib/andbug/proto.py b/lib/andbug/proto.py
index 9efbfac..da064c7 100644
--- a/lib/andbug/proto.py
+++ b/lib/andbug/proto.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -17,15 +20,20 @@
manageable request/response API using an input worker thread in the
background and a number of mutexes to control contests for output.
'''
+#将JDWP 抽象成一系列“请求/响应”
+
import socket, tempfile
-import andbug.util
from threading import Thread, Lock
-from andbug.jdwp import JdwpBuffer
from Queue import Queue, Empty as EmptyQueue
+
+import andbug.util
+from andbug import log
+from andbug.jdwp import JdwpBuffer
+
class EOF(Exception):
- 'signals that an EOF has been encountered'
+ 'signals that an EOF[帧结束] has been encountered[遇到、遭遇]'
def __init__(self, inner = None):
Exception.__init__(
self, str(inner) if inner else "EOF"
@@ -47,20 +55,26 @@ class ProtocolError(Exception):
'\x00\x00\x00\x0B' # Length
'\x00\x00\x00\x01' # Identifier
'\x00' # Flags
- '\x01\x07' # Command 1:7
+ '\x01\x07' # Command 1:7 IDSizes Command (7)
)
+#adb -s [dev] forward localfilesystem: [temp] jdwp [pid]
+#adb -s emulator-5554 forward localfilesystem:/tmp/tmpzeJZR5 jdwp:333
+#与虚拟机建立链接
def forward(pid, dev=None):
'constructs an adb forward for the context to access the pid via jdwp'
if dev:
dev = andbug.util.find_dev(dev)
pid = andbug.util.find_pid(pid)
- temp = tempfile.mktemp()
- cmd = ('-s', dev) if dev else ()
- cmd += ('forward', 'localfilesystem:' + temp, 'jdwp:%s' % pid)
+ temp = tempfile.mktemp() #创建一个临时文件
+ cmd = ('-s', dev) if dev else () #'-s', 'emulator-5554'
+ cmd += ('forward', 'localfilesystem:' + temp, 'jdwp:%s' % pid) #'-s', 'emulator-5554', 'forward', 'localfilesystem:/tmp/tmpSSCNAl', 'jdwp:843')
andbug.util.adb(*cmd)
return temp
+#调用时的方式:andbug.proto.connect(andbug.proto.forward(pid, dev))
+#self.sess = andbug.vm.connect(self.pid, self.dev)
+# addr 参数是一个临时文件的路径
def connect(addr, portno = None, trace=False):
'connects to an AF_UNIX or AF_INET JDWP transport'
if addr and portno:
@@ -71,6 +85,7 @@ def connect(addr, portno = None, trace=False):
conn = socket.socket(socket.AF_UNIX)
conn.connect(addr)
+ #负责读出数据的函数
def read(amt):
'read wrapper internal to andbug.proto.connect'
req = amt
@@ -84,6 +99,7 @@ def read(amt):
print ":: RECV:", repr(buf)
return buf
+ #负责写入数据的函数
def write(data):
'write wrapper internal to andbug.proto.connect'
try:
@@ -93,18 +109,18 @@ def write(data):
except Exception as exc:
raise EOF(exc)
- p = Connection(read, write)
+ p = Connection(read, write) #定义一个Connection对象
p.start()
return p
class Connection(Thread):
'''
- The JDWP Connection is a thread which abstracts the asynchronous JDWP protocol
+ The JDWP Connection is a thread which abstracts the asynchronous[异步] JDWP protocol
into a more synchronous one. The thread will listen for packets using the
- supplied read function, and transmit them using the write function.
+ supplied[提供] read function, and transmit[传送] them using the write function.
Requests are sent by the processor using the calling thread, with a mutex
- used to protect the write function from concurrent access. The requesting
+ used to protect the write function from concurrent[并发的] access. The requesting
thread is then blocked waiting on a response from the processor thread.
The Connectionor will repeatedly use the read function to receive packets, which
@@ -115,127 +131,149 @@ class Connection(Thread):
def __init__(self, read, write):
Thread.__init__(self)
- self.xmitbuf = JdwpBuffer()
+ self.xmitbuf = JdwpBuffer() #具体实现在jdwp文件中,用C语言实现,这块需要了解C语言嵌入python语言的知识
self.recvbuf = JdwpBuffer()
self._read = read
self.write = write
self.initialized = False
- self.next_id = 3
- self.bindqueue = Queue()
- self.qmap = {}
- self.rmap = {}
- self.xmitlock = Lock()
+ self.next_id = 3 #下一个请求id,『可能每次请求都是有序号的』
+ self.bindqueue = Queue() #定义一个先进先出的队列
+ self.qmap = {} #初始化一个空的字典
+ self.rmap = {} #初始化一个空的字典
+ self.xmitlock = Lock() #是一个互斥锁
+ #读数据的函数,sz准备读取数据的长度,
def read(self, sz):
'read size bytes'
if sz == 0: return ''
- pkt = self._read(sz)
- if not len(pkt): raise EOF()
+ pkt = self._read(sz) #返回值是读到的数据
+ if not len(pkt): raise EOF() #如果读到的数据的长度为0,抛出EOF异常
return pkt
###################################################### INITIALIZATION STEPS
+ #写入id序列信息
def writeIdSzReq(self):
'write an id size request'
return self.write(IDSZ_REQ)
+ #读取各id的长度信息
def readIdSzRes(self):
'read an id size response'
- head = self.readHeader()
- if head[0] != 20:
- raise ProtocolError('expected size of an idsize response')
- if head[2] != 0x80:
+ head = self.readHeader() #读到的header的值是:list: [20L, 1L, 128L, 0L] [Length, Id,Flags, Error Code]
+ if head[0] != 20: #id size命令的返回数据包的长度为20字节,其中包括11字节的包头长度。
+ raise ProtocolError('expected size of an idsize response') #抛出协议错误异常
+ if head[2] != 0x80: #返回包包头中的Flags字段的值是固定的均为0x80即128
raise ProtocolError(
- 'expected first server message to be a response'
+ 'expected first server message to be a response' #抛出协议错误异常
)
- if head[1] != 1:
- raise ProtocolError('expected first server message to be 1')
+ if head[1] != 1: #由于发送id size请求包的id编号是1,所以合法的返回包的编号也应用是1
+ raise ProtocolError('expected first server message to be 1') #抛出协议错误异常
sizes = self.recvbuf.unpack( 'iiiii', self.read(20) )
- self.sizes = sizes
- self.recvbuf.config(*sizes)
+ self.sizes = sizes #读取到的sizes的值是 list: [4L, 4L, 8L, 8L, 8L] 记录下各类型对象所占空间的长度
+ self.recvbuf.config(*sizes)
self.xmitbuf.config(*sizes)
return None
+ #接收握手数据
def readHandshake(self):
'read the jdwp handshake'
data = self.read(len(HANDSHAKE_MSG))
if data != HANDSHAKE_MSG:
- raise HandshakeError()
-
+ raise HandshakeError() #抛出握手失败异常
+ #发送握手数据
def writeHandshake(self):
'write the jdwp handshake'
return self.write(HANDSHAKE_MSG)
############################################### READING / PROCESSING PACKETS
+ #读取头,在readIdSzRes(self)函数中被调用
def readHeader(self):
'reads a header and returns [size, id, flags, event]'
- head = self.read(11)
- data = self.recvbuf.unpack(HEADER_FORMAT, head)
+ head = self.read(11)
+ data = self.recvbuf.unpack(HEADER_FORMAT, head) #unpack函数在jdwp文件中实现
data[0] -= 11
return data
-
+ #启动新的线程,来处理从虚拟机中返回的信息。process函数被放在一个死循环中不断调用
def process(self):
'invoked repeatedly by the processing thread'
- size, ident, flags, code = self.readHeader() #TODO: HANDLE CLOSE
- data = self.read(size) #TODO: HANDLE CLOSE
- try: # We process binds after receiving messages to prevent a race
+ size, ident, flags, code = self.readHeader() #TODO: HANDLE CLOSE #读取数据头,包含一下元素size、ident、flags、code
+ log.debug("study", "In Connection(Thread).process size=" + str(size) + "\t ident="+ str(ident) + "\t flags=" +str(flags) + "\t code=" + str(code))
+ data = self.read(size) #TODO: HANDLE CLOSE #根据Header中的长度信息,读取具体数据。
+ try: # We process binds[绑定] after receiving messages to prevent a race
while True:
- self.processBind(*self.bindqueue.get(False))
+ self.processBind(*self.bindqueue.get(False)) #bindqueue.get(False)参数为False,队列将引发Empty异常
except EmptyQueue:
+ log.debug("study", "Except for Empty Queue")
pass
#TODO: update binds with all from bindqueue
-
- if flags == 0x80:
- self.processResponse(ident, code, data)
+ #对于来自虚拟机的事件消息,self.processBind(*self.bindqueue.get(False))不起作用,直接触发EmptyQueue异常,后续调用processRequest函数
+ if flags == 0x80:
+ self.processResponse(ident, code, data) #答复数据包的flag是0x80
else:
- self.processRequest(ident, code, data)
-
+ self.processRequest(ident, code, data) #请求数据包的flag是0x00
+ #函数调用的参数为:qr="r" ident=16484=0x4064 chan 是一个在Session类中定义的一个队列。qr="q" ident=3 其中ident是请求的编号id,调试器发往虚拟机的id从3开始
def processBind(self, qr, ident, chan):
- 'internal to i/o thread; performs a query or request bind'
+ 'internal[内部的] to i/o thread; performs a query or request bind'
+ #根据qr值的不同,以ident为关键字,以chan为值,放入不同的字典中
+ log.debug("study", "In Connection(Thread).processBind qr=" + str(qr) + "\t ident=" + str(ident) + "\t chan=" + str(chan))
+ log.debug("study", "++bindqueue.get FOR q ++")
if qr == 'q':
- self.qmap[ident] = chan
+ self.qmap[ident] = chan
elif qr == 'r':
self.rmap[ident] = chan
+
+ #处理请求
+ ##请求数据包的flag是0x00
def processRequest(self, ident, code, data):
'internal to the i/o thread w/ recv ctrl; processes incoming request'
- chan = self.rmap.get(code)
+ log.debug("study", "In Connection.processRequest ident=" + str(ident) + "\t code=" + str(code) + "\t data=")
+ chan = self.rmap.get(code) #所有中断都由该chan队列处理,每次只是从rmap读出内容,而没有将rmap对应的chan清除
if not chan: return #TODO
buf = JdwpBuffer()
buf.config(*self.sizes)
buf.prepareUnpack(data)
- return chan.put((ident, buf))
-
+ return chan.put((ident, buf)) #将解析后的数据压入队列中
+
+ #处理相应,chan变量是什么类型的需要关注,其与类的队列成员变量self.bindqueue有关
+ #答复数据包的flag是0x80
def processResponse(self, ident, code, data):
'internal to the i/o thread w/ recv ctrl; processes incoming response'
- chan = self.qmap.pop(ident, None)
+ log.debug("study", "In Connection.processResponse ident=" + str(ident) + "\t code=" + str(code) + "\t data=")
+ chan = self.qmap.pop(ident, None) #从字典中读取,并删除该数据
if not chan: return
buf = JdwpBuffer()
buf.config(*self.sizes)
buf.prepareUnpack(data)
return chan.put((code, buf))
+ #调用的实际情况为:conn.hook(0x4064, self.evtq),其中self.evtq是一个队列
def hook(self, code, chan):
'''
when code requests are received, they will be put in chan for
processing
'''
+ #使用锁
with self.xmitlock:
- self.bindqueue.put(('r', code, chan))
+ self.bindqueue.put(('r', code, chan)) #加入一个先进先出的队列
+ log.debug("study", "++ for hook function bindqueue.put FOR r ++ code=" + str(code))
####################################################### TRANSMITTING PACKETS
+ #申请一个请求id
def acquireIdent(self):
- 'used internally by the processor; must have xmit control'
+ 'used internally by the processor; must have xmit[传输] control'
ident = self.next_id
self.next_id += 2
return ident
+ #发送[写入]指定的数据
def writeContent(self, ident, flags, code, body):
'used internally by the processor; must have xmit control'
@@ -247,19 +285,22 @@ def writeContent(self, ident, flags, code, body):
self.write(data)
return self.write(body)
+ #构造请求
def request(self, code, data='', timeout=None):
'send a request, then waits for a response; returns response'
queue = Queue()
-
+ log.debug("study", "In Connection.request code=" + str(code) + "\t data=" + str(data))
with self.xmitlock:
ident = self.acquireIdent()
- self.bindqueue.put(('q', ident, queue))
+ self.bindqueue.put(('q', ident, queue)) #每发送一个请求向bindqueue中压入一个数据
+ log.debug("study", "++bindqueue.put FOR q ++")
self.writeContent(ident, 0x0, code, data)
try:
- return queue.get(1, timeout)
+ log.debug("study", "wait_code:" + str(code))
+ return queue.get(1, timeout) #向虚拟机发出指令后一直处于等待状态,知道queue队列中出现返回信息,接下来处理
except EmptyQueue:
- return None
+ return None, None
def buffer(self):
'returns a JdwpBuffer configured for this connection'
@@ -270,15 +311,15 @@ def buffer(self):
################################################################# THREAD API
def start(self):
- 'performs handshaking and solicits configuration information'
- self.daemon = True
+ 'performs handshaking and solicits[恳求] configuration information'
+ self.daemon = True #守护线程
- if not self.initialized:
+ if not self.initialized: #如果为false初始化尚未完成,完成下面初始化工作
self.writeHandshake()
self.readHandshake()
self.writeIdSzReq()
self.readIdSzRes()
- self.initialized = True
+ self.initialized = True #确认完成初始话
Thread.start(self)
return None
diff --git a/lib/andbug/screed.py b/lib/andbug/screed.py
index a59c374..9670a94 100644
--- a/lib/andbug/screed.py
+++ b/lib/andbug/screed.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,6 +15,7 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
+#被类的作用在于进行格式化输出,目的在于和用户交互,目前是在shell中
'''
Screed (plural Screeds)
@@ -19,9 +23,9 @@
2. A piece of writing.
3. A tool, usually a long strip of wood or other material, for producing a smooth, flat surface on, for example, a concrete floor or a plaster wall.
4. A smooth flat layer of concrete or similar material.
-5. A python module for formatting text output
+5. A python module for formatting text output ***
-Written language has evolved concurrent with the advent of movable type and
+Written language has evolved(发展) concurrent(并发) with the advent(来临) of movable(变动) type and
the information age, introducing a number of typographic conventions that are
used to impose structure. The Screed format employs a subset of these
conventions to structure output in way that is easily parsed by software or
@@ -35,13 +39,16 @@
import sys
import subprocess
import re
-import andbug.log
+#import andbug.log
+import log
+
-rx_blocksep = re.compile('[\r\n][ \t]*[\r\n]+')
+#用于正则表达式
+rx_blocksep = re.compile('[\r\n][ \t]*[\r\n]+')
rx_linesep = re.compile('[\r\n][ \t]*')
def body(data):
- blocks = rx_blocksep.split(data.strip())
+ blocks = rx_blocksep.split(data.strip()) #strip函数除去字符串首尾的空格
for block in blocks:
block = block.strip()
@@ -51,12 +58,13 @@ def body(data):
item(block[3:])
else:
text(block)
-
+#运行tput linux命令, 用来实现和用户的复杂交流,如光标的移动和输出数据颜色的变化 self.width = tput('cols', self.width)
def tput(attr, alt=None):
- p = subprocess.Popen(('tput', attr), stdout=subprocess.PIPE, stderr=None)
+ p = subprocess.Popen(('tput', attr), stdout=subprocess.PIPE, stderr=None)
p.wait()
if p.returncode:
return alt
+
o, _ = p.communicate()
return int(o)
@@ -76,6 +84,15 @@ def exit(self):
pass
def create(self):
pass
+'''
+__init__、 __enter__、 __exit__ 三个函数是object默认定义的。
+三个函数的作用分别是:class声明是调用__init__
+进入with指令时,调用__enter__函数
+跳出with指令时,调用__exit__函数
+
+而enter、exit、create函数不是python原有函数,是自定义的用来被上面调用的函数
+'''
+
class section(area):
def create(self):
@@ -124,9 +141,9 @@ def __init__(self, output=None):
if output is None:
output = sys.stdout
self.output = output
- self.tty = self.output.isatty()
+ self.tty = self.output.isatty() #istty判断文件是否是一个终端设备文件
self.indent = []
- self.textwrap = textwrap.TextWrapper()
+ self.textwrap = textwrap.TextWrapper() #文本包装和填充
def __call__(self):
return self
@@ -178,7 +195,8 @@ def create_dump(self, data):
width -= 13 # overhead
width = width / 4 # dd_c
- hex = andbug.log.format_hex(data, self.current_indent, width)
+ #hex = andbug.log.format_hex(data, self.current_indent, width)
+ hex = log.format_hex(data, self.current_indent, width) #函数的定义
self.write(hex)
self.newline()
@@ -230,6 +248,7 @@ def load(self, tag, depth):
if not depth: return ''
return (self.c256 if (depth == 256) else self.c16).get(tag, '\x1B[0m')
+#定义显示的方式
redmedicine = scheme((
('##', 9, 69),
('--', 15, 254),
@@ -254,7 +273,7 @@ def __init__(self, output=None, width=None, depth=None, palette=redmedicine):
self.prev_tag = ''
self.palette = palette
- def transition(self, next):
+ def transition(self, next): #转换
prev = self.prev_tag
self.prev_tag = next
#print "TRANSITION", repr(prev), "->", repr(next)
@@ -273,7 +292,7 @@ def transition(self, next):
self.newline()
self.prev_tag = '00'
- def create_section(self, title):
+ def create_section(self, title): #section[部分]
self.create_tagged_area( '##', title)
def enter_section(self, title):
@@ -282,7 +301,7 @@ def enter_section(self, title):
def exit_section(self, title):
self.exit_tagged_area()
- def create_item(self, title):
+ def create_item(self, title): #item[项目]
self.create_tagged_area( '--', title)
def enter_item(self, title):
@@ -291,7 +310,7 @@ def enter_item(self, title):
def exit_item(self, title):
self.exit_tagged_area()
- def create_meta(self, title):
+ def create_meta(self, title):#meta[元的]
self.create_tagged_area( '//', title)
def enter_meta(self, title):
@@ -300,7 +319,7 @@ def enter_meta(self, title):
def exit_meta(self, title):
self.exit_tagged_area()
- def create_refer(self, title):
+ def create_refer(self, title):#refer[涉及]
self.create_tagged_area( '::', title)
def enter_refer(self, title):
@@ -321,7 +340,7 @@ def create_tagged_area(self, tag, banner):
tag += ' '
self.next_indent = self.current_indent + ' ' * len(tag)
self.wrap_line(self.current_indent + tag + banner, self.next_indent)
- self.write("\x1B[0m")
+ self.write("\x1B[0m") #待输入提示符号“>>”
def enter_tagged_area(self):
self.push_indent(self.next_indent)
@@ -335,11 +354,11 @@ def exit_tagged_area(self):
if self.prev_tag != '00':
self.prev_tag = next
#print 'EXIT ->', repr(self.prev_tag)
-
+ #获取终端的相关信息
def pollcap(self):
if not self.tty: return
- self.width = tput('cols', self.width)
- self.depth = tput('colors', self.depth)
+ self.width = tput('cols', self.width) #显示终端的列数,def tput(attr, alt=None):
+ self.depth = tput('colors', self.depth) #获取终端的颜色信息
self.textwrap.width = self.width
OUTPUT = None
@@ -351,12 +370,14 @@ def scheme():
else:
return PALETTE
-def output():
+def output(): #output函数返回的是一个ascii类
global OUTPUT
if OUTPUT is None:
OUTPUT = ascii(palette=scheme())
return OUTPUT
+
+#用于测试的函数
if __name__ == '__main__':
with section('Introduction'):
text('''Since the sentence detection algorithm relies on string.lowercase for the definition of lowercase letter, and a convention of using two spaces after a period to separate sentences on the same line, it is specific to English-language texts.''')
@@ -365,6 +386,11 @@ def output():
item('''String that will be prepended to the first line of wrapped output. Counts towards the length of the first line.''')
text('''this is some inbetween text''')
item('''This is a much shorter item.''')
+ item('''This is a much shorter item.''')
+ meta('''1111111111111''')
+ meta('''1111111111111''')
+ refer('''11111111111111111111111111111111111''')
+
with section('Data'):
dump(open('/dev/urandom').read(1024))
with section('Conclusion'):
diff --git a/lib/andbug/source.py b/lib/andbug/source.py
index c2c231e..38fdf6d 100644
--- a/lib/andbug/source.py
+++ b/lib/andbug/source.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -104,8 +107,8 @@ def exit():
line = line.strip()
if not line: continue
lead = line[0]
- if lead == '.':
- if line.startswith(".method "):
+ if lead == '.': #标注信息
+ if line.startswith(".method "): #函数标识
section(line[1:])
elif line.startswith(".end"):
exit()
@@ -113,9 +116,9 @@ def exit():
andbug.screed.line(line, row)
else:
item(line[1:])
- elif line.startswith(":"):
+ elif line.startswith(":"): #跳转索引信息的标签
refer(line[1:])
- elif line.startswith("#"):
+ elif line.startswith("#"): #注释
meta(line[1:])
elif line == '*/}':
pass # meh
diff --git a/lib/andbug/util.py b/lib/andbug/util.py
index 984b227..4792d89 100644
--- a/lib/andbug/util.py
+++ b/lib/andbug/util.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## Redistribution and use in source and binary forms, with or without
@@ -23,11 +26,16 @@
## ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
## POSSIBILITY OF SUCH DAMAGE.
-import subprocess, os, os.path
+import subprocess, threading, os, os.path
import re
+
from andbug.errors import *
+from andbug import log
+
RE_INT = re.compile('^[0-9]+$')
+from cStringIO import StringIO
+
class ShellException( Exception ):
def __init__( self, command, output, status ):
self.command = command
@@ -39,6 +47,7 @@ def printout( prefix, data ):
if not data: return ''
print prefix + data.replace( '\n', '\n' + prefix )
+#创建一个进程,去执行一个指定的command
def sh( command, no_echo=True, no_fail=False, no_wait=False ):
if not no_echo:
printout( '>>> ', repr( command ) )
@@ -55,7 +64,7 @@ def sh( command, no_echo=True, no_fail=False, no_wait=False ):
output, _ = process.communicate( )
status = process.returncode
-
+ #print "status=" + str(status)
if status:
if not no_echo: printout( '!!! ', output )
if not no_fail: raise ShellException( command, output, status )
@@ -64,6 +73,22 @@ def sh( command, no_echo=True, no_fail=False, no_wait=False ):
return output
+def ShellIO( command):
+ '''
+ 临时增加以便解决多次输入的问题
+ '''
+ print "begin"
+ process = subprocess.Popen(
+ command,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE,
+ stdin = subprocess.PIPE,
+ shell = True if isinstance( command, str ) else False
+ )
+
+ print "end"
+ return process
+
def which( utility ):
for path in os.environ['PATH'].split( os.pathsep ):
path = os.path.expanduser( os.path.join( path, utility ) )
@@ -91,21 +116,24 @@ def seq(*args):
return args
def adb(*args):
- #print adb, ' '.join(map(str, args))
+ log.debug(adb, ' '.join(map(str, args)))
try:
return sh(seq("adb", *args))
- except OSError:
+ except OSError as err:
raise ConfigError('could not find "adb" from the Android SDK in your PATH')
+#函数用于获得设备信息,使用命令"adb devices"来获得
def find_dev(dev=None):
'determines the device for the command based on dev'
if dev:
+ #当dev不为空时,检验dev中是否是devices值
if dev not in map(
lambda x: x.split()[0],
adb('devices').splitlines()[1:-1]
):
raise OptionError('device serial number not online')
else:
+ #当dev为空时,获取devices的值后保存到dev中
lines = adb('devices').splitlines()
if len(lines) != 3:
raise OptionError(
@@ -116,8 +144,11 @@ def find_dev(dev=None):
return dev
+#获得进程id的值,执行“adb shell ps” 命令
def find_pid(pid, dev=None):
- 'determines the process id for the command based on dev, pid and/or name'
+ '''determines the process id for the command based on dev, pid and/or name
+ 返回值:如果在虚拟机中找到返回pid的值,如果没有找到返回None
+ '''
ps = ('-s', dev, 'shell', 'ps') if dev else ('shell', 'ps')
ps = adb(*ps)
@@ -136,14 +167,27 @@ def find_pid(pid, dev=None):
pid = int(pid)
ps = list(p for p in ps if p[0] == pid)
if not ps:
- raise OptionError('could not find process ' + pid)
+ #raise OptionError('could not find process ' + str(pid))
+ return None
elif pid:
ps = list(ps)
ps = list(p for p in ps if p[1] == pid)
if not ps:
- raise OptionError('could not find process ' + pid)
+ #raise OptionError('could not find process ' + str(pid))
+ return None
pid = ps[0][0]
else:
raise OptionError('process pid or name must be specified')
return pid
+
+def cur_file_dir():
+ #获取脚本路径
+ path = sys.path[0]
+ #判断为脚本文件还是py2exe编译后的文件,如果是脚本文件,则返回的是脚本的目录,
+ #如果是py2exe编译后的文件,则返回的是编译后的文件路径
+ if os.path.isdir(path):
+ return path
+ elif os.path.isfile(path):
+ return os.path.dirname(path)
+
diff --git a/lib/andbug/vm.py b/lib/andbug/vm.py
index 4698d85..97ee56e 100644
--- a/lib/andbug/vm.py
+++ b/lib/andbug/vm.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -12,11 +15,19 @@
## You should have received a copy of the GNU Lesser General Public License
## along with AndBug. If not, see .
-import andbug, andbug.data, andbug.proto, andbug.errors
+
import threading, re
from andbug.data import defer
from threading import Lock
from Queue import Queue
+import json
+
+
+import andbug #andbug.data, andbug.proto, andbug.screed
+from andbug import log
+import traceback
+
+g_jdwp_request_timeout =5
## Implementation Questions:
## -- unpackFrom methods are used to unpack references to an element from
@@ -39,10 +50,10 @@ def __init__(self, code):
self.code = code
class Element(object):
- def __repr__(self):
+ def __repr__(self): #以打印方式,输出字符串
return '<%s>' % self
- def __str__(self):
+ def __str__(self): #输出字符串
return '%s:%s' % (type(self).__name__, id(self))
class SessionElement(Element):
@@ -52,11 +63,16 @@ def __init__(self, sess):
@property
def conn(self):
return self.sess.conn
-
+
+
+#主要是指类的成员变量
class Field(SessionElement):
def __init__(self, session, fid):
SessionElement.__init__(self, session)
self.fid = fid
+ def __str__(self):
+ field_str = self.get_property() + " " + str(self.jni) + " "+ str(self.name)
+ return field_str
@classmethod
def unpackFrom(impl, sess, buf):
@@ -90,6 +106,42 @@ def volatile(self):
def transient(self):
return self.flags & 0x0080
+
+ def get_property(self):
+ '''
+ 判断field的属性是什么样的,如public static
+ '''
+ property_value= ""
+ if self.public:
+ property_value="public "
+ if self.private:
+ property_value = property_value + "private "
+ if self.protected:
+ property_value = property_value + "protected "
+ if self.static:
+ property_value = property_value + "static "
+ if self.final:
+ property_value = property_value + "final "
+ if self.volatile:
+ property_value = property_value + "volatile "
+ if self.transient:
+ property_value = property_value + "transient "
+
+ return property_value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
class Value(SessionElement):
@property
def isPrimitive(self):
@@ -111,7 +163,7 @@ def __str__(self):
@classmethod
def unpackFrom(impl, sess, buf):
- return sess.pool(impl, sess, buf.unpackFrameId())
+ return sess.pool(impl, sess, buf.unpackFrameId()) #创建一个Frame类型的对象
def packTo(self, buf):
buf.packFrameId(self.fid)
@@ -122,30 +174,43 @@ def native(self):
@property
def values(self):
+ '''
+ 1、命令 0x0a 0x01
+ 2、注释:堆栈命令集
+ 3、[StackFrame Command Set (16)][GetValues Command (1)]
+ '''
vals = {}
- if self.native: return vals
+ if self.native: return vals #如果是系统函数返回空
sess = self.sess
conn = self.conn
buf = conn.buffer()
- buf.packObjectId(self.tid)
- buf.packFrameId(self.fid)
- slots = self.loc.slots
- buf.packInt(len(slots))
+ buf.packObjectId(self.tid) #thread id
+ buf.packFrameId(self.fid) #frame id
+ slots = self.loc.slots
+ log.debug("study", "In frame thread_id=" + str(self.tid))
+ log.debug("study", "In frame frame_id=" + str(self.fid))
+ log.debug("study", "In frame len(slots)=" + str(len(slots)))
+ buf.packInt(len(slots)) #The number of values to get. 要获取局部变量的个数
for slot in slots:
- buf.packInt(slot.index)
- buf.packU8(slot.tag) #TODO: GENERICS
-
- code, buf = conn.request(0x1001, buf.data())
+ buf.packInt(slot.index) #The local variable's index in the frame. 局部变量的索引值
+ buf.packU8(slot.tag) #TODO: GENERICS #A tag identifying the type of the variable 标志变量类型的标签
+ log.debug("study", "In frame slot.index=" + str(slot.index))
+ log.debug("study", "In frame slot.tag=" + str(slot.tag))
+
+ log.debug("study", "call jdwp 0x10 01")
+ code, buf = conn.request(0x1001, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
ct = buf.unpackInt()
for x in range(0, ct):
s = slots[x]
- vals[s.name] = unpack_value(sess, buf)
+ vals[s.name] = unpack_value(sess, buf) #The number of values retrieved, always equal to slots, the number of values to get.
+ log.debug("study", "In frame vals[%s]= %s"%(s.name, vals[s.name]))
+ log.debug("study", "In frame date for String= %s"%(buf))
return vals
def value(self, name):
@@ -223,21 +288,53 @@ def __str__(self):
return 'thread %s\t(%s %s)' % (self.name or hex(self.tid), Thread.threadStatusStr(tStatus), Thread.suspendStatusStr(sStatus))
def suspend(self):
+ '''
+ 1、似乎命令应该是0x0b 0x01 暂停线程的命令 [ThreadReference Command Set (11)][Suspend Command (2)]
+ 2、用于暂停单个线程。
+
+ '''
conn = self.conn
buf = conn.buffer()
buf.packObjectId(self.tid)
- code, buf = conn.request(0x0b02, buf.data())
+ log.debug("study", "call jdwp 0x0B 01")
+ code, buf = conn.request(0x0B01, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
def resume(self):
+ '''
+ 1、命令是0x0b 0x03 重新启动线程 [ThreadReference Command Set (11)][Resume Command (3)]
+ 2、启动之前的线程
+ '''
conn = self.conn
buf = conn.buffer()
buf.packObjectId(self.tid)
- code, buf = conn.request(0x0b03, buf.data())
+ log.debug("study", "call jdwp 0x0B 03")
+ code, buf = conn.request(0x0B03, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
+ #add by sq.luo
+ #self.sess.suspendState.resume(self.sess)
+ #add by sq.luo
+ def singleStep(self, func = None, queue = None, stepdepth = 1):
+ suspendState = self.sess.suspendState
+ if suspendState.set:
+ self.sess.emap[suspendState.eid].clear()
+ conn = self.conn
+ buf = conn.buffer()
+ # 1:SINGLE_STEP, 1: SP_THREAD, 1: modifers 10: condition of Step
+ # 1: Step size :Line 1: stepdepth: over
+ buf.pack('11i1tii', 1, 1, 1, 10, self.tid, 1, stepdepth)
+ code, buf = conn.request(0x0f01, buf.data())
+ if code != 0:
+ raise RequestError(code)
+ eid = buf.unpackInt()
+ self.sess.suspendState.setBreakPoint(eid, stepdepth)
+ h = self.sess.hook(eid, func, queue, self)
+ self.resume();
+ return h
+
def packTo(self, buf):
buf.packObjectId(self.tid)
@@ -245,6 +342,7 @@ def hook(self, func = None, queue = None):
conn = self.conn
buf = conn.buffer()
# 40:EK_METHOD_ENTRY, 1: SP_THREAD, 1 condition of type ClassRef (3), ThreadId
+ log.debug("study", "call jdwp 0x0f 01")
buf.pack('11i1t', 40, 1, 1, 3, self.tid)
code, buf = conn.request(0x0f01, buf.data())
if code != 0:
@@ -254,45 +352,59 @@ def hook(self, func = None, queue = None):
@classmethod
def unpackFrom(impl, sess, buf):
- tid = buf.unpackObjectId()
- return sess.pool(impl, sess, tid)
+ tid = buf.unpackObjectId() #获取当前线程的ID值
+ return sess.pool(impl, sess, tid) #创造一个Thred类,sess和tid是传递给构造函数的变量
@property
def frames(self):
+ '''命令 0x0b 0x06 功能返回当前挂起线程的堆栈信息, [ThreadReference Command Set (11)][Frames Command(6)]
+ '''
tid = self.tid
sess = self.sess
- conn = self.conn
+ conn = self.conn #conn是Connection类型的变量
buf = conn.buffer()
- buf.pack('oii', self.tid, 0, -1)
- code, buf = conn.request(0x0b06, buf.data())
+ buf.pack('oii', self.tid, 0, -1) #三个参数,tid 线程id;0表示从堆栈最开始位置获取,;-1表示获取所有的堆栈信息
+ log.debug("study", "call jdwp 0x0B 06")
+ code, buf = conn.request(0x0B06, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
- ct = buf.unpackInt()
-
- def load_frame():
- f = Frame.unpackFrom(sess, buf)
- f.loc = Location.unpackFrom(sess, buf)
- f.tid = tid
+ ct = buf.unpackInt() #堆栈信息的个数
+ #这个jdwp命令返回的数据是:ct:堆栈的个数,frameID:每个堆栈的id;location:位置信息
+
+ def load_frame(): #用来解析返回的堆栈数据
+ f = Frame.unpackFrom(sess, buf) #f为返回的Frame堆栈类型的对象
+ f.loc = Location.unpackFrom(sess, buf) #Location类型的对象
+ f.tid = tid #传递当前线程的id
return f
return andbug.data.view(load_frame() for i in range(0,ct))
@property
- def frameCount(self):
+ def frameCount(self):
+ '''命令: 0x0b 0x07
+ 功能 : 获取挂起线程的堆栈帧的个数
+ [ThreadReference Command Set (11)][FrameCount Command (7)#]
+ '''
conn = self.conn
buf = conn.buffer()
buf.packObjectId(self.tid)
- code, buf = conn.request(0x0b07, buf.data())
+ log.debug("study", "call jdwp 0x0B 07")
+ code, buf = conn.request(0x0B07, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
return buf.unpackInt()
@property
def name(self):
+ '''命令: 0x0b 0x01
+ 功能 : 获取线程名称
+ [ThreadReference Command Set (11)][FrameCount Command (1)#]
+ '''
conn = self.conn
buf = conn.buffer()
buf.packObjectId(self.tid)
- code, buf = conn.request(0x0b01, buf.data())
+ log.debug("study", "call jdwp 0x0B 01")
+ code, buf = conn.request(0x0B01, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
return buf.unpackStr()
@@ -328,12 +440,16 @@ def suspendStatusStr(sStatus):
return szSS[sStatus]
class Location(SessionElement):
+ '''
+ 类功能:描述代码中的一个位置
+ '''
def __init__(self, sess, tid, mid, loc):
SessionElement.__init__(self, sess)
- self.tid = tid
- self.mid = mid
- self.loc = loc
+ self.tid = tid #class type id
+ self.mid = mid #method id
+ self.loc = loc #long 类型的整数
self.line = None
+ log.debug("study", "in Loction class: tid=" + str(tid) + "\t mid=" + str(mid) + "\t loc=" + str(loc))
def __str__(self):
if self.loc >= 0:
@@ -344,15 +460,42 @@ def __str__(self):
def packTo(self, buf):
c = self.klass
buf.ipack('1tm8', c.tag, self.tid, self.mid, self.loc)
+ log.debug("study", "in Location.packTo: tag=" + str(c.tag) + "\t tid=" + str(c.tid) + "\t mid=" + str(self.mid) + "\t loc=" + str(self.loc))
- @classmethod
+ @classmethod #类方法
def unpackFrom(impl, sess, buf):
tag, tid, mid, loc = buf.unpack('1tm8')
- return sess.pool(impl, sess, tid, mid, loc)
+ log.debug("study", "In Location.unpackFrom: tag=" + str(tag) + "\t tid=" + str(tid) + "\t mid=" + str(mid) + "\t loc=" + str(loc))
+ return sess.pool(impl, sess, tid, mid, loc) #设置一个Location类型
+
+ def hookOut(self, func=None, queue=None):
+ '''
+ 功能:函数调用结束时,将程序hook终端
+ '''
+ conn = self.conn
+ buf = conn.buffer()
+ # 40:EK_METHOD_ENTRY, 1: EVENT_THREAD, 1 condition of type Location (7) Case LocationOnly - if modKind is 7:
+ buf.pack('11i1', 41, 1, 1, 7) #只实现了METHOD_ENTRY方法入口事件的处理,其他事件没有处理
+
+ self.packTo(buf) #在这里将loc传入jdwp的参数,
+ log.debug("study", "call jdwp 0x0F 01")
+ code, buf = conn.request(0x0F01, buf.data(), g_jdwp_request_timeout)
+ if code != 0:
+ raise RequestError(code)
+ eid = buf.unpackInt() #返回的是一个ID of created request,用来区别与这个断点
+ log.debug("study", "eid=" + str(eid))
+ return self.sess.hook(eid, func, queue, self) #queue参数为空 sess的类型是Session
def hook(self, func = None, queue = None):
+ '''
+ 命令:0x0f 0x01
+ 功能:设置一个事件请求,调用指定函数时,中断函数
+ [EventRequest Command Set (15)] [Set Command (1)]
+ 注:所设置的具体事件由 buf.pack('11i1', 40, 1, 1, 7) 确定
+ '''
conn = self.conn
buf = conn.buffer()
+ # 40:EK_METHOD_ENTRY, 1: EVENT_THREAD, 1 condition of type Location (7) Case LocationOnly - if modKind is 7:
# 2: BREAKPOINT
# 40:METHOD_ENTRY
# 41:METHOD_EXIT
@@ -363,14 +506,16 @@ def hook(self, func = None, queue = None):
else:
eventKind = 2
# 1: SP_THREAD, 1 condition of type Location (7)
- buf.pack('11i1', eventKind, 1, 1, 7)
+ buf.pack('11i1', eventKind, 1, 1, 7) #只实现了METHOD_ENTRY方法入口事件的处理,其他事件没有处理
- self.packTo(buf)
- code, buf = conn.request(0x0f01, buf.data())
+ self.packTo(buf) #在这里将loc传入jdwp的参数,
+ log.debug("study", "call jdwp 0x0F 01")
+ code, buf = conn.request(0x0F01, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
- eid = buf.unpackInt()
- return self.sess.hook(eid, func, queue, self)
+ eid = buf.unpackInt() #返回的是一个ID of created request,用来区别与这个断点
+ log.debug("study", "eid=" + str(eid))
+ return self.sess.hook(eid, func, queue, self) #queue参数为空 sess的类型是Session
@property
def native(self):
@@ -382,20 +527,40 @@ def method(self):
@property
def klass(self):
- return self.sess.pool(Class, self.sess, self.tid)
+ return self.sess.pool(Class, self.sess, self.tid) #从sess中找指定tid的class对象,这里应该能够找到(之前已经创建了),返回该class对象
@property
def slots(self):
+ log.debug("study", "In Location slots")
+ log.debug("study", "self.native=" + str(self.native))
l = self.loc
def filter_slots():
- for slot in self.method.slots:
+ for slot in self.method.slots:
f = slot.firstLoc
+ log.debug("study", "In Location.slots l=" + str(l) + "\t f=" + str(f))
if f > l: continue
if l - f > slot.locLength: continue
yield slot
- return tuple() if self.native else tuple(filter_slots())
-
+ return tuple() if self.native else tuple(filter_slots())#如果是系统函数,返回没有内容的空元组
+ '''
+ In Location.slots l=9 f=93
+ In Location.slots l=9 f=93
+ In Location.slots l=9 f=93
+ In Location.slots l=9 f=173
+ In Location.slots l=9 f=150
+ In Location.slots l=9 f=156
+ In Location.slots l=9 f=144
+ In Location.slots l=9 f=93
+ In Location.slots l=9 f=2
+ In Location.slots l=9 f=93
+ In Location.slots l=9 f=1
+ In Location.slots l=9 f=93
+ In Location.slots l=9 f=0
+ '''
class Slot(SessionElement):
+ '''
+ 类的功能:描述一个变量的信息,主要用来描述成员函数中的成员变量和函数参数
+ '''
def __init__(self, sess, tid, mid, index):
SessionElement.__init__(self, sess)
self.tid = tid
@@ -410,6 +575,7 @@ def __str__(self):
return 'slot at index %i' % (self.index)
def load_slot(self):
+ log.debug("study", "############In slot.load ")
self.sess.pool(Class, self.sess, self.tid).load_slots()
firstLoc = defer(load_slot, 'firstLoc')
@@ -425,12 +591,12 @@ def tag(self):
class Method(SessionElement):
def __init__(self, sess, tid, mid):
SessionElement.__init__(self, sess)
- self.tid = tid
- self.mid = mid
+ self.tid = tid #refType id 的值
+ self.mid = mid #method id 的值
@property
def klass(self):
- return self.sess.pool(Class, self.sess, self.tid)
+ return self.sess.pool(Class, self.sess, self.tid) #这里是TypeId
def __str__(self):
return '%s.%s%s' % (
@@ -441,40 +607,69 @@ def __repr__(self):
return '' % self
def load_line_table(self):
+ '''
+ 命令:0x06 0x01
+ 功能:返回函数的行号信息
+ [Method Command Set (6)][LineTable Command (1)]
+ '''
+
+ if self.abstract!=0:
+ #说明当前方法是一个抽象方法
+ log.debug("study", "the function [" + self.name + "]is abstract function")
+ log.debug("study", "flag:" + str(self.flags))
+ log.debug("study", "abstract:" + str(self.abstract))
+ self.firstLoc = None
+ self.lastLoc = None
+ self.lineTable = None
+ return
+
sess = self.sess
conn = sess.conn
pool = sess.pool
tid = self.tid
mid = self.mid
- data = conn.buffer().pack('om', tid, mid)
- code, buf = conn.request(0x0601, data)
- if code != 0: raise RequestError(code)
+ data = conn.buffer().pack('om', tid, mid) #输入参数是type id和method id
+ log.debug("study", "call jdwp 0x06 01")
+ code, buf = conn.request(0x0601, data, g_jdwp_request_timeout)
+ log.debug("study", "finish " + str(buf)+ " code:" + str(code))
+ if code != 0:
+ raise RequestError(code)
+
+
f, l, ct = buf.unpack('88i')
+ log.debug("study", "firstLoc=" + str(f) + "\t lastLoc=" + str(l) + "\t lineTable=" + str(ct))
if (f == -1) or (l == -1):
self.firstLoc = None
self.lastLoc = None
self.lineTable = andbug.data.view([])
#TODO: How do we handle native methods?
-
- self.firstLoc = pool(Location, sess, tid, mid, f)
+
+ self.firstLoc = pool(Location, sess, tid, mid, f) #声明一个Locaton类来保存获取的locaiton信息
self.lastLoc = pool(Location, sess, tid, mid, l)
+
ll = {}
+ #self.lineLocs = ll #这个变量是否应该是lineTable,在正常路径中linetable没有赋值
self.lineTable = ll
- def line_loc():
+ def line_loc(): #根据获取的函数的代码行数信息,逐行取出代码信息
loc, line = buf.unpack('8i')
- loc = pool(Location, sess, tid, mid, loc)
- loc.line = line
+ log.debug("study", "loc="+ str(loc) + "\t line=" + str(line))
+ loc = pool(Location, sess, tid, mid, loc)
+ loc.line = line
ll[line] = loc
+
- for i in range(0,ct):
+ for i in range(0,ct):
line_loc()
+
- firstLoc = defer(load_line_table, 'firstLoc')
+ firstLoc = defer(load_line_table, 'firstLoc') #methods类中的变量,所以对于每个方法都会有这些信息
lastLoc = defer(load_line_table, 'lastLoc')
lineTable = defer(load_line_table, 'lineTable')
+
+
def load_method(self):
self.klass.load_methods()
@@ -484,16 +679,27 @@ def load_method(self):
flags = defer(load_method, 'flags' )
def load_slot_table(self):
+ '''
+ 1 命令:0x06 0x05
+ 2 功能:获取一个方法中的参数和变量的信息
+ 3 解释: [Method Command Set (6)][VariableTableWithGeneric Command (5)]
+ '''
sess = self.sess
conn = self.conn
pool = sess.pool
tid = self.tid
mid = self.mid
data = conn.buffer().pack('om', tid, mid)
- code, buf = conn.request(0x0605, data)
+ log.debug("study", "In Method.load_slot_table classTypeId=" + str(tid) + "\t mid=" + str(mid))
+ log.debug("study", "call jdwp 0x06 05")
+ code, buf = conn.request(0x0605, data, g_jdwp_request_timeout)
+ log.debug("study", "code="+ str(code))
if code != 0: raise RequestError(code)
- act, sct = buf.unpack('ii')
+ act, sct = buf.unpack('ii') #获取参数的个数,获取自变量的个数
+ self.arg_cnt = act
+ self.slot_cnt = sct
+ log.debug("study", "In Method.load_slot_table argCount=" + str(act) + "\t sct=" + str(sct))
#TODO: Do we care about the argCnt ?
def load_slot():
@@ -504,18 +710,130 @@ def load_slot():
slot.name = name
slot.jni = jni
slot.gen = gen
+ log.debug("study", "In Method.load_slot_table.load_slot firstLoc=" + str(slot.firstLoc) + "\t locLength=" + str(slot.locLength) + "\t name=" + str(slot.name) + "\t jni=" + str(slot.jni) + "\t gen=" + str(slot.gen))
return slot
self.slots = andbug.data.view(load_slot() for i in range(0,sct))
slots = defer(load_slot_table, 'slots')
+
+
+
+ def load_bytecodes (self):
+ '''
+ 函数功能:获得一个函数的字节码
+ 注:dilvik 暂时不支持通过0x0603指令获得函数字节码的功能
+ '''
+ sess = self.sess
+ conn = self.conn
+ pool = sess.pool
+ tid = self.tid
+ mid = self.mid
+ bytecode = ''
+ data = conn.buffer().pack('om', tid, mid)
+ log.debug("study", "call jdwp 0x06 03")
+ code, buf = conn.request(0x0603, data, g_jdwp_request_timeout)
+ if code !=0: raise RequestError(code)
+
+ bytecodeLen = buf.unpack('i')
+
+ for i in range(0,bytecodeLen):
+ bytecode += buf.unpackU8()
+
+ log.debug("study", "bytecode=" + bytecode)
+
+
+ @property
+ def public(self):
+ return self.flags & 0x0001
+
+ @property
+ def private(self):
+ return self.flags & 0x0002
+
+ @property
+ def protected(self):
+ return self.flags & 0x0004
+
+ @property
+ def static(self):
+ return self.flags & 0x0008
+
+ @property
+ def final(self):
+ return self.flags & 0x0010
+
+
+ @property
+ def synchronized(self):
+ return self.flags & 0x0020
+
+
+ @property
+ def bridge(self):
+ return self.flags & 0x0040
+
+ @property
+ def varargs(self):
+ return self.flags & 0x0080
+
+ @property
+ def native(self):
+ return self.flags & 0x0100
+
+ @property
+ def abstract(self):
+ return self.flags & 0x0400
+
+ @property
+ def strict(self):
+ return self.flags & 0x0800
+
+ @property
+ def synthetic(self):
+ return self.flags & 0x1000
+
+ def get_property(self):
+ '''
+ 判断field的属性是什么样的,如public static
+ '''
+
+ property_value= ""
+ if self.public:
+ property_value="public "
+ if self.private:
+ property_value = property_value + "private "
+ if self.protected:
+ property_value = property_value + "protected "
+ if self.static:
+ property_value = property_value + "static "
+ if self.final:
+ property_value = property_value + "final "
+ if self.synchronized:
+ property_value = property_value + "synchronized "
+ if self.bridge:
+ property_value = property_value + "bridge "
+ if self.varargs:
+ property_value = property_value + "varargs "
+ if self.native:
+ property_value = property_value + "native "
+ if self.abstract:
+ property_value = property_value + "abstract "
+ if self.strict:
+ property_value = property_value + "strict "
+ if self.synthetic:
+ property_value = property_value + "synthetic "
+
+ return property_value
+
class RefType(SessionElement):
def __init__(self, sess, tag, tid):
SessionElement.__init__(self, sess)
self.tag = tag
self.tid = tid
+ log.debug("study", "in RefType class: tag=" + str(tag) + "\t tid=" + str(tid))
def __repr__(self):
return '' % (self.jni, chr(self.tag), self.tid)
@@ -531,10 +849,16 @@ def packTo(self, buf):
buf.packObjectId(self.tid)
def load_signature(self):
+ ''''
+ 1 命令: 0x02 0x0d
+ 2 功能:返回引用类型的JNI signature和generic signature
+ 3 解释:[ReferenceType Command Set (2)][SignatureWithGeneric Command (13)]
+ '''
conn = self.conn
buf = conn.buffer()
self.packTo(buf)
- code, buf = conn.request(0x020d, buf.data())
+ log.debug("study", "call jdwp 0x02 0d")
+ code, buf = conn.request(0x020d, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
self.jni = buf.unpackStr()
@@ -544,19 +868,27 @@ def load_signature(self):
jni = defer(load_signature, 'jni')
def load_fields(self):
+ ''''
+ 1 命令: 0x02 0x0e
+ 2 功能:返回信息,包括 引用类型中每一个字段的通用签名
+ 3 解释:[ReferenceType Command Set (2)][FieldsWithGeneric Command (14)]
+ '''
sess = self.sess
conn = self.conn
buf = conn.buffer()
buf.pack("t", self.tid)
- code, buf = conn.request(0x020e, buf.data())
+ log.debug("study", "load_fields tid=" + str(self.tid))
+ log.debug("study", "call jdwp 0x02 0E")
+ code, buf = conn.request(0x020E, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
ct = buf.unpackU32()
def load_field():
- field = Field.unpackFrom(sess, buf)
+ field = Field.unpackFrom(sess, buf) #先获取field的值
name, jni, gen, flags = buf.unpack('$$$i')
+ #log.debug("study", "field_id="+ str(field) +"\t name=" + str(name) + "\t jni=" + str(jni) + "\t gen=" + str(gen) + "\t flags="+ str(flags))
field.name = name
field.jni = jni
field.gen = gen
@@ -571,45 +903,73 @@ def load_field():
@property
def statics(self):
+ ''''
+ 1 命令: 0x02 0x06
+ 2 功能:返回一个引用类型中的一个或多个静态变量的值
+ 3 解释:[ReferenceType Command Set (2)][GetValues Command (6)]
+ 4 输入参数:指定类的type id的值
+ 这个类中的静态变量的个数
+ 各静态变量的field id值
+ '''
sess = self.sess
conn = self.conn
buf = conn.buffer()
- buf.packTypeId(self.tid)
- fields = list(f for f in self.fieldList if f.static)
+ buf.packTypeId(self.tid)
+ fields = list(f for f in self.fieldList if f.static) #这里在在发起0x0206命令时,会访问
buf.packInt(len(fields))
+ log.debug("study", "statics tid(type_id)= " + str(self.tid))
+ log.debug("study", "the fields len = " + str(len(fields)))
+
for field in fields:
buf.packFieldId(field.fid)
- code, buf = conn.request(0x0206, buf.data())
+ log.debug("study", "static field id=" + str(field.fid))
+
+ log.debug("study", "call jdwp 0x02 06 获取指定类型中的静态变量信息 " )
+ code, buf = conn.request(0x0206, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
ct = buf.unpackInt()
-
+ log.debug("study", "in statics ct=" + str(ct))
vals = {}
for x in range(ct):
- f = fields[x]
- vals[f.name] = unpack_value(sess, buf)
+ f = fields[x]
+ vals[f.name] = unpack_value(sess, buf) #通过调用unpack_value函数,获取的field的值,针对值的不同内容,进行不同的处理
+ log.debug("study", "ttt_" + str(vals[f.name]))
+
+ log.debug("study", "in statics finish function")
return vals
def load_methods(self):
+ ''''
+ 1 命令: 0x02 0x0f
+ 2 功能:通过一个引用类型返回其包含的方法的通用签名信息
+ 3 解释:[ReferenceType Command Set (2)][MethodsWithGeneric Command (15)]
+ '''
tid = self.tid
sess = self.sess
conn = self.conn
pool = sess.pool
buf = conn.buffer()
- buf.pack("t", tid)
- code, buf = conn.request(0x020f, buf.data())
+ buf.pack("t", tid) #传入的值是refType id
+ log.debug("study", "call jdwp 0x02 0F "+ str(tid))
+ code, buf = conn.request(0x020F, buf.data(), g_jdwp_request_timeout)
+
if code != 0:
raise RequestError(code)
ct = buf.unpackU32()
def load_method():
- mid, name, jni, gen, flags = buf.unpack('m$$$i')
+ mid, name, jni, gen, flags = buf.unpack('m$$$i') #method_id str str str int
obj = pool(Method, sess, tid, mid)
obj.name = name
obj.jni = jni
obj.gen = gen
obj.flags = flags
+ infor = "tid="+ str(hex(tid))+ "\t mid=" + str(hex(mid)) + "\t name=" + name + "\t jni=" + jni + "\t gen=" + gen + "\t flags=" + str(hex(flags))
+ #infor = "name=" + name + "\t flags=" + str(hex(flags))
+ log.debug("study", infor)
+
return obj
self.methodList = andbug.data.view(
@@ -620,6 +980,7 @@ def load_method():
for item in self.methodList:
jni = item.jni
+ log.debug("study", str(jni))
name = item.name
self.methodByJni[jni] = item
self.methodByName[name] = item
@@ -632,10 +993,12 @@ def load_method():
methodByJni = defer(load_methods, 'methodByJni')
methodByName = defer(load_methods, 'methodByName')
- def methods(self, name=None, jni=None):
+ def methods(self, name=None, jni=None, filtername=None):
if name and jni:
+ log.debug("study", name + "\t" + jni)
seq = self.methodByName[name]
- seq = filter(x in seq, self.methodByJni[jni])
+ log.debug("study", "seq=" + str(seq))
+ seq = filter(x in seq, self.methodByJni[jni]) #2.7 版本的python会执行错误
elif name:
seq = andbug.data.view(self.methodByName[name])
elif jni:
@@ -653,7 +1016,8 @@ def name(self):
return name
class Class(RefType):
- def __init__(self, sess, tid):
+ #在obj = self.pool(Class, self, tid) 代码处,会初始化大量的Class类的对象
+ def __init__(self, sess, tid): #两个参数分别是:Session类行的变量,与具体类所对应的typeid的值
RefType.__init__(self, sess, 'L', tid)
def __str__(self):
@@ -663,14 +1027,22 @@ def __repr__(self):
return '' % self
def hookEntries(self, func = None, queue = None):
+ '''
+ 1 命令: 0x0f 0x01
+ 2 功能:设置事件
+ 3 解释:[EventRequest Command Set (15)][Set Command (1)]
+ '''
conn = self.conn
buf = conn.buffer()
- # 40:EK_METHOD_ENTRY, 1: SP_THREAD, 1 condition of type ClassRef (4)
- buf.pack('11i1t', 40, 1, 1, 4, self.tid)
- code, buf = conn.request(0x0f01, buf.data())
+ # 40:KEK_METHOD_ENTRY, 1: EVENT_THREAD, 1:modifiers(只有一个mod) 4:modKind是4,含义是 condition of type ClassRef (4)
+ # 针对4这个modkind值,需要传入一个指定的Reference TypeID
+ buf.pack('11i1t', 40, 1, 1, 4, self.tid) #tid为type id
+ log.debug("study", "call jdwp 0x0F 01")
+ code, buf = conn.request(0x0F01, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
- eid = buf.unpackInt()
+ eid = buf.unpackInt() #返回值是一个requestID :ID of created request
+ log.debug("study", "eid=" + str(eid)) #eid=536870915
return self.sess.hook(eid, func, queue, self)
#def load_class(self):
@@ -696,7 +1068,8 @@ def __init__(self, sess, ident, func = None, queue = None, origin = None):
self.origin = origin
#TODO: unclean
with self.sess.ectl:
- self.sess.emap[ident] = self
+ self.sess.emap[ident] = self #ident ID of created request
+ log.debug("study", "in Hook __init__ ident=" + str(ident))
def __str__(self):
return ('<%s> %s %s' %
@@ -704,7 +1077,7 @@ def __str__(self):
def put(self, data):
if self.func is not None:
- return self.func(data)
+ return self.func(data) #在这里回调事件处理函数,所有参数全部传入回调函数,分别包含Thread类型和Locaion两个类型的对象作为参数
else:
return self.queue.put(data)
@@ -725,6 +1098,71 @@ def clear(self):
with self.sess.ectl:
del self.sess.emap[self.ident]
+
+
+
+class VmCapability(Element):
+ '''
+ 记录当前vm所支持的功能
+ '''
+ def __init__(self, capabilityBuf, newCapabilityBuf):
+ # reserved16 - reserved32
+ self.vm_cap = {}
+ self.vm_cap["canWatchFieldModification"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canWatchFieldAccess"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canGetBytecodes"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canGetSyntheticAttribute"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canGetOwnedMonitorInfo"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canGetCurrentContendedMonitor"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canGetMonitorInfo"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canRedefineClasses"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canAddMethod"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canUnrestrictedlyRedefineClasses"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canPopFrames"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canUseInstanceFilters"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canGetSourceDebugExtension"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canRequestVMDeathEvent"] = newCapabilityBuf.unpackU8()
+ self.vm_cap["canSetDefaultStratum"] = newCapabilityBuf.unpackU8()
+
+
+ '''
+ self.canWatchFieldModification = newCapabilityBuf.unpackU8()
+ self.canWatchFieldAccess = newCapabilityBuf.unpackU8()
+ self.canGetBytecodes = newCapabilityBuf.unpackU8()
+ self.canGetSyntheticAttribute = newCapabilityBuf.unpackU8()
+ self.canGetOwnedMonitorInfo = newCapabilityBuf.unpackU8()
+ self.canGetCurrentContendedMonitor = newCapabilityBuf.unpackU8()
+ self.canGetMonitorInfo = newCapabilityBuf.unpackU8()
+ self.canRedefineClasses = newCapabilityBuf.unpackU8()
+ self.canAddMethod = newCapabilityBuf.unpackU8()
+ self.canUnrestrictedlyRedefineClasses = newCapabilityBuf.unpackU8()
+ self.canPopFrames = newCapabilityBuf.unpackU8()
+ self.canUseInstanceFilters = newCapabilityBuf.unpackU8()
+ self.canGetSourceDebugExtension = newCapabilityBuf.unpackU8()
+ self.canRequestVMDeathEvent = newCapabilityBuf.unpackU8()
+ self.canSetDefaultStratum = newCapabilityBuf.unpackU8()
+ '''
+
+ def __str__(self): #输出字符串
+ dataStr = "canWatchFieldModification=" + str(self.canWatchFieldModification) + "\r\n"
+ dataStr += "canWatchFieldAccess=" + str(self.canWatchFieldAccess) + "\r\n"
+ dataStr += "canGetBytecodes=" + str(self.canGetBytecodes) + "\r\n"
+ dataStr += "canGetSyntheticAttribute=" + str(self.canGetSyntheticAttribute) + "\r\n"
+ dataStr += "canGetOwnedMonitorInfo=" + str(self.canGetOwnedMonitorInfo) + "\r\n"
+ dataStr += "canGetCurrentContendedMonitor=" + str(self.canGetCurrentContendedMonitor) + "\r\n"
+ dataStr += "canGetMonitorInfo=" + str(self.canGetMonitorInfo) + "\r\n"
+ dataStr += "canRedefineClasses=" + str(self.canRedefineClasses) + "\r\n"
+ dataStr += "canAddMethod=" + str(self.canAddMethod) + "\r\n"
+ dataStr += "canUnrestrictedlyRedefineClasses=" + str(self.canUnrestrictedlyRedefineClasses) + "\r\n"
+ dataStr += "canPopFrames=" + str(self.canPopFrames) + "\r\n"
+ dataStr += "canUseInstanceFilters=" + str(self.canUseInstanceFilters) + "\r\n"
+ dataStr += "canGetSourceDebugExtension=" + str(self.canGetSourceDebugExtension) + "\r\n"
+ dataStr += "canRequestVMDeathEvent=" + str(self.canRequestVMDeathEvent) + "\r\n"
+ dataStr += "canSetDefaultStratum=" + str(self.canSetDefaultStratum) + "\r\n"
+
+
+ return dataStr
+
unpack_impl = [None,] * 256
@@ -741,12 +1179,16 @@ def unpack_events(sess, buf):
else:
yield im(sess, buf)
+#处理METHOD_ENTRY事件,对该事件时由虚拟机返回的数据进行解析
def unpack_event_location(sess, buf):
- rid = buf.unpackInt()
- t = Thread.unpackFrom(sess, buf)
- loc = Location.unpackFrom(sess, buf)
+ rid = buf.unpackInt() #Request that generated event
+ t = Thread.unpackFrom(sess, buf) #thread which entered method。 其中t为一个Thread类型的对象
+ loc = Location.unpackFrom(sess, buf) #The initial executable location in the method 其中loc为一个Location类型的对象
+ log.debug("study", "in unpack_methode_entry rid=" + str(rid) + "\t thread=" + str(t) + "\t loc=" + str(loc))
return rid, t, loc
+# Single Step
+register_unpack_impl(1, unpack_event_location)
# Breakpoint
register_unpack_impl(2, unpack_event_location)
# MothodEntry
@@ -754,54 +1196,101 @@ def unpack_event_location(sess, buf):
# MothodExit
register_unpack_impl(41, unpack_event_location)
+
+#add by sq.luo
+class SuspendState(object):
+ def __init__(self):
+ self.isSuspend = False
+ self.thread = None
+ self.location = None
+ self.set = False
+ self.eid = 0
+ def suspend(self, data):
+ self.isSuspend = True
+ if len(data) >= 2:
+ self.thread = data[0]
+ self.location = data[1]
+
+ def resume(self, sess):
+ self.isSuspend = False
+ self.thread = None
+ self.location = None
+ if self.set:
+ sess.emap[self.eid].clear()
+ self.set = False
+ def getThread(self):
+ return self.thread
+
+ def getLocation(self):
+ return self.location
+
+ def setBreakPoint(self, eid, stepdepth):
+ self.set = True
+ self.eid = eid
+ self.stepdepth = stepdepth
+
class Session(object):
def __init__(self, conn):
- self.pool = andbug.data.pool()
- self.conn = conn
- self.emap = {}
+ self.pool = andbug.data.pool() #在andbug/lib/andbug/data.py文件中定义
+ self.conn = conn #conn是Connection(Thread)的一个对象
+ self.emap = {} #用一个字典来存放hook点的信息,每个元素是一个Hook类型的对象
self.ectl = Lock()
self.evtq = Queue()
- conn.hook(0x4064, self.evtq)
+ conn.hook(0x4064, self.evtq) #加入evtq队列中 16484 这里是加入命令 0x40 64 转换成十进制是64 100 Event Command Set(64):Composite Command (100)
self.ethd = threading.Thread(
- name='Session', target=self.run
+ name='Session', target=self.run #线程的名称,线程的执行函数
)
- self.ethd.daemon=1
+ self.ethd.daemon=1 #主线程结束时,会把子线程也杀死。
self.ethd.start()
def run(self):
while True:
- self.processEvent(*self.evtq.get())
+ self.processEvent(*self.evtq.get()) #从evtq中取出一个队列,这个的值是在proto.Connection.processRequest函数中被压入队列的
def hook(self, ident, func = None, queue = None, origin = None):
- return Hook(self, ident, func, queue, origin)
-
- def processEvent(self, ident, buf):
- pol, ct = buf.unpack('1i')
+ return Hook(self, ident, func, queue, origin) #返回的是一个Hook类型的对象
+ def processEvent(self, ident, buf): #关注这里只有两个参数,
+ pol, ct = buf.unpack('1i') #按照格式对数据进行解析, 1表示无符号单字节数值,
+ log.debug("study", "in Session.processEvent: ident=" + str(ident) + "\t pol=" + str(pol) + "\t ct=" + str(ct))
+ #输出的值为:in Session.processEvent: ident=268435460 pol=1 ct=1
+ #其中 pol的值是suspendPolicy,标识暂停的策略,1表示只暂停当前线程, ct表示本次中断所触发的事件数
for i in range(0,ct):
- ek = buf.unpackU8()
- im = unpack_impl[ek]
+ ek = buf.unpackU8() #获取事件的类型保存到ek中,ek为jdwp协议中的eventKind
+ log.debug("study", "ek="+ str(ek))
+ im = unpack_impl[ek] #unpack_impl为定义的全局变量,该变量中保存的是函数,根据eventKind的之,获取对应的事件的处理函数,保存到im中
if im is None:
raise RequestError(ek)
- evt = im(self, buf)
- with self.ectl:
+ evt = im(self, buf) #调用具体的函数 这里im调用的是unpack_method_entry函数返回的是rid, t, loc三个变量。
+ with self.ectl: #请求锁
hook = self.emap.get(evt[0])
- if hook is not None:
- hook.put(evt[1:])
+ if hook is not None: #hook变量的类型是Hook
+ hook.put(evt[1:]) #调用Hook类型中的put函数
def load_classes(self):
- code, buf = self.conn.request(0x0114)
- if code != 0:
+ '''
+ 1、命令 0x01 0x14
+ 2、注释:释放一系列Object ID的信息列表
+ 3、[VirtualMachine Command Set ][AllClassesWithGeneric Command (20)]
+ '''
+ #在这里0x0114分别表示command=0x01和command set=0x14,即VisibleClasses和ClassLoaderReference
+ log.debug("study", "call jdwp 0x01 14")
+ code, buf = self.conn.request(0x0114, timeout=g_jdwp_request_timeout)
+ if code != 0: #如果code不为0,说明发给vm的请求发生错误。
raise RequestError(code)
def load_class():
- tag, tid, jni, gen, flags = buf.unpack('1t$$i')
- obj = self.pool(Class, self, tid)
+ tag, tid, jni, gen, flags = buf.unpack('1t$$i') #推测t为thead id 为一个DWORD型,$表示字符串,将数据解析出来
+ obj = self.pool(Class, self, tid) #保存相关信息到pool
obj.tag = tag
obj.tid = tid
obj.jni = jni
obj.gen = gen
obj.flags = flags
+
+ infor = "tag=" + str(tag) + ";\t tid=" + str(hex(tid)) + ";\t jni=" + jni + ";\t gen=" + str(gen) + ";\t flags=" +str(flags);
+ log.debug("study",infor)
+
return obj
ct = buf.unpackU32()
@@ -822,32 +1311,62 @@ def classes(self, jni=None):
return andbug.data.view(seq)
def suspend(self):
- code, buf = self.conn.request(0x0108, '')
+ ''''
+ 1、命令 0x01 0x08
+ 2、注释:暂停VM
+ 3、[VirtualMachine Command Set ][Suspend Command (8)]
+ '''
+ log.debug("study", "call jdwp 0x01 08")
+ code, buf = self.conn.request(0x0108, '', g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
@property
def count(self):
- code, buf = self.conn.request(0x0108, '')
+ ''''
+ 1、命令 0x01 0x08
+ 2、注释:似乎有问题
+ 3、[VirtualMachine Command Set ][Suspend Command (8)]
+ '''
+ log.debug("study", "call jdwp 0x01 08")
+ code, buf = self.conn.request(0x0108, '', g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
def resume(self):
- code, buf = self.conn.request(0x0109, '')
+ ''''
+ 1、命令 0x01 0x09
+ 2、注释:暂停VM
+ 3、[VirtualMachine Command Set ][Resume Command (9)]
+ '''
+ log.debug("study", "call jdwp 0x01 09")
+ code, buf = self.conn.request(0x0109, '', g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
def exit(self, code = 0):
+ ''''
+ 1、命令 0x01 0x0A
+ 2、注释:终止VM
+ 3、[VirtualMachine Command Set ][Exit Command (10)]
+ '''
conn = self.conn
buf = conn.buffer()
buf.pack('i', code)
- code, buf = conn.request(0x010A, '')
+ log.debug("study", "call jdwp 0x01 0A")
+ code, buf = conn.request(0x010A, '', g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
def threads(self, name=None):
+ ''''
+ 1、命令 0x01 0x04
+ 2、注释:终止VM
+ 3、[VirtualMachine Command Set ][AllThreads Command (4)]
+ '''
pool = self.pool
- code, buf = self.conn.request(0x0104, '')
+ log.debug("study", "call jdwp 0x01 04")
+ code, buf = self.conn.request(0x0104, '', g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
ct = buf.unpackInt()
@@ -865,6 +1384,30 @@ def load_thread():
name = name if not re.match('^\d+$', name) else '<' + name + '>'
seq = (t for t in seq if name in t.name.split(' ',1))
return andbug.data.view(seq)
+
+
+ def vmCapability(self):
+ '''
+ 函数功能:通过调用jdwp指令,获取vm所支持的功能的信息
+ 注: Capabilities Command (12)
+ CapabilitiesNew Command (17)
+ '''
+ log.debug("study", "call jdwp 0x01 0c Capabilities Command")
+ code, buf = self.conn.request(0x010c, '', g_jdwp_request_timeout)
+
+
+ log.debug("study", "call jdwp 0x01 11 CapabilitiesNew Command")
+ codeNew, bufNew = self.conn.request(0x0111, '', g_jdwp_request_timeout)
+
+
+ if code!=0:
+ raise RequestError(code)
+ elif codeNew!=0:
+ raise RequestError(codeNew)
+
+ vmCapability = VmCapability(buf, bufNew)
+
+ return vmCapability
rx_dalvik_tname = re.compile('^<[0-9]+> .*$')
@@ -881,15 +1424,43 @@ def __repr__(self):
# return str(self.fields.values())
def __str__(self):
return str("%s <%s>" % (str(self.jni), str(self.oid)))
+
+ def genJson(self):
+ '''
+ 函数功能:为将数据以json格式输出做准备
+ 参数:无
+ 返回值:dict或list
+ author:anbc
+ '''
+ data = {}
+ data["object_id"] = self.oid
+ data["object_type"] = self.jni
+ #data["fields_infor"] = self.fields
+
+
+
+
+ fieldInfor={}
+ fieldsValue = self.fields
+ for k in fieldsValue:
+ print k
+ fieldInfor[k]=str(fieldsValue[k])
+
+ data["fields_infor"] = fieldInfor
+
+ return data
@classmethod
def unpackFrom(impl, sess, buf):
oid = buf.unpackObjectId()
# oid = 0 indicates a GC omgfuckup in Dalvik
# which is NOT as uncommon as we would like..
- if not oid: return None
- return sess.pool(impl, sess, oid)
+ log.debug("study", "in unpackFrom oid(object_id)=" + str(oid))
+ if not oid: return None
+ log.debug("study", "in unpackFrom impl=" + str(impl))
+ return sess.pool(impl, sess, oid) #通过pool方法创建一个String类或Array类的对象,并返回这个对象
+ #将oid参数压入jdwp命令的输入参数
def packTo(self, buf):
buf.packObjectId(self.oid)
@@ -902,10 +1473,16 @@ def jni(self):
return self.refType.jni
def load_refType(self):
+ '''
+ 1、命令 0x09 0x01
+ 2、注释:返回一个正在运行的对象的引用类型
+ 3、[ObjectReference Command Set (9)][ReferenceType Command (1)]
+ '''
conn = self.sess.conn
buf = conn.buffer()
self.packTo(buf)
- code, buf = conn.request(0x0901, buf.data())
+ log.debug("study", "call jdwp 0x09 01")
+ code, buf = conn.request(0x0901, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
self.refType = RefType.unpackFrom(self.sess, buf)
@@ -923,6 +1500,11 @@ def typeTag(self):
@property
def fields(self):
+ '''
+ 1、命令 0x09 0x02
+ 2、注释:获得成员变量的值
+ 3、[ObjectReference Command Set (9) ][GetValues Command (2)]
+ '''
sess = self.sess
conn = self.conn
buf = conn.buffer()
@@ -931,7 +1513,8 @@ def fields(self):
buf.packInt(len(fields))
for field in fields:
buf.packFieldId(field.fid)
- code, buf = conn.request(0x0902, buf.data())
+ log.debug("study", "call jdwp 0x09 02")
+ code, buf = conn.request(0x0902, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
ct = buf.unpackInt()
@@ -939,6 +1522,7 @@ def fields(self):
for x in range(ct):
f = fields[x]
vals[f.name] = unpack_value(sess, buf)
+ log.info("study", "field: %s = %s"%(f.name, vals[f.name]))
return vals
@@ -1012,7 +1596,33 @@ def __repr__(self):
if self.jni == '[C':
return repr(''.join(data))
elif self.jni == '[B':
- return repr(''.join(chr(c) for c in data))
+
+ '''
+ #按照buffer实际长度展示
+ output=''
+ count=0
+ len = self.length
+ for c in data:
+ if count=33 and c<=126:
+ output +=chr(c)
+ else:
+ output +="$"
+ data["array_data_show"] = str(output)
+
+
+ return data
+
+
+ @property #将length方法当作属性使用
def length(self):
+ '''
+ 1、命令 0x0d 0x01
+ 2、注释:返回数组的长度
+ 3、[ArrayReference Command Set (13)][Length Command (1)]
+ '''
conn = self.conn
buf = conn.buffer()
- self.packTo(buf)
- code, buf = conn.request(0x0d01, buf.data())
+ self.packTo(buf) #调用父类Object的方法,获取object id的值
+ log.debug("study", "call jdwp 0x0d 01")
+ code, buf = conn.request(0x0d01, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
return buf.unpackInt()
def getSlice(self, first=0, last=-1):
+ '''
+ 1、命令 0x0d 0x02
+ 2、注释:获得指定数组元素的值,slice 是片段的意思
+ 3、[ArrayReference Command Set (13)][GetValues Command (2)]
+ '''
length = self.length
if first > length:
raise IndexError('first offset (%s) past length of array' % first)
@@ -1062,10 +1714,11 @@ def getSlice(self, first=0, last=-1):
conn = self.conn
buf = conn.buffer()
- self.packTo(buf)
- buf.packInt(first)
- buf.packInt(count)
- code, buf = conn.request(0x0d02, buf.data())
+ self.packTo(buf) #传入参数object id
+ buf.packInt(first) #传入数组的起始位置
+ buf.packInt(count) #传入要获取的数组的个数
+ log.debug("study", "call jdwp 0x0d 02")
+ code, buf = conn.request(0x0d02, buf.data(), g_jdwp_request_timeout)
if code != 0:
raise RequestError(code)
tag = buf.unpackU8()
@@ -1073,52 +1726,98 @@ def getSlice(self, first=0, last=-1):
sess = self.sess
if tag in OBJECT_TAGS:
- return tuple(unpack_value(sess, buf) for i in range(ct))
+ return list(unpack_value(sess, buf) for i in range(ct)) #处理对象类型的元素
else:
- return tuple(unpack_value(sess, buf, tag) for i in range(ct))
+ return list(unpack_value(sess, buf, tag) for i in range(ct)) #处理主类型的元素
PRIMITIVE_TAGS = set(ord(c) for c in 'BCFDIJSVZ')
OBJECT_TAGS = set(ord(c) for c in 'stglcL')
+#在statics文件的andbug.screed.item("%s = %s" % (k, v))代码中用到vm.String类中的__str__函数,进而调用data(self)发起"call jdwp 0x0A 01"命令
class String(Object):
def __repr__(self):
- return '#' + repr(str(self))
+ return repr(str(self))
def __str__(self):
- return self.data
+ return self.data #在这里调用data函数
+ def genJson(self):
+ return repr(str(self))
+
+
@property
def data(self):
+ '''
+ 1、命令 0x0a 0x01
+ 2、注释:返回一个字符串中包含的字符内容
+ 3、[StringReference Command Set (10)][Value Command (1)]
+ '''
conn = self.conn
buf = conn.buffer()
- self.packTo(buf)
- code, buf = conn.request(0x0A01, buf.data())
- if code != 0:
+ self.packTo(buf) #将oid参数压入jdwp命令的输入参数
+
+ log.debug("study", "buf=" + str(buf))
+ log.debug("study", "call jdwp 0x0A 01")
+ code, buf = conn.request(0x0A01, buf.data(), g_jdwp_request_timeout) #需要输入string对象的object id的值
+ if code != 0:
raise RequestError(code)
+
return buf.unpackStr()
unpack_value_impl = [None,] * 256
+
def register_unpack_value(tag, func):
+ #print "tag=" + tag
+ #print "func=" + str(func)
for t in tag:
- unpack_value_impl[ord(t)] = func
-
-register_unpack_value('B', lambda p, b: b.unpackU8())
-register_unpack_value('C', lambda p, b: chr(b.unpackU8()))
-register_unpack_value('F', lambda p, b: b.unpackFloat()) #TODO: TEST
-register_unpack_value('D', lambda p, b: b.unpackDouble()) #TODO:TEST
-register_unpack_value('I', lambda p, b: b.unpackInt())
-register_unpack_value('J', lambda p, b: b.unpackLong())
-register_unpack_value('S', lambda p, b: b.unpackShort()) #TODO: TEST
-register_unpack_value('V', lambda p, b: b.unpackVoid())
-register_unpack_value('Z', lambda p, b: (True if b.unpackU8() else False))
-register_unpack_value('L', Object.unpackFrom)
+ log.debug("study", "ord(t)=" + str(ord(t)))
+ unpack_value_impl[ord(t)] = func # ord(t)将字符转换成ascii码
+
+register_unpack_value('B', lambda p, b: b.unpackU8()) #BYTE 'B' - a byte value (1 byte).
+register_unpack_value('C', lambda p, b: chr(b.unpackU16())) #CHAR 'C' - a character value (2 bytes).
+register_unpack_value('F', lambda p, b: b.unpackFloat()) #TODO: TEST float型
+register_unpack_value('D', lambda p, b: b.unpackDouble()) #TODO:TEST double型
+register_unpack_value('I', lambda p, b: b.unpackInt())# int 型
+register_unpack_value('J', lambda p, b: b.unpackLong()) #long 型
+register_unpack_value('S', lambda p, b: b.unpackShort()) #TODO: TEST short型
+register_unpack_value('V', lambda p, b: b.unpackVoid()) #不清楚什么类型
+register_unpack_value('Z', lambda p, b: (True if b.unpackU8() else False)) #不清楚什么类型
+register_unpack_value('L', Object.unpackFrom) #对象类型
register_unpack_value('tglc', Object.unpackFrom) #TODO: IMPL
-register_unpack_value('s', String.unpackFrom)
-register_unpack_value('[', Array.unpackFrom)
-
+register_unpack_value('s', String.unpackFrom) #字符串型
+register_unpack_value('[', Array.unpackFrom) #数组型
+
+def get_variable_type(jni_signature):
+ '''
+ 通过jni_signature的值返回,变量的类型
+ '''
+ if jni_signature=="[":
+ return "ARRAY"
+ elif jni_signature=="B":
+ return "BYTE"
+ elif jni_signature=="C":
+ return "CHAR"
+ elif jni_signature=="L":
+ return "OBJECT"
+ elif jni_signature=="F":
+ return "FLOAT"
+ elif jni_signature=="D":
+ return "DOUBLE"
+ elif jni_signature=="I":
+ return "INT"
+ elif jni_signature=="J":
+ return "LONG"
+ elif jni_signature=="Z":
+ return "BOOLEAN"
+
+
+
+#在函数中根据获取的tag的不同,调用不同的函数进行处理,所有处理函数都保存在unpack_value_impl变量中
+#在Array.getSlice函数中,unpack_value函数时传入的tag参数
def unpack_value(sess, buf, tag = None):
if tag is None: tag = buf.unpackU8()
fn = unpack_value_impl[tag]
+ log.debug("study", "in unpack_value tag=" + str(tag) + "\t fn=" + str(fn))
if fn is None:
raise RequestError(tag)
else:
@@ -1154,6 +1853,6 @@ def pack_value(sess, buf, value, tag = None):
def connect(pid, dev=None):
'connects using proto.forward() to the process associated with this context'
- conn = andbug.proto.connect(andbug.proto.forward(pid, dev))
+ conn = andbug.proto.connect(andbug.proto.forward(pid, dev)) #conn是Connection(Thread)类型的一个对象
return andbug.vm.Session(conn)
diff --git a/lib/jdwp/wire.c b/lib/jdwp/wire.c
index b0f3091..dc86ca4 100644
--- a/lib/jdwp/wire.c
+++ b/lib/jdwp/wire.c
@@ -57,6 +57,13 @@ char *jdwp_en_errors[] = {
#define REQUIRE_CAP(n) if (jdwp_expand(buf, (n))) return JDWP_HEAP_FAULT;
#define REQUIRE_LEN(n) if ((buf->len - buf->ofs) < n) return JDWP_NEED_LEN;
+/*
+函数功能:判断jdwp_buffer的buf是否可以满足长度要求,不满足则对buf进行扩展
+jdwp_buffer* buf 当前的buf的指针
+int req 所需要的空间的长度
+返回值:函数调用正常返回0,调用异常返回JDWP_HEAP_FAULT
+满足要求或扩展成功返回0,重新申请空间失败返回JDWP_HEAP_FAULT
+*/
int jdwp_expand( jdwp_buffer* buf, int req ){
int cap = buf->cap;
req += buf->len;
@@ -73,6 +80,13 @@ int jdwp_expand( jdwp_buffer* buf, int req ){
return 0;
}
+/*
+函数功能:在jdwp_buffer存入字符串数据
+参数: jdwp_buffer* buf 原buf结构体变量
+ uint32_t size 计划追加的字符串的长度
+ char* data 要存入的字符串指针
+返回值:返回0
+*/
int jdwp_pack_str( jdwp_buffer* buf, uint32_t size, char* data ){
REQUIRE_CAP(size + 4);
jdwp_pack_u32(buf, size);
@@ -80,37 +94,53 @@ int jdwp_pack_str( jdwp_buffer* buf, uint32_t size, char* data ){
buf->len += size;
return 0;
}
-
+/*
+函数功能:在jdwp_buffer追加一个字节的数据
+参数: jdwp_buffer* buf 原buf结构体变量
+ uint8_t byte 增加的数据内容
+返回值:返回0
+*/
int jdwp_pack_u8( jdwp_buffer* buf, uint8_t byte){
REQUIRE_CAP(1);
*(uint8_t*)(buf->data + buf->len) = byte;
buf->len += 1;
return 0;
}
+
int jdwp_pack_u16( jdwp_buffer* buf, uint16_t word ){
REQUIRE_CAP(2);
- *(uint16_t*)(buf->data + buf->len) = htons(word);
+ *(uint16_t*)(buf->data + buf->len) = htons(word); //改变字节排序
buf->len += 2;
return 0;
}
+
int jdwp_pack_u32( jdwp_buffer* buf, uint32_t quad ){
REQUIRE_CAP(4);
*(uint32_t*)(buf->data + buf->len) = htonl(quad);
buf->len += 4;
return 0;
}
+
int jdwp_pack_u64( jdwp_buffer* buf, uint64_t octet ){
REQUIRE_CAP(8);
*(uint64_t*)(buf->data + buf->len) = htonll(octet);
buf->len += 8;
return 0;
}
+/*
+函数功能:从buf结构体中提取出一个字节的数据
+参数: jdwp_buffer* buf 结构体数据
+ uint8_t* byte 保存提取出来的一个字节数据的指针变量
+返回值:返回0
+*/
int jdwp_unpack_u8( jdwp_buffer* buf, uint8_t* byte){
REQUIRE_LEN(1);
*byte = *(uint8_t*)(buf->data + buf->ofs);
buf->ofs += 1;
return 0;
}
+
+
int jdwp_unpack_u16( jdwp_buffer* buf, uint16_t* word ){
REQUIRE_LEN(2);
*word = ntohs(*(uint16_t*)(buf->data + buf->ofs));
@@ -129,10 +159,11 @@ int jdwp_unpack_u64( jdwp_buffer* buf, uint64_t* octet ){
buf->ofs += 8;
return 0;
}
+
int jdwp_unpack_str( jdwp_buffer* buf, uint32_t *size, char** data ){
uint32_t sz;
int err = jdwp_unpack_u32(buf, &sz);
- if (err) return err;
+ if (err) return err; //没有作用,err的只永远是0,永远不会执行“return err;” 语句
REQUIRE_LEN(sz);
*size = sz;
*data = buf->data + buf->ofs;
@@ -140,6 +171,13 @@ int jdwp_unpack_str( jdwp_buffer* buf, uint32_t *size, char** data ){
return 0;
}
+/*
+函数功能:添加一个id值到buf结构体中
+参数: jdwp_buffer* buf buf变量的指针
+ uint64_t id 要保存的id的值
+ uint8_t sz id数据准备占用buf中内存空间的长度
+返回值:添加成功返回0,否则返回JDWP_SZ_UNSUPPORTE
+*/
int jdwp_pack_id( jdwp_buffer* buf, uint64_t id, uint8_t sz ){
switch (sz){
case 1:
@@ -171,17 +209,36 @@ int jdwp_unpack_id( jdwp_buffer* buf, uint64_t* id, uint8_t sz ){
}
}
-int jdwp_prepare( jdwp_buffer* buf, char* data, int len ){
- if (buf->data == NULL){
+
+/*
+函数功能:初始化一个jdwp buf的结构体
+参数: jdwp_buffer* buf 目标buf结构体
+ char* data 准备加入buf的数据
+ int len data数据的长度
+返回值:调用成功返回0,调用异常返回JDWP_HEAP_FAULT
+*/
+int jdwp_prepare( jdwp_buffer* buf, char* data, int len )
+{
+ if (buf->data == NULL)
+ {
+ //没有申请data空间,则在这里申请
buf->data = malloc(len);
- }else if (len > buf->cap){
+ }
+ else if (len > buf->cap)
+ {
+ //申请了空间,但是buf能容纳的长度,小于当前要加入的数据的长度,则重新申请len长度的空间
buf->data = realloc(buf->data, len);
}; // we do not collapse the heap, ever.. call it a character flaw.
- if (buf->data == NULL) return JDWP_HEAP_FAULT;
- if (data == NULL){
+
+ if (buf->data == NULL) return JDWP_HEAP_FAULT; //如果buf->data为NULL,则空间没有申请成功返回JDWP_HEAP_FAULT
+
+ if (data == NULL)
+ {
buf->len = 0;
// we also don't zeroize memory; also a character flaw.
- }else{
+ }
+ else
+ {
memcpy(buf->data, data, len);
buf->len = len;
}
@@ -190,14 +247,29 @@ int jdwp_prepare( jdwp_buffer* buf, char* data, int len ){
return 0;
}
-void jdwp_purge( jdwp_buffer* buf ){
+/*
+函数功能:净化jdwp_buffer结构体,将data中指向的内容空间释放
+参数:jdwp_buffer* buf 结构指针变量
+返回值:void
+*/
+void jdwp_purge( jdwp_buffer* buf )
+{
if (buf->data == NULL) return;
free(buf->data);
buf->data = NULL;
}
-int jdwp_pack( jdwp_buffer* buf, char format, uint64_t value ){
- switch (format) {
+/*
+函数功能:加入一个pack数据
+参数: jdwp_buffer* buf buf结构体指针
+ char format 所加入数据的格式
+ uint64_t value 要加入的数据的值
+返回值:调用成功返回0,调用失败返回JDWP_OP_UNSUPPORTED
+*/
+int jdwp_pack( jdwp_buffer* buf, char format, uint64_t value )
+{
+ switch (format)
+ {
case '1':
return jdwp_pack_u8(buf, (uint8_t)value);
case '2':
@@ -225,7 +297,11 @@ int jdwp_pack( jdwp_buffer* buf, char format, uint64_t value ){
}
}
-int jdwp_unpack( jdwp_buffer* buf, char format, uint64_t *value ){
+/*
+函数功能:根据格式表述符对数据进行转换
+*/
+int jdwp_unpack( jdwp_buffer* buf, char format, uint64_t *value )
+{
switch (format) {
case '1':
return jdwp_unpack_u8(buf, (uint8_t*)value);
@@ -241,7 +317,7 @@ int jdwp_unpack( jdwp_buffer* buf, char format, uint64_t *value ){
return jdwp_unpack_u64(buf, value);
case 'o':
return jdwp_unpack_id(buf, value, buf->oSz);
- case 't':
+ case 't': //thread id
return jdwp_unpack_id(buf, value, buf->tSz);
case 'f':
return jdwp_unpack_id(buf, value, buf->fSz);
@@ -283,34 +359,53 @@ int jdwp_size( jdwp_buffer* buf, char format ){
}
}
-int jdwp_pack_object_id( jdwp_buffer* buf, uint64_t id ){
+int jdwp_pack_object_id( jdwp_buffer* buf, uint64_t id )
+{
return jdwp_pack_id( buf, id, buf->oSz );
}
-int jdwp_pack_field_id( jdwp_buffer* buf, uint64_t id ){
+
+int jdwp_pack_field_id( jdwp_buffer* buf, uint64_t id )
+{
return jdwp_pack_id( buf, id, buf->fSz );
}
-int jdwp_pack_method_id( jdwp_buffer* buf, uint64_t id ){
+
+int jdwp_pack_method_id( jdwp_buffer* buf, uint64_t id )
+{
return jdwp_pack_id( buf, id, buf->mSz );
}
-int jdwp_pack_type_id( jdwp_buffer* buf, uint64_t id ){
+
+int jdwp_pack_type_id( jdwp_buffer* buf, uint64_t id )
+{
return jdwp_pack_id( buf, id, buf->tSz );
}
-int jdwp_pack_frame_id( jdwp_buffer* buf, uint64_t id ){
+
+int jdwp_pack_frame_id( jdwp_buffer* buf, uint64_t id )
+{
return jdwp_pack_id( buf, id, buf->sSz );
}
-int jdwp_unpack_object_id( jdwp_buffer* buf, uint64_t* id ){
+
+int jdwp_unpack_object_id( jdwp_buffer* buf, uint64_t* id )
+{
return jdwp_unpack_id( buf, id, buf->oSz );
}
-int jdwp_unpack_field_id( jdwp_buffer* buf, uint64_t* id ){
+
+int jdwp_unpack_field_id( jdwp_buffer* buf, uint64_t* id )
+{
return jdwp_unpack_id( buf, id, buf->fSz );
}
-int jdwp_unpack_method_id( jdwp_buffer* buf, uint64_t* id ){
+
+int jdwp_unpack_method_id( jdwp_buffer* buf, uint64_t* id )
+{
return jdwp_unpack_id( buf, id, buf->mSz );
}
-int jdwp_unpack_type_id( jdwp_buffer* buf, uint64_t* id ){
+
+int jdwp_unpack_type_id( jdwp_buffer* buf, uint64_t* id )
+{
return jdwp_unpack_id( buf, id, buf->tSz );
}
-int jdwp_unpack_frame_id( jdwp_buffer* buf, uint64_t* id ){
+
+int jdwp_unpack_frame_id( jdwp_buffer* buf, uint64_t* id )
+{
return jdwp_unpack_id( buf, id, buf->sSz );
}
@@ -383,7 +478,8 @@ int jdwp_packfv( jdwp_buffer* buf, char* format, va_list ap ){
}
/** unpacks fields from the current buf, reading field by field, and returning when complete */
-int jdwp_unpackf( jdwp_buffer* buf, char* format, ... ){
+int jdwp_unpackf( jdwp_buffer* buf, char* format, ... )
+{
va_list ap;
va_start(ap, format);
int err = jdwp_unpackfv(buf, format, ap);
@@ -392,7 +488,8 @@ int jdwp_unpackf( jdwp_buffer* buf, char* format, ... ){
}
/** unpacks fields from the current buf, reading field by field, and returning when complete */
-int jdwp_unpackfv( jdwp_buffer* buf, char* format, va_list ap ){
+int jdwp_unpackfv( jdwp_buffer* buf, char* format, va_list ap )
+{
uint8_t *v8;
uint16_t *v16;
uint32_t *v32;
diff --git a/lib/jdwp/wire.h b/lib/jdwp/wire.h
index ecac134..71cf050 100644
--- a/lib/jdwp/wire.h
+++ b/lib/jdwp/wire.h
@@ -45,12 +45,18 @@
extern char* jdwp_en_errors[];
/** represents an active JDWP buffer and associated state */
+/*
+cap ɵbufij
+len ǰʹõij
+ofs Ѿȡֽڵij
+data ݵָ
+*/
typedef struct {
- uint8_t fSz;
- uint8_t mSz;
- uint8_t oSz;
- uint8_t tSz;
- uint8_t sSz;
+ uint8_t fSz; //field ID size
+ uint8_t mSz; //method ID size
+ uint8_t oSz; //object ID size
+ uint8_t tSz; //reference Type ID size
+ uint8_t sSz; //frame ID size
int ofs, len, cap;
char* data;
} jdwp_buffer;
diff --git a/setup.py b/setup.py
index 44b31ff..bcc37e5 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
## Copyright 2011, IOActive, Inc. All rights reserved.
##
## AndBug is free software: you can redistribute it and/or modify it under
@@ -25,19 +27,24 @@ class TestCommand(Command):
user_options = []
def initialize_options(self):
- self._dir = os.getcwd()
+ self._dir = os.getcwd() #用来获得程序的当前环境
def finalize_options(self):
pass
def run(self):
testfiles = [ ]
+ #glob 获取指定目录的所有.py文件
+ #splitext 函数用于分解文件名的扩展名
+ #basename() 去掉目录路径, 返回文件名
+ #b='.'.join('hello','world') 则b为“hello.world”
for t in glob(pjoin(self._dir, 'tests', '*.py')):
if not t.endswith('__init__.py'):
testfiles.append('.'.join(
['tests', splitext(basename(t))[0]])
)
+ #使用测试框架进行测试的工作
tests = TestLoader().loadTestsFromNames(testfiles)
t = TextTestRunner(verbosity = 1)
t.run(tests)
@@ -56,6 +63,7 @@ def initialize_options(self):
def finalize_options(self):
pass
+ #对“.pyc”文件进行删除
def run(self):
for clean_me in self._clean_me:
try: