图文精华

分享

第一篇java的向上向下转型对应的smali分析。

本帖最后由 八十万禁军教头 于 2021-7-26 23:39 编辑
  1. package cc.cspl;

  2. import java.util.ArrayList;
  3. import java.util.List;

  4. interface Pay{
  5.     void callBack();
  6. }

  7. class AliPay implements Pay{
  8.     public void doPay(){
  9.         System.out.println("支付包支付");
  10.     }

  11.     @Override
  12.     public void callBack() {
  13.         System.out.println("支付包回调验签");
  14.     }
  15. }

  16. class WxPay implements Pay{
  17.     public void doPay(){
  18.         System.out.println("微信支付");
  19.     }
  20.     @Override
  21.     public void callBack() {
  22.         System.out.println("微信回调验签");
  23.     }
  24. }

  25. class ChinaMobilePay implements Pay{
  26.     public void doPay(){
  27.         System.out.println("和包支付");
  28.     }
  29.     @Override
  30.     public void callBack() {
  31.         System.out.println("移动和包回调验签");
  32.     }
  33. }

  34. public class MyPay {
  35.     private List<Pay> payList = new ArrayList<Pay>();
  36.     //添加支付选择
  37.     public void add(Pay pay){
  38.         payList.add(pay);
  39.     }
  40.     //得到支付集合数量
  41.     public Integer size(){
  42.         return payList.size();
  43.     }

  44.     public Pay getPayItem(int index){
  45.         return payList.get(index);
  46.     }
  47.     //向下转型,整理得到,不同的类实现同一个接口,放到一个集合内,去掉不同类的区别,保留共同的特性,形成一个标准
  48.     //向上转型的使用范围举例:Thrown
  49.     //向下转型的使用范围:

  50.     public static void main(String[] args) {
  51.         int Alipay = 1;
  52.         int Wxpay = 2;
  53.         int ChinaMobilepay = 3;
  54.         MyPay myPay = new MyPay();
  55.         myPay.add(new AliPay());//支付宝向上转型为Pay类型,丢失子类方法
  56.         myPay.add(new WxPay());//微信向上转型为Pay类型,丢失子类方法
  57.         myPay.add(new ChinaMobilePay());//和包向上转型为Pay类型,丢失子类方法
  58.         int type = 3;
  59.         switch(type){
  60.             case 1:
  61.                 AliPay aliPay = (AliPay) myPay.getPayItem(type-1);//向下转型,得到子类的方法
  62.                 aliPay.doPay();
  63.                 aliPay.callBack();
  64.                 break;
  65.             case 2:
  66.                 WxPay wxPay = (WxPay) myPay.getPayItem(type-1);//向下转型
  67.                 wxPay.doPay();
  68.                 wxPay.callBack();
  69.                 break;
  70.             case 3:
  71.                 ChinaMobilePay chinaMobilePay = (ChinaMobilePay) myPay.getPayItem(type-1);//向下转型
  72.                 chinaMobilePay.doPay();
  73.                 chinaMobilePay.callBack();
  74.                 break;
  75.         }
  76.     }
  77. }
复制代码
以上是Java代码,下面是smali代码分析,晚上继续写
  1. # .class <访问权限> [修饰关键字] <类名>
  2. # 类定义
  3. .class public Lcc/cspl/MyPay;
  4. # .super <父类名>
  5. # 父类
  6. .super Ljava/lang/Object;
  7. # .source <源文件名>
  8. # 源文件名MyPay.java
  9. .source "MyPay.java"
  10. #以上三行,告诉我们文件名也就是类名在cc.cspl下的MyPay类,父类是Object

  11. # instance fields 类的入口
  12. # .field <访问权限> [static] [修饰关键字] <字段名>:<字段类型>
  13. # 私有字段payList,类型是List
  14. .field private payList:Ljava/util/List;
  15.     # .annotation [注解属性] <注解类名>;
  16.     # 注解 调用系统的 dalvik注解签名
  17.     .annotation system Ldalvik/annotation/Signature;
  18.          # value 得到 List<pay>
  19.         value = {
  20.             "Ljava/util/List",
  21.             "<",# 注意 < 也占一位
  22.             "Lcc/cspl/Pay;",
  23.             ">;"# 注意 > 也占一位
  24.         }
  25.     .end annotation
  26. # 根据 field 和 value 得到 private List<Pay> payList
  27. .end field


  28. # direct methods 直接方法,一般在上面,不能被覆写的方法 [private|static|final]以上三种访问权限不可以被修改
  29. # 系统自动添加的构造方法属性是public,返回void。统一放入<init>方法中,<init>方法java在字节码中自动生成
  30. # <init>执行顺序1、父类变量初始化。2、父类语句块。3、父类构造函数。4、子类变量初始化。5、子类语句块。6、子类构造函数。
  31. .method public constructor <init>()V
  32.     # 定义了2个寄存器
  33.     .registers 2
  34.     # 构造代码开始段
  35.     .prologue
  36.     .line 41 # 对应java源码41行
  37.     # p0(this)|Ljava/lang/Object;-><init>()V 调用{p0}就是this调用父类Object的<init>方法,参数为空,对应上面的执行顺序。
  38.     invoke-direct {p0}, Ljava/lang/Object;-><init>()V

  39.     .line 42 # 对应java源码42行
  40.     # new了一个ArrayList对象放到 v0中
  41.     new-instance v0, Ljava/util/ArrayList;
  42.     # v0|Ljava/util/ArrayList;-><init>()V ArrayList {v0}第一个参数就是本身ArrayList,调用init方法,参数为空
  43.     invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V
  44.     # p0=v0 p0(payList) = v0(new ArrayList())
  45.     iput-object v0, p0, Lcc/cspl/MyPay;->payList:Ljava/util/List;
  46.     # 返回void 对应 <init>()V
  47.     return-void
  48. .end method
  49. # 静态main方法,默认传入了字符串数组,返回void
  50. .method public static main([Ljava/lang/String;)V
  51.     # 定义了11个寄存器
  52.     .registers 11
  53.     # 参数p0 args
  54.     .param p0, "args"    # [Ljava/lang/String;
  55.     # 源码开始
  56.     .prologue
  57.     # 定义 局部变量 v9=2
  58.     const/4 v9, 0x2

  59.     .line 60
  60.     # 定义 v0=1
  61.     const/4 v0, 0x1

  62.     .line 61
  63.     # 设置 v0 别名Alipay,类型int
  64.     .local v0, "Alipay":I
  65.     # 定义 v2=2
  66.     const/4 v2, 0x2

  67.     .line 62
  68.     # 设置 v2 别名Wxpay,类型int
  69.     .local v2, "Wxpay":I
  70.     # 定义 v1=3
  71.     const/4 v1, 0x3

  72.     .line 63
  73.     # 设置 v1 别名ChinaMobilepay,类型int
  74.     .local v1, "ChinaMobilepay":I
  75.     # 定义一个实例对象v5,类型是MyPay;
  76.     new-instance v5, Lcc/cspl/MyPay;
  77.     # 调用第一个参数 {v5} ,实际没有参数,就是本身就是参数,初始化<init>
  78.     invoke-direct {v5}, Lcc/cspl/MyPay;-><init>()V

  79.     .line 64
  80.     # 定义一个本地变量 v5 别名是myPay,类型是MyPay
  81.     .local v5, "myPay":Lcc/cspl/MyPay;
  82.     # 定义一个实例对象v8,类型是AliPay;
  83.     new-instance v8, Lcc/cspl/AliPay;
  84.     # 调用直接方法,用第一个参数 {v8} ,实际没有参数,就是本身就是参数,初始化<init>
  85.     invoke-direct {v8}, Lcc/cspl/AliPay;-><init>()V
  86.     # 调用v5的虚方法add,参数是v8对象AliPay,返回void
  87.     # v8定义的类型是AliPay,v5的add方法接受的Pay类型,就是说Alipay向上转型为Pay
  88.     invoke-virtual {v5, v8}, Lcc/cspl/MyPay;->add(Lcc/cspl/Pay;)V

  89.     .line 65
  90.     # 定义一个实例对象v8,类型是WxPay;
  91.     new-instance v8, Lcc/cspl/WxPay;
  92.     # 调用直接方法,用第一个参数 {v8} ,实际没有参数,就是本身就是参数,初始化<init>
  93.     invoke-direct {v8}, Lcc/cspl/WxPay;-><init>()V
  94.     # 调用v5的虚方法add,参数是v8对象WxPay,返回void
  95.     # v8定义的类型是AliPay,v5的add方法接受的Pay类型,就是说WxPay向上转型为Pay
  96.     invoke-virtual {v5, v8}, Lcc/cspl/MyPay;->add(Lcc/cspl/Pay;)V

  97.     .line 66
  98.     # 定义一个实例对象v8,类型是ChinaMobilePay;
  99.     new-instance v8, Lcc/cspl/ChinaMobilePay;
  100.     # 调用直接方法,用第一个参数 {v8} ,实际没有参数,就是本身就是参数,初始化<init>
  101.     invoke-direct {v8}, Lcc/cspl/ChinaMobilePay;-><init>()V
  102.     # 调用v5的虚方法add,参数是v8对象ChinaMobilePay,返回void
  103.     # v8定义的类型是AliPay,v5的add方法接受的Pay类型,就是说ChinaMobilePay向上转型为Pay
  104.     invoke-virtual {v5, v8}, Lcc/cspl/MyPay;->add(Lcc/cspl/Pay;)V

  105.     .line 67
  106.     # 定义 v6=3
  107.     const/4 v6, 0x3

  108.     .line 68
  109.     # 定义 v6 别名是type
  110.     .local v6, "type":I
  111.     # 定义了一个有序的switch 传了v6进来,跳转到标签:pswitch_data_4e
  112.     packed-switch v6, :pswitch_data_4e

  113.     .line 85
  114.     # 设置标签 goto_25
  115.     :goto_25
  116.     # 执行完毕返回 void
  117.     return-void

  118.     .line 70
  119.     #case 分支 pswitch_26
  120.     :pswitch_26
  121.     #调用v5的虚方法getPayItem,参数是v9,返回Pay
  122.     invoke-virtual {v5, v9}, Lcc/cspl/MyPay;->getPayItem(I)Lcc/cspl/Pay;
  123.     # v3接受Pay对象
  124.     move-result-object v3
  125.     # 将v3(Pay)转化为Alipay,向下转型
  126.     check-cast v3, Lcc/cspl/AliPay;

  127.     .line 71
  128.     # v3 别名 aliPay
  129.     .local v3, "aliPay":Lcc/cspl/AliPay;
  130.     # 调用v3对象的doPay方法,没有参数,返回void
  131.     invoke-virtual {v3}, Lcc/cspl/AliPay;->doPay()V

  132.     .line 72
  133.     # 调用v3对象的callBack方法,没有参数,返回void
  134.     invoke-virtual {v3}, Lcc/cspl/AliPay;->callBack()V
  135.     # 无条件跳转到goto_25
  136.     goto :goto_25

  137.     .line 75
  138.     # 跟.local v3 和 .end local v3是一条语句。结束
  139.     .end local v3    # "aliPay":Lcc/cspl/AliPay;
  140.     # case 标签 pswitch_33
  141.     :pswitch_33
  142.     # 调用 v5对象的方法getPayItem,放入参数 v9,返回Pay类型对象
  143.     invoke-virtual {v5, v9}, Lcc/cspl/MyPay;->getPayItem(I)Lcc/cspl/Pay;
  144.     # v7接受返回Pay对象
  145.     move-result-object v7
  146.     # 把v7强转为WxPay类型,向下转型
  147.     check-cast v7, Lcc/cspl/WxPay;

  148.     .line 76
  149.     # v7 的别名是 wxPay
  150.     .local v7, "wxPay":Lcc/cspl/WxPay;
  151.     # 调用v7对象的doPay方法,返回void
  152.     invoke-virtual {v7}, Lcc/cspl/WxPay;->doPay()V

  153.     .line 77
  154.     # 调用v7对象的callBack方法,返回void
  155.     invoke-virtual {v7}, Lcc/cspl/WxPay;->callBack()V
  156.     # 无条件跳转到goto_25,就是结束
  157.     goto :goto_25

  158.     .line 80
  159.     # 跟.local v7 和 .end local v7是一条语句。结束
  160.     .end local v7    # "wxPay":Lcc/cspl/WxPay;
  161.     # case pswitch_40标签
  162.     :pswitch_40
  163.     # 调用v5对象的getPayItem方法,参数是v9,类型是int ,返回Pay
  164.     invoke-virtual {v5, v9}, Lcc/cspl/MyPay;->getPayItem(I)Lcc/cspl/Pay;
  165.     # 把 Pay对象存到v4中
  166.     move-result-object v4
  167.     # 把Pay对象v4转换为ChinaMobilePay,向下转型
  168.     check-cast v4, Lcc/cspl/ChinaMobilePay;

  169.     .line 81
  170.     # 给 v4 定义别名 chinaMobilePay,类型是 Lcc/cspl/ChinaMobilePay;
  171.     .local v4, "chinaMobilePay":Lcc/cspl/ChinaMobilePay;
  172.     # 调用v4对象中的方法 doPay方法,返回void
  173.     invoke-virtual {v4}, Lcc/cspl/ChinaMobilePay;->doPay()V

  174.     .line 82
  175.     # 调用v4对象中的方法 callBack方法,返回void
  176.     invoke-virtual {v4}, Lcc/cspl/ChinaMobilePay;->callBack()V
  177.     # 无条件跳转到goto_25
  178.     goto :goto_25

  179.     .line 68
  180.     # 空操作
  181.     nop
  182.     # switch 标签 pswitch_data_4e
  183.     :pswitch_data_4e
  184.     # switch(1)
  185.     .packed-switch 0x1
  186.         # case分支 跳转标签:pswitch_26
  187.         :pswitch_26
  188.         # case分支 跳转标签:pswitch_33
  189.         :pswitch_33
  190.         # case分支 跳转标签:pswitch_40
  191.         :pswitch_40
  192.      # 结束switch语句
  193.     .end packed-switch
  194. .end method

  195. # 虚方法,类中定义的方法,可以修改
  196. # virtual methods
  197. # 在MyPay类中定义一个add方法,参数是Pay对象,返回void
  198. .method public add(Lcc/cspl/Pay;)V
  199.     # 定义了3个寄存器
  200.     .registers 3
  201.     # 定义参数p1的别名是pay
  202.     .param p1, "pay"    # Lcc/cspl/Pay;

  203.     .prologue
  204.     .line 45
  205.     # 获取字段p0.payList,赋值给v0
  206.     iget-object v0, p0, Lcc/cspl/MyPay;->payList:Ljava/util/List;
  207.     # 调用v0的接口方法add,传入p1(pay),返回boolean
  208.     invoke-interface {v0, p1}, Ljava/util/List;->add(Ljava/lang/Object;)Z

  209.     .line 46
  210.     # 返回 void
  211.     return-void
  212. # add方法结束
  213. .end method
  214. # 在MyPay类中定义一个getPayItem方法,参数是int,返回Pay类型
  215. .method public getPayItem(I)Lcc/cspl/Pay;
  216.     #定义3个寄存器
  217.     .registers 3
  218.     # # 定义参数p1的别名是index,类型是int
  219.     .param p1, "index"    # I

  220.     .prologue
  221.     .line 53
  222.     # 获取字段p0.payList,赋值给v0
  223.     iget-object v0, p0, Lcc/cspl/MyPay;->payList:Ljava/util/List;
  224.     # 调用v0的接口方法add,传入p1(int),返回Object
  225.     invoke-interface {v0, p1}, Ljava/util/List;->get(I)Ljava/lang/Object;
  226.     # 把Object对象赋值给 v0
  227.     move-result-object v0
  228.     # 把vo 类型转换为Pay
  229.     check-cast v0, Lcc/cspl/Pay;
  230.     # 返回v0
  231.     return-object v0
  232. .end method
  233. # 在MyPay类中定义一个size方法,没有参数,返回Integer类型
  234. .method public size()Ljava/lang/Integer;
  235.     # 定义了2个寄存器
  236.     .registers 2

  237.     .prologue
  238.     .line 49
  239.     # 调用类型p0(MyPay).payList方法,返回List,赋值给v0
  240.     iget-object v0, p0, Lcc/cspl/MyPay;->payList:Ljava/util/List;
  241.     # 调用v0的接口方法size,返回int
  242.     invoke-interface {v0}, Ljava/util/List;->size()I
  243.     # 把返回的值赋值给v0
  244.     move-result v0
  245.     # 调用Integer.valueOf的方法,参入v0参数,返回Integer
  246.     invoke-static {v0}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
  247.     # 把对象返回给v0
  248.     move-result-object v0
  249.     # 返回对象v0
  250.     return-object v0
  251. .end method
复制代码


Java中异常举列:如下图
如果定义的是接口类型,那么向上向下转型会更灵活。

实际案例:登山赛车第一步:找到切入点,点击购买,提示购买失败
第二步:打开jadx,搜索失败关键词,定位代码
第三步:this.payCallback = new ChinaBillingPayCallback(this.billingCallback);找到ChinaBillingPayCallback定义
第四步:打开ChinaBillingPayCallback文件,定位构造函数,进入AbstractChinaBillingPayCallback父类
第五步:发现AbstractChinaBillingPayCallback构造函数是定义的支付集合,其中有一个函数launchResultReceived,被各种支付调用,最终调用billingCallback方法
第六步:修改billingCallback中的switch中的code值,始终等于1。最后完美实现购买道具。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

已有(2)人评论

跳转到指定楼层
恐龙先生 发表于 2021-7-28 00:01:54
此处应该有呼喊:牛批(破音~)

点评

明年还要找你  详情 回复 发表于 2021-7-30 14:12
<
回复

使用道具 举报

八十万禁军教头 发表于 2021-7-30 14:12:35
恐龙先生 发表于 2021-7-28 00:01
此处应该有呼喊:牛批(破音~)

明年还要找你
<
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则