亞寵展、全球寵物產業風向標——亞洲寵物展覽會深度解析
987
2022-05-27
0 引言
在一個用Java語言開發的大型軟件系統中,一旦發生軟件的錯誤,往往會導致整個系統產生故障.只有
找到故障發生的真正原因,才能從根本上排除故障.定位大型系統中的故障來源,需要耗費較多的時間和人
力.如果能為系統建立起一個軟件錯誤傳遞圖,輔助分析和定位故障的來源,就能幫助操作員、系統分析員快
速定位故障的來源,找到解決故障的辦法[1-2].
Java作為一門面向對象的語言,其程序由類、類成員、方法和方法的參數等構成.類方法之間存在互相依
賴、互相調用的關系.建立起一個類方法關系圖,并將其擴展成為軟件的錯誤傳遞圖,如果故障發生在軟件的
某個模塊或類方法中,在傳遞圖上就會顯示故障的位置,結合一定的算法,就能找出故障產生的來源.建立類
方法的關系圖,還需要分析出類方法的名稱、包含的參數、返回類型及方法之間的關系.
1 逆向分析Java文件的現狀與實踐
在有源代碼的情況下,要分析出類方法之間的關系,前提就是去閱讀和理解源代碼.現實中往往因商業
機密得不到源代碼,比如只有Java類文件(.class的擴展名).這類文件經編譯器編譯過,用戶看不到源代碼,
只能看到字節碼(byte code),即二進制代碼,難以理解,無從下手,必須另尋途徑.
有一種辦法是通過逆向分析Java文件,得到類結構和信息,人們在這方面做過不少有益的嘗試.比如說
第36卷 第5期廣東第二師范學院學報Vol.36 No.5
2016年10月Journal of Guangdong University of Education Oct.2016
有一種方法是在一個類中,通過識別類方法的行號,來找出該類及其方法[3].這種方法需要逐行讀取程序.
還有一種是利用Javassist(Java Programming Assistant)工具來打開Java類文件,得到類包含的方法、
構造函數等.Javassist是一個Java字節代碼類庫,允許編輯Java字節代碼,使Java程序在運行時能定義一
個新類,當JVM(Java虛擬機)載入類文件時可以修改這個類文件.它提供了兩種類型的API供調用,一種是
源代碼API,另外一種是字節碼API.利用源代碼API,無需懂得Java字節碼,就可編輯一個類文件,且源代
碼API也是用Java語言來設計的,支持在源代碼窗口中插入字節碼,再經Javaassit在運行時編譯.字節碼
API則允許用戶像使用其他編輯器一樣,來編輯一個類文件[4].這種方法通過打開類文件,從字節碼得出類
方法,還可以編輯這個文件.
類方法的定義語句和調用語句格式不同,類方法還存在有重載現象,需要區分出名字相同,但參數不同
的方法.Java Reflection是Java自帶的類,允許使用一個類或對象,去分析另一個Java源程序或者.class文
件,從而得到該程序或文件所包含的類名稱、方法和方法的參數,最終可得到整個類結構.Java Reflection提
供了許多詳細的工具集來操作Java代碼,能提供Visual Basic用戶熟悉的那些工具.新的類可以在設計和運
行時被加入,使用快速開發工具還可以動態查詢新增加類的容量[5].Java Reflection的API(Application
圖1 實現思路
Program Interface)存在于Java.lang.reflect類內.譬
如,調用java.lang.reflect.Field,java.lang.reflect.
Method,java.lang.reflect.Constructor[6],就能得到類
成員、方法和構造函數.本文探討了如何使用Java
Reflection設計逆向解析Java源程序或者.class文件
的解析器,實現思路可分為以下三方面,如圖1所示:
1)使用Java Reflection創建一個解析器程序,
具備識別和分析Java源程序或者類(class)文件的功
能,能搜索到類的構造函數、類方法名稱和定義、類方法的返回類型和作用范圍.
2)導入一個Java樣例程序文件,分析文件的類結構.
3)對搜索到的類及類方法信息,予以整理,并以特定格式的動態數組保存,為分析類方法之間的關系和
生成類關系圖奠定基礎.
2 用Java Reflection逆向分析Java程序文件的具體步驟
Java Reflection存在于Java.lang.reflect類中,導入了這個類后,才能調用其中的代碼,步驟如圖2所示:
圖2 逆向分析Java程序文件的具體步驟
2.1 獲取類名稱
分析一個Java文件(以.java或者.class為擴展名),先
要分析該文件包含了哪些類.通過調用方法“Class.
forName”可以得到.java或者.class文件中包含的類名
稱[7],如以下代碼所示:
/* CVSClass.java*/
System.out.println(" 尋找類" +className);
this.className=className;
2.2 獲取類方法
當找出Java文件中的類之后,調用getMethod()可以
2016年第5期 薛 亮,等:基于Java Reflection自動逆向生成類間方法關系圖的解析器 · 9 3 ·
得到類中全部的公有(public)方法,以及繼承(inherited)方法.如果調用getDeclaredMethods(),可以得到類
中已聲明的公有和非公有方法,但不能得到從superclasses繼承來的方法.代碼中methodSet使用了
HashSet(哈希集合),methodList存儲類型使用了ArrayList(數組類型)來存儲類方法信息,目的是為了對
比兩種不同存儲類型的存取速度,在實際應用中只要選擇一個即可.以下是如何得到類中已聲明方法的部分
代碼:
/* CVSClass.java*/
Method methods=cls.getDeclaredMethods();//得到所有已聲明的方法
cvsMethodArray=new CVSMethod[methods.length];//初始化數組
/* 找到的類方法名存放在methodSet和methodList中*/
if(methods!=null){for(int i=0;i< methods.length;i++){
cvsMethodArray[i]=new CVSMethod(methods[i],className);
methodSet.add(new CVSMethod(methods[i],className));
methodList.add(new CVSMethod(methods[i],className));}
2.3 獲取構造函數
為得到類中的構造函數,調用getDeclaredConstructors()方法可以得到構造函數的名字.面向對象編程
中,構造函數有時可以被重載(Overloading),因此一個類可以包含不止一個構造函數.如以下代碼所示:
/* CVSClass.java*/
//創建數組來存儲構造函數的名字
Constructor[]constructors=cls.getDeclaredConstructors();
cvsConstructorArray=new CVSConstructor[constructors.length];
if(constructors!=null){for(int i=0;i<constructors.length;i++){
/*給constructorSet和cvsConstructorArray數組添加搜尋到的構造函數名稱*/
constructorSet.add(new CVSConstructor(constructors[i]));
cvsConstructorArray[i]=new CVSConstructor(constructors[i]);}
2.4 獲取類方法的返回類型
在Java中除了構造函數之外,每個類方法均有一個返回類型.返回類型可以是空值、整數型、字符串型、
雙精度型等.調用method.getReturnType().getName()可以得到類方法返回類型的名稱,如以下所示:
/* CVSMethod.java*/
this.refMethod=refMethod;
this.cvsMethodName=refMethod.getName();//得到類名稱
this.cvsMethodReturnType=refMethod.getReturnType().getName();//得到返回類型
this.className=className;
2.5 獲取類方法的參數
一個類方法通常帶有一個或多個參數的聲明.面向對象編程中,類方法允許被重載,即多個類方法可以
有相同名字,但返回參數可以不一樣.逆向分析時,需要知道類方法的名字、參數名稱和數量,才能區別出哪
些是重載的方法.讀取類方法的參數部分代碼如下:
/* CVSMethod.java*/
public CVSMethod(Method refMethod,String className){
/* 創建methodParameterClasses類數據來存放參數的類型*/
· 94· 廣東第二師范學院學報第36卷
Class[]methodParameterClasses=refMethod.getParameterTypes();
/* 把類方法的參數存儲進數組*/
for(int i=0;i<methodParameterClasses.length;i++){
cvsMethodParameterSet.add (CVSUtil.toShortName(methodParameterClasses[i].getName()));
methodParameters.append(CVSUtil.toShortName(methodParameterClasses[i].getName()));}
}
/* MethInfo.java
/* MethodInfo類用于存取類方法的名稱、類型、起止行信息*/
this.shortMethodName=CVSUtil.getShortMethodInfoName(methodName);/* 類方法名字*/
this.methodNameArgu=CVSUtil.getMethodNameArgu(methodName);/*類方法參數聲明*/
2.6 在類方法內讀取括號類型、所在行號,識別方法的定義行和作用域
Java類方法的定義語句,由一對小括號“(”和“)”表示開始和結束.用一對花括號”{”和“}”表示一個類或
方法的作用域開始和結束.通過識別這些成對括號的開始和結束的行號,就能知道哪些語句屬于類方法的定
義語句,哪些語句屬于作用域語句,以及哪些語句才是類方法的內容.Bracket類定義了存儲括號類型和行
號,CVSUtil類識別類方法定義和作用域,部分代碼如下所示:
/* Bracket.java*/
this.bracketType=bracketType;//存放括號類型
this.lineNumber=lineNumber;//存放行號}
public String getBracketType(){return bracketType;}//返回括號類型
public int getLineNumber(){return lineNumber;}//返回行號
/*CVSUtil.java*/
/*判斷是否含類方法定義和作用域語句*/
{//探測讀入的語句是否存在“(”,“)”,“{”,“}”,以及表示方法語句結束的符號“;”
if((containMethodLines.indexOf(cvsMethod.toPartString())!= -1)
&& (containMethodLines.indexOf(" (" )!= -1)&& (containMethodLines.indexOf(" )" )!= -1)
&& ((containMethodLines.indexOf(" {" )!= -1)||(containMethodLines.indexOf(" ;" )!= -1)))......}
2.7 尋找注釋語句
注釋語句在程序運行時不會被編譯,Java語言用兩種注釋符號,一種是以“//”開始,另一種是“/*”和
“*/”來表示注釋的部分.在逆向分析時,必須識別出這些注釋語句,并將其忽略掉.在Bracket.java代碼中,
isCommentBracket方法使用字符串函數IndexOf來搜索注釋符號“//”,如下:
readLineString.indexOf("//" );//在字符串中尋找”//”符號
2.8 搜索類方法之間的關系
在類中的一個方法內,往往需要調用其他的方法.類方法之間存在著相互調用和依賴的關系.在搜索一
個類方法內的語句時,解析器并不知道具體在哪行代碼調用其他方法,所以需要讀取類方法內的每一行代
碼,保證調用其他方法的語句不會被遺漏.在SechDep.java中,readline方法用于讀取行并找出方法之間的
關系.MethArr數組儲存了類中所有聲明的方法.代碼請參照SechDep.Java文件.
2.9 保存搜索到的類方法信息到數組
當類方法的名稱、返回類型、參數、參數的數量以及類方法之間的關系被識別出來后,這些信息就可以儲
2016年第5期 薛 亮,等:基于Java Reflection自動逆向生成類間方法關系圖的解析器 · 9 5 ·
存在一定格式的數組中.在SechDep.java的readline方法中,定義了一個名為finalResult的數組存放這些信
息.代碼請參照SechDep.Java文件.
3 導入樣例文件進行測試
導入一個樣例文件SafeExp.java,然后運行逆向分析解析器程序,搜索到類方法信息并存儲到數組中.
類方法數組存儲的信息如表1所示.
表1 搜索到SafeExp.java中所包含類方法的信息的存儲數組
類名方法名方法返回類型類成員類型是否構造函數參數數量
SafeExp SafeExp void public 是0
SafeExp fun_A void private 否0
SafeExp fun_B void public 否0
SafeExp fun_C void public 否0
SafeExp fun_D void public 否0
SafeExp fun_E void public 否0
SafeExp Main void public static 否1
在存儲完類方法數組后,將存儲類方法之間相互調用的關系,用另一個名為“finalResult”的數組來存
放,存儲信息如表2所示.
表2 SafeExp.java中類方法之間的關系存儲在finalResult數組
方法名稱調用的方法方法之間的關系
main SafeExp main→SafeExp
SafeExp fun_A,fun_E SafeExp→fun_A,SafeExp→fun_E
Fun_A fun_B,fun_C,fun_D fun_A→fun_B,fun_A→fun_B,fun_A→fun_C
Fun_B 無無
Fun_C fun_D fun_C→fun_D
Fun_D 無無
Fun_E fun_B,fun_D fun_E→fun_B,fun_E→fun_D
利用類方法和方法之間關系的數組,使用Java圖形編程,可生成類方法關系圖,如圖3中由節點和有向
邊構成的圖形.
4 結語
對于Java源程序或者.class文件,使用Java Reflection設計逆向分析解析器程序,獲得類的名稱、方法
名、方法參數、構造函數等信息,并搜索出每個類方法與其他方法之間的關系.然后,將這些信息用特定格式
的數組存儲起來.該數組存儲的信息就可作為生成類方法關系圖所用.
類方法關系圖可進一步擴展成為軟件錯誤傳遞圖,供操作員或系統分析員監測軟件運行的狀況,幫助
· 96· 廣東第二師范學院學報第36卷
圖3 分析SafeExp.java后生成的類
方法關系圖
他們在故障發生時,分析和確定故障產生的來源,及時
采取相應措施.目前,對于大型的軟件系統,逆向分析類
方法之間的關系還存在一定的困難,需要更多地研究、
實踐和改進解析器程序.比如,當系統的文件內部結構
(邏輯結構)非常復雜,存在各種不同類型的類(子類、抽
象類、內部類等等)時,類方法之間互相調用的關系更加
錯綜復雜,分析工作更為艱巨.
文中使用Java Reflection進行逆向分析的解析器
源代碼,限于篇幅無法詳盡列出,完整的代碼可通過訪
問網盤鏈接“http://pan.baidu.com/s/1c1H7Ksg”下
載.源代碼在JDK1.6和NetBeans中調試通過.
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。