JD-GUI反编译后代码逻辑分析,增加一点我自己碰到的例子

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

http://blog.csdn.net/hp_2008/article/details/8207879

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

http://download.csdn.net/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);

      ......

 6,一个continue对应一个back原则(switch while结构)
在这种形式中,一个contiune一定是对应一个back,但一个case不一定只对应一个contiune,也有一个case对应两个或多个contiune(即back).
如以下反编译后的代码:

 

[java]  view plain copy
  1. switch (getId())  
  2.  {  
  3.  case 2131034119:  
  4.  case 2131034120:  
  5.  case 2131034121:  
  6.  case 2131034122:  
  7.  case 2131034123:  
  8.  case 2131034124:  
  9.  case 2131034125:  
  10.  case 2131034126:  
  11.  case 2131034127:  
  12.  case 2131034128:  
  13.  default:  
  14.  case 2131034129:  
  15.  case 2131034130:  
  16.  case 2131034131:  
  17.  case 2131034132:  
  18.  case 2131034133:  
  19.  case 2131034134:  
  20.  case 2131034117:  
  21.  case 2131034118:  
  22.  }  
  23.  while (true)  
  24.  {  
  25.    return;  
  26.    int i = paramVerticalSeekBar.getProgress() * 14;  
  27.    int j = paramVerticalSeekBar.getMax();  
  28.    int k = i / j;  
  29.    if (APPActivity.access$200(this.this$0) == k)  
  30.      continue;  
  31.    int l = APPActivity.access$202(this.this$0, k);  
  32.    int i1 = APPActivity.access$200(this.this$0);  
  33.     continue;  
  34.    int i3 = paramVerticalSeekBar.getProgress() * 14;  
  35.    int i4 = paramVerticalSeekBar.getMax();  
  36.    k = i3 / i4;  
  37.    if (APPActivity.access$400(this.this$0) == k)  
  38.      continue;  
  39.    int i5 = APPActivity.access$402(this.this$0, k);  
  40.    EQ localEQ2 = APPActivity.access$1400(this.this$0);  
  41.    int i6 = APPActivity.access$400(this.this$0);  
  42.    int i7 = localEQ2.sendCommand(2572, i6);  
  43.    continue;  
  44.    int i8 = paramVerticalSeekBar.getProgress() * 14;  
  45.    int i9 = paramVerticalSeekBar.getMax();  
  46.    k = i8 / i9;  
  47.    if (APPActivity.access$500(this.this$0) == k)  
  48.      continue;  
  49.    int i10 = APPActivity.access$502(this.this$0, k);  
  50.    int i11 = APPActivity.access$500(this.this$0);  
  51.    continue;  
  52.    int i13 = paramVerticalSeekBar.getProgress() * 3;  
  53.    int i14 = paramVerticalSeekBar.getMax();  
  54.    k = i13 / i14;  
  55.    if (APPActivity.access$600(this.this$0) == k)  
  56.      continue;  
  57.    int i15 = APPActivity.access$602(this.this$0, k);  
  58.    EQ localEQ4 = APPActivity.access$1400(this.this$0);  
  59.    int i16 = APPActivity.access$600(this.this$0);  
  60.    int i17 = localEQ4.sendCommand(2578, i16);  
  61.    continue;  
  62.    int i18 = paramVerticalSeekBar.getProgress() * 3;  
  63.    int i19 = paramVerticalSeekBar.getMax();  
  64.    k = i18 / i19;  
  65.    if (EQActivity.access$700(this.this$0) == k)  
  66.      continue;  
  67.    int i20 = APPActivity.access$702(this.this$0, k);  
  68.    EQ localEQ5 = APPActivity.access$1400(this.this$0);  
  69.    int i21 = APPActivity.access$700(this.this$0);  
  70.    continue;  
  71.    int i23 = paramVerticalSeekBar.getProgress() * 3;  
  72.    int i24 = paramVerticalSeekBar.getMax();  
  73.    k = i23 / i24;  
  74.    if (APPActivity.access$800(this.this$0) == k)  
  75.      continue;  
  76.    int i25 = APPActivity.access$802(this.this$0, k);  
  77.    EQ localEQ6 = APPActivity.access$1400(this.this$0);  
  78.    int i26 = APPActivity.access$800(this.this$0);  
  79.    continue;  
  80.    int i28 = paramVerticalSeekBar.getProgress() * 14;  
  81.    int i29 = paramVerticalSeekBar.getMax();  
  82.    k = i28 / i29;  
  83.    if (APPActivity.access$900(this.this$0) == k)  
  84.      continue;  
  85.    int i30 = APPActivity.access$902(this.this$0, k);  
  86.    EQ localEQ7 = APPActivity.access$1400(this.this$0);  
  87.    int i31 = APPActivityvity.access$900(this.this$0);  
  88.    continue;  
  89.    int i33 = paramVerticalSeekBar.getProgress() * 3;  
  90.    int i34 = paramVerticalSeekBar.getMax();  
  91.    k = i33 / i34;  
  92.    if (APPActivity.access$1000(this.this$0) == k)  
  93.      continue;  
  94.    int i35 = APPActivity.access$1002(this.this$0, k);  
  95.    EQ localEQ8 = APPActivity.access$1400(this.this$0);  
  96.    int i36 = APPActivity.access$1000(this.this$0);  
  97.  }  


    分析代码:

1),上遍已对这种形式有讲过,一个continue对应一个case,但是你数一数会发现,对不上号,明显case多于contiune,原因是什么呢?其实switch里的default对应的是while中的return,在switch中default以上的case是用不着,是没有用的。

2),如果default上面的case没有用,聪明的你可以可能会问两个问题?
A,default上面的case没有用,为什么还会有呢?原因很简单,因为反编译器也不是全智能的总会有不对的(但是执行逻辑是不会有错),呵呵真正的原因当然不会是这样,你可以自己去分析一下,从整个程序分析就可以得出结论来。
B,那按一个continue对应一个back的原则不是有错吗? 当然没有。一个continue还是对应一个back, 聪明的你一定看懂了,每两个continue中间还有一个if语句,对,没错,你理解是对的,就是在这中间满足条件时就会back,所以就会有一个continue与之相对应。
  所这里每个if + continue + continue的形式对应一个case。
3),恢复原代码结构就变的简单,这里就再敖叙了。


这个是我碰到的

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;


import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;


public class FileCharsetDetector {
    private boolean found = false;
    private String encoding = null;


    public static void main(String[] argv) throws Exception {
        File file1 = new File("C:\\test1.txt");
        
        System.out.println("文件编码:" + new FileCharsetDetector().guessFileEncoding(file1));
    }


    /**
     * 传入一个文件(File)对象,检查文件编码
     * 
     * @param file
     *            File对象实例
     * @return 文件编码,若无,则返回null
     * @throws FileNotFoundException
     * @throws IOException
     */
    public String guessFileEncoding(File file) throws FileNotFoundException, IOException {
        return guessFileEncoding(file, new nsDetector());
    }


    /**
     * <pre>
     * 获取文件的编码
     * @param file
     *            File对象实例
     * @param languageHint
     *            语言提示区域代码 @see #nsPSMDetector ,取值如下:
     *             1 : Japanese
     *             2 : Chinese
     *             3 : Simplified Chinese
     *             4 : Traditional Chinese
     *             5 : Korean
     *             6 : Dont know(default)
     * </pre>
     * 
     * @return 文件编码,eg:UTF-8,GBK,GB2312形式(不确定的时候,返回可能的字符编码序列);若无,则返回null
     * @throws FileNotFoundException
     * @throws IOException
     */
    public String guessFileEncoding(File file, int languageHint) throws FileNotFoundException, IOException {
        return guessFileEncoding(file, new nsDetector(languageHint));
    }


    /**
     * 获取文件的编码
     * 
     * @param file
     * @param det
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     */
    private String guessFileEncoding(File file, nsDetector det) throws FileNotFoundException, IOException {
        // Set an observer...
        // The Notify() will be called when a matching charset is found.
        det.Init(new nsICharsetDetectionObserver() {
            public void Notify(String charset) {
                encoding = charset;
                found = true;
            }
        });


        BufferedInputStream imp = new BufferedInputStream(new FileInputStream(file));
        byte[] buf = new byte[1024];
        int len;
        boolean done = false;
        boolean isAscii = false;


        while ((len = imp.read(buf, 0, buf.length)) != -1) {
            // Check if the stream is only ascii.
            isAscii = det.isAscii(buf, len);
            if (isAscii) {
                break;
            }
            // DoIt if non-ascii and not done yet.
            done = det.DoIt(buf, len, false);
            if (done) {
                break;
            }
        }
        imp.close();
        det.DataEnd();


        if (isAscii) {
            encoding = "ASCII";
            found = true;
        }


        if (!found) {
            String[] prob = det.getProbableCharsets();
            //这里将可能的字符集组合起来返回
            for (int i = 0; i < prob.length; i++) {
                if (i == 0) {
                    encoding = prob[i];
                } else {
                    encoding += "," + prob[i];
                }
            }


            if (prob.length > 0) {
                // 在没有发现情况下,也可以只取第一个可能的编码,这里返回的是一个可能的序列
                return encoding;
            } else {
                return null;
            }
        }
        return encoding;
    }
}

/*** Eclipse Class Decompiler plugin, copyright (c) 2012 Chao Chen (cnfree2000@hotmail.com) ***/
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;

public class FileCharsetDetector {
	private boolean found;
	private String encoding;

	public static FileCharsetDetector getInstance() {
		return Holder.INSTANCE;
	}

	public static void main(String[] argv) throws Exception {
		if ((argv.length != 1) && (argv.length != 2)) {
			System.out.println("Usage: FileCharsetDetector <path> [<languageHint>]");

			System.out.println("");
			System.out.println("Where <path> is d:/demo.txt");
			System.out.println("For optional <languageHint>. Use following...");
			System.out.println("        1 => Japanese");
			System.out.println("        2 => Chinese");
			System.out.println("        3 => Simplified Chinese");
			System.out.println("        4 => Traditional Chinese");
			System.out.println("        5 => Korean");
			System.out.println("        6 => Dont know (default)");

			return;
		}
		String encoding = null;
		if (argv.length == 2) {
			encoding = new FileCharsetDetector().guestFileEncoding(argv[0], Integer.valueOf(argv[1]).intValue());
		} else {
			encoding = new FileCharsetDetector().guestFileEncoding(argv[0]);
		}
		System.out.println("文件编码:" + encoding);
	}

	public String guestFileEncoding(File file) throws FileNotFoundException, IOException {
		return geestFileEncoding(file, new nsDetector());
	}

	public String guestFileEncoding(File file, int languageHint) throws FileNotFoundException, IOException {
		return geestFileEncoding(file, new nsDetector(languageHint));
	}

	public String guestFileEncoding(String path) throws FileNotFoundException, IOException {
		return guestFileEncoding(new File(path));
	}

	public String guestFileEncoding(String path, int languageHint) throws FileNotFoundException, IOException {
		return guestFileEncoding(new File(path), languageHint);
	}

	private String geestFileEncoding(File file, nsDetector det) throws FileNotFoundException, IOException {
		det.Init(new nsICharsetDetectionObserver() {
			public void notify(String charset) {
				FileCharsetDetector.access$202(FileCharsetDetector.this, true);
				FileCharsetDetector.access$302(FileCharsetDetector.this, charset);
			}
		});
		BufferedInputStream imp = new BufferedInputStream(new FileInputStream(file));

		byte[] buf = new byte[1024];
		int len;
		boolean done = false;
		boolean isAscii = true;
		
		while ((len = imp.read(buf, 0, buf.length)) != -1) {
			
			if (isAscii) {
				isAscii = det.isAscii(buf, len);
			}

			if ((!(isAscii)) && (!(done)))
				;
			done = det.DoIt(buf, len, false);
		}
		det.DataEnd();

		if (isAscii) {
			this.encoding = "ASCII";
			this.found = true;
		}

		if (!(this.found)) {
			String[] prob = det.getProbableCharsets();
			if (prob.length > 0) {
				this.encoding = prob[0];
			} else
				return null;
		}

		return this.encoding;
	}

	private static class Holder {
		private static final FileCharsetDetector INSTANCE = new FileCharsetDetector();
	}
}

FileCharsetDetector.access$202(FileCharsetDetector.this, true);
FileCharsetDetector.access$302(FileCharsetDetector.this, charset);

这两行代码反编译没编译成功

对比上面其实是给FIleCharsetDetector的两个参数赋值encoding = charset;
found = true;

转自:http://blog.csdn.net/ordinaryjoe/article/details/8626010

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值