APK反编译后代码分析(一)

本文详细解析了使用jd-gui等工具查看并理解Android应用源代码的过程,包括如何利用反编译工具获取源码、面对混淆代码的解码思路、常见代码结构分析及还原方法,为开发者提供深入学习Android应用内部逻辑的指南。

一,用jd-gui.exe等工具查看源代码。如何你不会,可以参看此文章:

http://blog.youkuaiyun.com/hp_2008/article/details/8207879

 可以到以下连接下载可视化反编译工具:

http://download.youkuaiyun.com/detail/hp_2008/5099046

二,反编译后的源代码,是乱的,看不太懂。这是因为开发者在发布APK时,一般都会用代码混淆器将原始的源代码打乱,
这也是防盗的一种方法。但是再怎样防,道高一尺,魔高一丈,用反编译工具很轻松的就可反编译成源码。要想做到反编译成一点也看不懂的代码,这很难,
在Android Java开源的世界里就更难做到了。当然你可以把核心的放到中间层,用C/C++封装成库,通过JNI调用,这样要想反编译库就有难度了。

用混淆器打乱的代码,反编译后,要想看懂也不是一件容易的事。因为大部人都会用免费的混淆器来混淆源代码,大部份反编译过来的代码就有一定的规则可寻:

(本人水平有限,如有什么不对的地方,还请指教,非常非感)

反编译后的代码一般会产生以下结构的代码,(代码结构是个人总结的,如有雷同,纯属巧合)比较难看懂,本文章根据实现情况对这几种结构做个简单说明。

1,反编译后的代码:if while结构
    if (BTActivity.access$2300(this.this$0))
    {
      int i = BTActivity.access$700(this.this$0).sendCommand(1028, 4, paramInt);
      BTActivity.access$2500(this.this$0).notifyDataSetChanged();
    }
    while (true)
    {
      return;
      Bluetooth localBt = BTActivity.access$700(this.this$0);
      int k = BTActivity.access$600(this.this$0);
      int l = localBt.sendCommand(1026, paramInt, k);
     }

 (1)只分析逻辑
  很显然上面的代码也不是原始的源代码,但是很接近了,如果要想完成还原,还得自己改。
 分析上面的代码:这里应该有一个if else的逻辑。从上面的代码分析,return下的代码
 应该是永远不会被执行,但这是不可能的。所以上面的代码逻辑(我们先不看语句)应该是这样的,
  去掉while和return,加上else,修改后如下:
     if (BTActivity.access$2300(this.this$0))
    {
       int i = BTActivity.access$700(this.this$0).sendCommand(1028, 4, paramInt);
       BTActivity.access$2500(this.this$0).notifyDataSetChanged();
    }
  else
  {
      Bluetooth localBt = BTActivity.access$700(this.this$0);
      int k = BTActivity.access$600(this.this$0);
      int l = localBt.sendCommand(1026, paramInt, k);
    }
 
 这样的逻辑才是作者的本意。

  (2)逻辑分析完了,应该分析语句.
      A,这个真的很难看懂。像这句:int i = BTActivity.access$700(this.this$0).sendCommand(1028, 4, paramInt);
      很显然,这个是引用外部的一个类中的一个方法。然后返回整类,从下面的语句可以得知,这个返回值是用不到的。所以这句可去掉int i 
      还原上面的语句应该是这样的:
      BTActivity.access$700(this.this$0).sendCommand(1028, 4, paramInt);
      
      B,access$700是什么意思呢。这个应该是BTActivity类中的一个对像,然后这个对象有sendCommand方法。这样你就可以去查这个BTActivity类定义的哪个对象
      有这样一个方法了,这好理解。
      
      不好理解的是这句:int k = BTActivity.access$600(this.this$0); 这句初步还原就是去掉int k。
      然后是这个access$600,它对应的是BTActivity中的哪个方法呢。这很难确定,所以我说要猜,那有没有方法呢。当然有:
      1,看这个类中有多少个方法,如果只有一个,那指定就是它了。
      2,如果有多个,那就要看参数。如果只有一个方法的参数与之相对应,那一定是它。
      3,如果参数一样的也有多个。那看逻辑。如果看不出来,只有猜:)。或参加自己的逻辑。
   
   (3)为什么会出现像access$700(方法不一样,后面的数字也不一样)这样的方法名呢,原因是,调用者和被调者不在同一个类中。内部类也不行,如果
   两者在同一个类中,比如同类的方法调用,那反编译后的名字一定是可看懂的。不会是有数字的名字。
   
   (4) 再举一个更简单的if else例
       反编译后的代码:
        if (paramBoolean)
        paramTextView.setTextColor(-16727809);
       while (true)
       {
         return;
         paramTextView.setTextColor(-1315861);
       }
     
       还原成真正的原始代码,按我上面说的应该是:
       
         if (paramBoolean)
         {
           paramTextView.setTextColor(-16727809);
         }
         else
         {
            paramTextView.setTextColor(-1315861);
         }   
         
         以后碰到这样的if while还原就应该是上面的样子。
   

2, 反编译后的代码:switch case while结构

    switch (this.mBand)
    {
     default:
     case 0:
     case 1:
     case 2:
    }
    while (true)
    {
      return;
      this.mBand.setText("FM1");
      continue;
      this.mBand.setText("FM2");
      continue;
      this.mBand.setText("AM");
    }
   
   (1)分析逻辑:根据mBand的不同值,设置文本的显示内容。
   
   这个很好看懂,不再多说,还原成原始代码:
    switch (mBand)
    {

     case 0:
      mBand.setText("FM1");
      break;
     case 1:
       mBand.setText("FM2");
       break;
     case 2:
       mBand.setText("AM");
       break;
     default:
    }
 
   (2)这里关键的地方是:一个continue对你应着一个case的结束;。

 

3,反编译后代码如下:if for while结构 
      int i15 = this.freq;
      int i16 = this.rmin;
      if (i15 < i16)
        i17 = this.min;
      int i29;
      for (this.freq = i17; ; this.freq = i29)
      {
        int i27;
        int i28;
        do
        {
          this.x = getWidth();
          this.y = -1;
          break label32:
          i27 = this.freq;
          i28 = this.max;
        }
        while (i27 <= i28);
        i29 = this.max;
      }
      this.y = 0;
      invalidate();
      
      (1)代码逻辑分析:保证freq的值在min和max之间。
      
       分析后得到的原始源代码:
       
      if (freq < min)
      {
       freq = min;
      }
      
      if (freq <= max)
      {
         x = getWidth();
         y = -1;  
      }
      else
      {
       freq  = max;
       y = 0;
       invalidate();
      }
      
     (2)得到上面的源代码关键在于,按反编译后的代码走一遍。 

 

 4, 解析switch for while结构代码

 

   PowerManager localPowerManager = (PowerManager)getSystemService("power");
    switch (paramInt)
    {
    default:
    case 0:
    case 1:
    }
    for (String str = "on"; ; str = "other")
      while (true)
      {
        PowerManager.WakeLock localWakeLock = localPowerManager.newWakeLock(6, str);
        localWakeLock.acquire();
        localWakeLock.release();
        return;
        str = "off";
      }

还原原始源代码:

     PowerManager localPowerManager = (PowerManager)getSystemService("power");
     String str = null;
     switch (paramInt)
     {
      case 0:
       str = "on";
       break;
      case 1:
       str = "off";
       break;
      default:
       str = "other";
       break;
     }

     PowerManager.WakeLock localWakeLock = localPowerManager.newWakeLock(6, str);
        localWakeLock.acquire();
        localWakeLock.release();

 

 

5, 分析返编译后的代码(if while结构)

 例1:

   if (paramInt1 == 0)
      this.mMessage.setText("");
    while (true)
    {
      this.mAdditionalMessage.setVisibility(8);
      int i = this.mLevel.getMax();
      if (paramInt2 != i)
        this.mLevel.setMax(paramInt2);
      Toast localToast = this.mToast;
      ...... 

      return;
      TextView localTextView = this.mMessage;
      String str = "" + paramInt1;
      localTextView.setText(str);
    }

  分析:1,先去掉“this"

              2,看返编译后的按顺序逻辑走一遍。可以看出while到return这段代码,不管怎么样都会执行的。所以原始代码应该是这样的:

     setSmallIcon(paramInt1);
     paramInt1 &= 2147483647;
     if (paramInt1 == 0)
     {
       mMessage.setText("");
     }
     else
     {
       String str = "" + paramInt1;
       mMessage.setText(str);
     }
     mAdditionalMessage.setVisibility(8);
     if (paramInt2 != mLevel.getMax())
     {
       mLevel.setMax(paramInt2);
     }
     mToast.setView(mView);

原文:http://blog.youkuaiyun.com/ordinaryjoe/article/details/8626010

APK反编译后可从多个方面进行分析。 ### 代码层面分析 反编译得到的代码通常有Java代码和Smali代码。对于Java代码,可查看类、方法、变量的定义和使用,分析应用的业务逻辑,例如查看网络请求方法,了解应用与服务器的交互方式;查看数据库操作相关代码,明确数据的存储和读取逻辑。对于Smali代码,它是Dalvik虚拟机的汇编语言,可分析代码的执行流程、方法调用顺序等,比如通过查看invoke指令分析方法调用关系,通过查看寄存器的使用来理解数据的传递和处理过程 [^1]。 ### 资源文件分析 反编译会得到XML资源文件和图片等资源。XML资源文件中,AndroidManifest.xml文件至关重要,它包含了应用的组件信息(如Activity、Service、BroadcastReceiver等)、权限声明等。分析该文件可了解应用的功能模块和所需权限。其他XML文件可能包含布局信息、字符串资源等,可查看应用的界面结构和显示文本。图片等资源可了解应用的视觉设计风格和可能使用的图标、图片素材 [^2]。 ### 安全漏洞分析 检查代码中是否存在SQL注入、跨站脚本攻击(XSS)等安全漏洞。例如,查看代码中对用户输入的处理,若未进行有效的过滤和验证,可能存在SQL注入风险。还可检查应用的权限使用情况,判断是否存在过度申请权限的问题,以及权限的使用是否符合应用的功能需求 [^1]。 ### 功能模块分析反编译后的代码和资源按功能模块进行划分,分析每个模块的实现细节。比如,对于个社交应用,可分为用户登录模块、好友管理模块、消息通信模块等,分别分析每个模块的代码逻辑和数据交互方式 [^1]。 ### 代码混淆分析 如果应用进行了代码混淆,反编译后的代码可能难以阅读。可通过分析混淆规则,尝试还原部分代码的真实含义。例如,查看混淆配置文件,了解类名、方法名和变量名的混淆规则,再结合代码上下文进行推测和分析 [^2]。 ```python # 以下为个简单示例,模拟分析代码中是否存在未过滤用户输入的情况 def check_input_filtering(code): if "input = get_user_input()" in code and "filter(input)" not in code: return "可能存在安全漏洞,用户输入未过滤" return "未发现明显输入过滤问题" code_sample = 'input = get_user_input(); process(input);' result = check_input_filtering(code_sample) print(result) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值