android反编译还原项目指南

文章链接:http://www.liuschen.com

Android反编译工具就不说了,之前已经具体说过,只要会用几个功能,就能解出源码,如果恰巧源码没有进行混淆,那么就赚了,理论上可以还原出项目的开发文件的

首先举一个例子,以下是原始代码,某个ListView的adapter的getView方法

@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		
		if (getCount() == 0) {
			return null;
		}
		ViewHolder holder = null;
		if (convertView == null) {
			holder=new ViewHolder();
			convertView=mInflater.inflate(R.layout.list_item_updownload, null);
			holder.tv1=(TextView) convertView.findViewById(R.id.listitem_1);
			holder.tv2=(TextView) convertView.findViewById(R.id.listitem_2);
			holder.tv3=(TextView) convertView.findViewById(R.id.listitem_3);
			holder.tv4=(TextView) convertView.findViewById(R.id.listitem_4);
			holder.tv5=(TextView) convertView.findViewById(R.id.listitem_5);
			holder.tv6=(TextView) convertView.findViewById(R.id.listitem_6);
			
			convertView.setTag(holder);
			
		} else {
			holder = (ViewHolder) convertView.getTag();
		}
		holder.tv1.setText(mList.get(position).getNum());
		holder.tv2.setText(mList.get(position).getProjectName());
		holder.tv3.setText(mList.get(position).getZypName());
		holder.tv4.setText(mList.get(position).getZuoyedanwei());
		holder.tv5.setText(mList.get(position).getStartTime());
		holder.tv6.setText(mList.get(position).getEndTime());
		return convertView;
	}
}

以下是编译成class文件后反解后对应的方法

 public View getView(int paramInt, View paramView, ViewGroup paramViewGroup)
{
  if (getCount() == 0)
    return null;
  DataUploadActivity.ViewHolder localViewHolder;
  if (paramView == null)
  {
    localViewHolder = new DataUploadActivity.ViewHolder(null);
    paramView = this.mInflater.inflate(2130903048, null);
    DataUploadActivity.ViewHolder.access$1(localViewHolder, (TextView)paramView.findViewById(2131492897));
    DataUploadActivity.ViewHolder.access$2(localViewHolder, (TextView)paramView.findViewById(2131492898));
    DataUploadActivity.ViewHolder.access$3(localViewHolder, (TextView)paramView.findViewById(2131492899));
    DataUploadActivity.ViewHolder.access$4(localViewHolder, (TextView)paramView.findViewById(2131492901));
    DataUploadActivity.ViewHolder.access$5(localViewHolder, (TextView)paramView.findViewById(2131492902));
    DataUploadActivity.ViewHolder.access$6(localViewHolder, (TextView)paramView.findViewById(2131492903));
    paramView.setTag(localViewHolder);
  }
  while (true)
  {
    DataUploadActivity.ViewHolder.access$7(localViewHolder).setText(((ZYPbeanUpDownLoad)this.mList.get(paramInt)).getNum());
    DataUploadActivity.ViewHolder.access$8(localViewHolder).setText(((ZYPbeanUpDownLoad)this.mList.get(paramInt)).getProjectName());
    DataUploadActivity.ViewHolder.access$9(localViewHolder).setText(((ZYPbeanUpDownLoad)this.mList.get(paramInt)).getZypName());
    DataUploadActivity.ViewHolder.access$10(localViewHolder).setText(((ZYPbeanUpDownLoad)this.mList.get(paramInt)).getZuoyedanwei());
    DataUploadActivity.ViewHolder.access$11(localViewHolder).setText(((ZYPbeanUpDownLoad)this.mList.get(paramInt)).getStartTime());
    DataUploadActivity.ViewHolder.access$12(localViewHolder).setText(((ZYPbeanUpDownLoad)this.mList.get(paramInt)).getEndTime());
    return paramView;
    localViewHolder = (DataUploadActivity.ViewHolder)paramView.getTag();
  }
}

可以看出的是:

  1. 原始代码的注解不见了
  2. 资源文件变成了一串数字
  3. 里面多了很多个$

先来说第三点,因为第三点出现的范围相当广而且需要先进行修改来使项目顺利通过编译。

  • 首先,当定义一个变量并被赋值,然后再回调方法中被调用,这时由于回调方法内部和定义变量的位置位于不同作用域,所以需要变量前加final修饰,当编译在反编译后的得到的代码将没有final修饰符,该变量作为一个参数传入回调的类中,在类中引用该变量则变为 val$原始类名

由此引申,一个回调方法即使没有参数,也会默认传入null,遇到删除就好

  • break的用法,在反编译得到的代码中会遇到break label+数字 的代码,这个其实和goto语句一样是一个跳转语句,千万不要大惊小怪。可见,其实像if这样的条件语句都是翻译成跳转语句执行的。

for循环会变成while(true)+if条件判断,if条件语句如果体量过大,会被优化为if-break跳转语句。我遇到的多重嵌套语句,很多都会变成break(这里同goto,因为Java中没有goto,所以用break)+while循环形式

  • 上面例子中的access$数字形式,适用于内部类访问外部类数据成员时,是编译阶段自动生成的
  • switch语句也是有点头疼,反编译出来会变得很错乱,就不要看代码表面的逻辑了,还是会出现一个while循环,而且里面可能有出现的很不可理的return和continue,这是就看对代码上下文理解来整理了,没什么技巧。switch的default会被放在第一个,还有最后的return会被提前
  • 还有一个有趣的现象,就是值数字的变量名会被改为诸如i,j,k这种让人混淆的名字。

还有诸多小问题,如

  • 变量没有初始化值
  • 优化后代码出现大量跟在return或是break后的不可用语句
  • 至于资源文件的对照,只能参考反编译出来的R文件批量修改了
  • 大多注解都是对应的编译阶段,还有泛型就是为了编译而生的,如果因为缺少而报错需要加上

这里还有一个很重要的问题,有时候一大段代码会反编译不出来,反编译的结果是注释掉的代码块,然后提示说是这一段代码有错误。那么,这一大段代码就只有自己根据上下文的逻辑自己写了。

Loading Disqus comments...
Table of Contents