1. 程式人生 > 實用技巧 >java反射技術,逆向開發必備技能

java反射技術,逆向開發必備技能

相信很多人都知道反射可以說是Java中最強大的技術了,它可以做的事情太多太多,很多優秀的開源框架都是通過反射完成的,比如最初的很多註解框架,後來因為java反射影響效能,所以被執行時註解APT替代了,java反射有個開源框架jOOR相信很多人都用過,不過我們還是要學習反射的基礎語法,這樣才能自己寫出優秀的框架,當然這裡所講的反射技術,是學習Android外掛化技術、Hook技術等必不可少的!

一、基本反射技術

1.1 根據一個字串得到一個類

getClass方法

  1. Stringname="Huanglinqing";
  2. Classc1=name.getClass();
  3. System.out.println(c1.getName());

列印結果如下:

Class.forName

比如我們獲取java.lang.String的類名

  1. Stringname="java.lang.String";
  2. Classc1=null;
  3. try{
  4. c1=Class.forName(name);
  5. System.out.println(c1.getName());
  6. }catch(ClassNotFoundExceptione){
  7. }

這裡也通過捕獲異常,因為我們傳的這個字串可能不合法,字串合法命名是類的名稱空間和類的名稱組成

列印結果如下:


我們還可以通過c1.getSuperclass()獲取到他的父類

Type屬性

基本型別都有type屬性,可以得到這個基本型別的型別,比如:

  1. Classc1=Boolean.TYPE;
  2. Classc2=Byte.TYPE;
  3. Classc3=Float.TYPE;
  4. Classc4=Double.TYPE;
 停停停!這些東西有啥子用,如此雞肋! 別急,一切都是為後續做準備。

二、獲取類的成員

當類中方法定義為私有的時候我們能呼叫?不能!當變數是私有的時候我們能獲取嗎?不能!但是反射可以,比如原始碼中有你需要用到的方法,但是那個方法是私有的,這個時候你就可以通過反射去執行這個私有方法,並且獲取私有變數。

獲取類的建構函式

為了便於測試,我們定義一個Test類,Test類如下:(省略get和set方法)

Test類中我們定義是三個私有變數,生成兩個公有的含參構造方法和一個私有的含參構造方法以及一個公有的無參構造方法。

  1. publicclassTest{
  2. privateintage;
  3. privateStringname;
  4. privateinttestint;
  5. publicTest(intage){
  6. this.age=age;
  7. }
  8. publicTest(intage,Stringname){
  9. this.age=age;
  10. this.name=name;
  11. }
  12. privateTest(Stringname){
  13. this.name=name;
  14. }
  15. publicTest(){
  16. }

下面我們通過反射獲取這些構造方法

獲取類的所有構造方法

  1. Testtest=newTest();
  2. Classc4=test.getClass();
  3. Constructor[]constructors;
  4. constructors=c4.getDeclaredConstructors();

通過getDeclaredConstructors可以返回類的所有構造方法,返回的是一個數組因為構造方法可能不止一個,通過getModifiers可以得到構造方法的型別,getParameterTypes可以得到構造方法的所有引數,返回的是一個Class陣列,所以我們如果想獲取所有構造方法以及每個構造方法的引數型別,可以有如下程式碼:

  1. for(inti=0;i<constructors.length;i++){
  2. System.out.print(Modifier.toString(constructors[i].getModifiers())+"引數:");
  3. Class[]parametertypes=constructors[i].getParameterTypes();
  4. for(intj=0;j<parametertypes.length;j++){
  5. System.out.print(parametertypes[j].getName()+"");
  6. }
  7. System.out.println("");
  8. }

執行結果如下所示:

這樣我們就得到了類中所有構造方法和構造方法中的引數,那麼我們如何獲取特定的構造方法呢?

獲取類中特定的構造方法

我們可以通過getConstructors方法獲取類中 所有的public型別的構造方法,程式碼和上面一樣就不演示了。

我們可以通過getDeclaredConstructor()方法傳參獲取特定引數型別的構造方法,這裡注意是getDeclaredConstructor()不是 getDeclaredConstructors() ,所以返回的是一個Class物件而不是一個Class陣列。

獲取無參構造方法直接不傳引數,如下所示:

  1. try{
  2. constructors=c4.getDeclaredConstructor();
  3. System.out.print(Modifier.toString(constructors.getModifiers())+);
  4. }catch(NoSuchMethodExceptione){
  5. e.printStackTrace();
  6. }

這裡要進行異常捕獲,因為可能不存在對應的構造方法,列印結果如下:


如果我們想獲取有兩個引數分別為int和String型別的構造方法,程式碼如下:

  1. Class[]p={int.class,String.class};
  2. try{
  3. constructors=c4.getDeclaredConstructor(p);
  4. System.out.print(Modifier.toString(constructors.getModifiers())+"引數:");
  5. Class[]parametertypes=constructors.getParameterTypes();
  6. for(intj=0;j<parametertypes.length;j++){
  7. System.out.print(parametertypes[j].getName()+"");
  8. }
  9. }catch(NoSuchMethodExceptione){
  10. e.printStackTrace();
  11. }

這裡我們同樣打印出構造方法的引數:


呼叫構造方法

從這裡開始慢慢到了關鍵的一步,得到類的例項,我們主要藉助於newInstance方法,為了方便演示我們將測試類的兩個構造方法打印出來.

  1. publicTest(intage,Stringname){
  2. this.age=age;
  3. this.name=name;
  4. System.out.println("hello"+name+"iam"+age);
  5. }
  6. privateTest(Stringname){
  7. this.name=name;
  8. System.out.println("MyNameis"+
  9. name);
  10. }

我們先來呼叫public的方法,如下所示:

  1. Class[]p={int.class,String.class};
  2. constructors=c4.getDeclaredConstructor(p);
  3. constructors.newInstance(24,"HuangLinqing");

執行列印結果如下:


那麼呼叫私有構造方法呢,和上面一樣,只是我們要設定constructors.setAccessible(true);程式碼如下:

  1. Class[]p={String.class};
  2. constructors=c4.getDeclaredConstructor(p);
  3. constructors.setAccessible(true);
  4. constructors.newInstance("HuangLinqing");

列印結果如下:


呼叫類的私有方法

如何呼叫類中的私有方法呢,我們先在測試類中編寫一個測試的私有方法 如下:

  1. privatevoidwelcome(Stringtips){
  2. System.out.println(tips);
  3. }

我們知道如果我們要正常的呼叫類的方法都是通過類.方法呼叫,所以我們呼叫私有方法也需要得到類的例項,而我們上面newInstace已經得到了類的例項,這樣就好辦了。

  1. Class[]p4={String.class};
  2. Methodmethod=c4.getDeclaredMethod("welcome",p4);
  3. method.setAccessible(true);

我們首先通過getDeclaredMethod方法獲取到這個私有方法,第一個引數是方法名,第二個引數是引數型別

然後通過invoke方法執行,invoke需要兩個引數一個是類的例項,一個是方法引數。

  1. Class[]p4={String.class};
  2. Methodmethod=c4.getDeclaredMethod("welcome",p4);
  3. method.setAccessible(true);
  4. Objectarg1s[]={"歡迎關注程式碼男人技術公眾號"};
  5. method.invoke(test,arg1s);

test類的例項當不能new 獲取的時候我們也可以通過反射獲取,就是上面的newInstance方法。列印結果如下:


獲取類的私有欄位並修改值

看到這裡你可能會說,有了set方法,什麼私有不私有,test.set不就可以了,但是這裡要注意我們是沒有辦法得到這個類的例項的,要不然都可以得到例項就沒有反射一說了。我們在通過反射得到類的例項之後先獲取欄位:

  1. Fieldfield=c4.getDeclaredField("name");
  2. field.setAccessible(true);
  3. field.set(o,"程式碼男人");

o是我們上面通過反射構造方法獲取的例項,列印field.get(o).toString()的值如下:

不過要注意的是我們修改了name的值只對當前的例項物件有效。

Java的基本反射語法就是這樣了,歡迎加入技術群一起探討!

最後反射封裝類如下:

  1. package jnidemo.hlq.com.hookdemo;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. /**
  6. * @author Huanglinqing
  7. * @date 2019/4/28
  8. */
  9. public class Reflex {
  10. /**
  11. * 獲取無參建構函式
  12. * @param className
  13. * @return
  14. */
  15. public static Object createObject(String className) {
  16. Class[] pareTyples = new Class[]{};
  17. Object[] pareVaules = new Object[]{};
  18. try {
  19. Class r = Class.forName(className);
  20. return createObject(r, pareTyples, pareVaules);
  21. } catch (ClassNotFoundException e) {
  22. e.printStackTrace();
  23. }
  24. return null;
  25. }
  26. /**
  27. * 獲取無參構造方法
  28. * @param clazz
  29. * @return
  30. */
  31. public static Object createObject(Class clazz) {
  32. Class[] pareTyple = new Class[]{};
  33. Object[] pareVaules = new Object[]{};
  34. return createObject(clazz, pareTyple, pareVaules);
  35. }
  36. /**
  37. * 獲取一個引數的建構函式 已知className
  38. *
  39. * @param className
  40. * @param pareTyple
  41. * @param pareVaule
  42. * @return
  43. */
  44. public static Object createObject(String className, Class pareTyple, Object pareVaule) {
  45. Class[] pareTyples = new Class[]{pareTyple};
  46. Object[] pareVaules = new Object[]{pareVaule};
  47. try {
  48. Class r = Class.forName(className);
  49. return createObject(r, pareTyples, pareVaules);
  50. } catch (ClassNotFoundException e) {
  51. e.printStackTrace();
  52. }
  53. return null;
  54. }
  55. /**
  56. * 獲取單個引數的構造方法 已知類
  57. *
  58. * @param clazz
  59. * @param pareTyple
  60. * @param pareVaule
  61. * @return
  62. */
  63. public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) {
  64. Class[] pareTyples = new Class[]{pareTyple};
  65. Object[] pareVaules = new Object[]{pareVaule};
  66. return createObject(clazz, pareTyples, pareVaules);
  67. }
  68. /**
  69. * 獲取多個引數的構造方法 已知className
  70. * @param className
  71. * @param pareTyples
  72. * @param pareVaules
  73. * @return
  74. */
  75. public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) {
  76. try {
  77. Class r = Class.forName(className);
  78. return createObject(r, pareTyples, pareVaules);
  79. } catch (ClassNotFoundException e) {
  80. e.printStackTrace();
  81. }
  82. return null;
  83. }
  84. /**
  85. * 獲取構造方法
  86. *
  87. * @param clazz
  88. * @param pareTyples
  89. * @param pareVaules
  90. * @return
  91. */
  92. public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) {
  93. try {
  94. Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
  95. ctor.setAccessible(true);
  96. return ctor.newInstance(pareVaules);
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. }
  100. return null;
  101. }
  102. /**
  103. * 獲取多個引數的方法
  104. * @param obj
  105. * @param methodName
  106. * @param pareTyples
  107. * @param pareVaules
  108. * @return
  109. */
  110. public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) {
  111. if (obj == null) {
  112. return null;
  113. }
  114. try {
  115. //呼叫一個private方法 //在指定類中獲取指定的方法
  116. Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
  117. method.setAccessible(true);
  118. return method.invoke(obj, pareVaules);
  119. } catch (Exception e) {
  120. e.printStackTrace();
  121. }
  122. return null;
  123. }
  124. /**
  125. * 獲取一個引數的方法
  126. * @param obj
  127. * @param methodName
  128. * @param pareTyple
  129. * @param pareVaule
  130. * @return
  131. */
  132. public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) {
  133. Class[] pareTyples = {pareTyple};
  134. Object[] pareVaules = {pareVaule};
  135. return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
  136. }
  137. /**
  138. * 獲取無參方法
  139. * @param obj
  140. * @param methodName
  141. * @return
  142. */
  143. public static Object invokeInstanceMethod(Object obj, String methodName) {
  144. Class[] pareTyples = new Class[]{};
  145. Object[] pareVaules = new Object[]{};
  146. return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
  147. }
  148. /**
  149. * 無參靜態方法
  150. * @param className
  151. * @param method_name
  152. * @return
  153. */
  154. public static Object invokeStaticMethod(String className, String method_name) {
  155. Class[] pareTyples = new Class[]{};
  156. Object[] pareVaules = new Object[]{};
  157. return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
  158. }
  159. /**
  160. * 獲取一個引數的靜態方法
  161. * @param className
  162. * @param method_name
  163. * @param pareTyple
  164. * @param pareVaule
  165. * @return
  166. */
  167. public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) {
  168. Class[] pareTyples = new Class[]{pareTyple};
  169. Object[] pareVaules = new Object[]{pareVaule};
  170. return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
  171. }
  172. /**
  173. * 獲取多個引數的靜態方法
  174. * @param className
  175. * @param method_name
  176. * @param pareTyples
  177. * @param pareVaules
  178. * @return
  179. */
  180. public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) {
  181. try {
  182. Class obj_class = Class.forName(className);
  183. return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);
  184. } catch (Exception e) {
  185. e.printStackTrace();
  186. }
  187. return null;
  188. }
  189. /**
  190. * 無參靜態方法
  191. * @param method_name
  192. * @return
  193. */
  194. public static Object invokeStaticMethod(Class clazz, String method_name) {
  195. Class[] pareTyples = new Class[]{};
  196. Object[] pareVaules = new Object[]{};
  197. return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);
  198. }
  199. /**
  200. * 一個引數靜態方法
  201. * @param clazz
  202. * @param method_name
  203. * @param classType
  204. * @param pareVaule
  205. * @return
  206. */
  207. public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) {
  208. Class[] classTypes = new Class[]{classType};
  209. Object[] pareVaules = new Object[]{pareVaule};
  210. return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);
  211. }
  212. /**
  213. * 多個引數的靜態方法
  214. * @param clazz
  215. * @param method_name
  216. * @param pareTyples
  217. * @param pareVaules
  218. * @return
  219. */
  220. public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules) {
  221. try {
  222. Method method = clazz.getDeclaredMethod(method_name, pareTyples);
  223. method.setAccessible(true);
  224. return method.invoke(null, pareVaules);
  225. } catch (Exception e) {
  226. e.printStackTrace();
  227. }
  228. return null;
  229. }
  230. public static Object getFieldObject(String className, Object obj, String filedName) {
  231. try {
  232. Class obj_class = Class.forName(className);
  233. return getFieldObject(obj_class, obj, filedName);
  234. } catch (ClassNotFoundException e) {
  235. e.printStackTrace();
  236. }
  237. return null;
  238. }
  239. public static Object getFieldObject(Class clazz, Object obj, String filedName) {
  240. try {
  241. Field field = clazz.getDeclaredField(filedName);
  242. field.setAccessible(true);
  243. return field.get(obj);
  244. } catch (Exception e) {
  245. e.printStackTrace();
  246. }
  247. return null;
  248. }
  249. public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule) {
  250. try {
  251. Field field = clazz.getDeclaredField(filedName);
  252. field.setAccessible(true);
  253. field.set(obj, filedVaule);
  254. } catch (Exception e) {
  255. e.printStackTrace();
  256. }
  257. }
  258. public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule) {
  259. try {
  260. Class obj_class = Class.forName(className);
  261. setFieldObject(obj_class, obj, filedName, filedVaule);
  262. } catch (ClassNotFoundException e) {
  263. e.printStackTrace();
  264. }
  265. }
  266. public static Object getStaticFieldObject(String className, String filedName) {
  267. return getFieldObject(className, null, filedName);
  268. }
  269. public static Object getStaticFieldObject(Class clazz, String filedName) {
  270. return getFieldObject(clazz, null, filedName);
  271. }
  272. public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) {
  273. setFieldObject(classname, null, filedName, filedVaule);
  274. }
  275. public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) {
  276. setFieldObject(clazz, null, filedName, filedVaule);
  277. }
  278. }