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: