Android逆向分析之dex2jar和jd-gui使用

从大三伊始到大四落幕,从刚开始接触Android到辞掉第一份实习工作,我接触Android应用层开发也快接近两年了。越来越发觉Android的应用层已经没什么挑战性了,想当初刚开始学习Android的时候,弄了一个Activity出来显示在手机的那份喜悦,真是~哈哈~,应用层的开发无非也就调用JDK,SDK而已,现在感觉有点小儿科啊,实习期间,每当工作项目之余,基本都泡到自定义View的绘制去,那也是我所能解闷的工作了。可是,这并不符合的职业规划,我想往前发展,比如framework层或者其他深层的技术等。

因为实习将近一年,在Android开发方面有一定的经验,再加上自己是应届生,所以有家稍微有点大但名气比较低(起码叫我面试的时候我都没听过这公司,额。。)的上市公司给我伸出了橄榄枝,从白纸开始培养人才。又因为提供的岗位叫Android逆向分析工程师,以前就听过这霸气的名字了,实际就是白帽子的工作,所以我也签了这公司,来实习了,毕竟还有一个月才能拿到毕业证。


说实话,逆向分析已经和Android应用开发不是一个level了,也和应用层开发没什么关系了,只是逆向分析需要熟悉应用层开发中的内容而已,比如反编译后要找到某个Activity或着fragment,总之,你要定位到关键代码,那你就必须得熟悉Android应用层的开发内容,尤其是混淆过,那就更需要熟悉开发的结构了,不然要在一大堆反编译的文件中找到你要的代码简直就令人发怵。

嗯,废话不多说,既然没有什么经验,那么就好好学习。先来个入门的工作,就是利用dex2jar反编译一个APK,并用jd-gui.exe查看jar包内容。

第一步,先写个简单的工程,并签名打包导出APK。

LoginActivity.java:

public class LoginActivity extends Activity {

    private final String ACCOUNT="samuel";
    private final String PASSWORD="123456";
    private EditText etAccount, etPassword;
    private Button btnLogin;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        etAccount=(EditText)findViewById(R.id.et_account);

        etPassword=(EditText)findViewById(R.id.et_password);

        btnLogin=(Button)findViewById(R.id.btn_login);

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isOK(etAccount.getText().toString(), etPassword.getText().toString())) {
                    Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
                }

            }
        });

    }

    private boolean isOK(String account, String password){
        return account.equals(ACCOUNT) && password.equals(PASSWORD);
    }

}

布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_centerInParent="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="帐号:"/>

            <EditText
                android:id="@+id/et_account"
                android:layout_width="100dp"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="密码:"/>

            <EditText
                android:id="@+id/et_password"
                android:layout_width="100dp"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <Button
            android:id="@+id/btn_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="登录"/>

    </LinearLayout>

</RelativeLayout>

签名打包 Build>Generate Signed APK:



第二步,将导出的xxx.apk文件的后缀.apk改为.zip,即压缩文件后缀,然后解压。(高版本的dex2jar貌似是不需要2,3步的,直接把APK扔到dos里,直接输入命令dex2jar xxx.apk即可)


第三步,下载dex2jar,用cmd打开dos系统,并在dex2jar所在窗口的dos位置输入  dex2jar  路径名+classes.dex,即在上图的文件夹中(不带路径,则默认在当前文件夹)生成一个.jar后缀的名字的文件。



第四步,jar文件是不可以直接看的,要用到配套的工具jd-gui.exe打开。



至此,反编译的入门工作也差不多了。可以看到,由于APK打包时没有作混淆处理,被反编译过来后,其代码的类名和成员变量名字都是没有变化的,这很容易让反编译的人看到源码,并实施恶意行为,所以在打包的时候必须要作混淆处理。

最后,我们比较一下不做和做了混淆处理后,同样经过以上步骤反编译出来的jar文件是怎样的:

不做混淆的反编译代码:

LoginActivity.java:

public class LoginActivity extends Activity
{
  private final String ACCOUNT = "samuel";
  private final String PASSWORD = "123456";
  private Button btnLogin;
  private EditText etAccount;
  private EditText etPassword;

  private boolean isOK(String paramString1, String paramString2)
  {
    return (paramString1.equals("samuel")) && (paramString2.equals("123456"));
  }

  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130968601);
    this.etAccount = ((EditText)findViewById(2131492944));
    this.etPassword = ((EditText)findViewById(2131492945));
    this.btnLogin = ((Button)findViewById(2131492946));
    this.btnLogin.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        if (LoginActivity.this.isOK(LoginActivity.this.etAccount.getText().toString(), LoginActivity.this.etPassword.getText().toString()))
        {
          Toast.makeText(LoginActivity.this, "登录成功", 0).show();
          return;
        }
        Toast.makeText(LoginActivity.this, "登录失败", 0).show();
      }
    });
  }
}

经过混淆处理后的反编译代码:

LoginActivity.java:

public class LoginActivity extends Activity
{
  private final String a = "samuel";
  private final String b = "123456";
  private EditText c;
  private EditText d;
  private Button e;

  private boolean a(String paramString1, String paramString2)
  {
    return (paramString1.equals("samuel")) && (paramString2.equals("123456"));
  }

  protected void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130968601);
    this.c = ((EditText)findViewById(2131492944));
    this.d = ((EditText)findViewById(2131492945));
    this.e = ((Button)findViewById(2131492946));
    this.e.setOnClickListener(new a(this));
  }
}

a.java:

class a
  implements View.OnClickListener
{
  a(LoginActivity paramLoginActivity)
  {
  }

  public void onClick(View paramView)
  {
    if (LoginActivity.a(this.a, LoginActivity.a(this.a).getText().toString(), LoginActivity.b(this.a).getText().toString()))
    {
      Toast.makeText(this.a, "登录成功", 0).show();
      return;
    }
    Toast.makeText(this.a, "登录失败", 0).show();
  }
}

比较可看到,混淆过的代码中,关键的变量名都被a, b, c, d等字母代替了,其中button的匿名内部监听类由View.OnClickListener类变成了a类。这样一来,混淆过的代码就没有那么容易受到恶意攻击了。但貌似还是有办法反混淆的,所以也没有百分百的安全吧。


反编译工具下载:dex2jar & jd-gui


发布了33 篇原创文章 · 获赞 50 · 访问量 21万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章