【android】Android 破解实例(二)

接上一篇

分析一下这个apk的代码结构:google.gson肯定是google 解析json的gson库,nd.dianjin初步看一下,推断为 获取积分的第三方sdk,首先重点分析android.a这个包。

贴出Aa这个class的源代码,发现这个就是第一个activity的代码了,看来这个app并没有使用proguard进行混淆 =,= ,纯粹是命名方式让我产生了错觉。继续分析这个类的代码:

package com.android.a;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.nd.dianjin.DianJinPlatform;
import com.nd.dianjin.DianJinPlatform.OfferWallStyle;
import com.nd.dianjin.DianJinPlatform.Oriention;
import com.nd.dianjin.listener.AppActivatedListener;
import com.nd.dianjin.webservice.WebServiceListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Aa extends Activity
{
  String Pname;
  boolean isRomHave = false;
  final Handler mHan = new Handler();
  Runnable sxMB = new Runnable()
  {
    public void run()
    {
      TextView localTextView = (TextView)Aa.this.findViewById(1);
      if (Ac.point == null)
        Aa.this.getPoint();
      localTextView.setText("当前M币:" + Ac.point);
      Button localButton = (Button)Aa.this.findViewById(11);
      if ((Aa.this.isRomHave) && (Ac.point != null))
        localButton.setEnabled(true);
      while (true)
      {
        Aa.this.mHan.postDelayed(Aa.this.sxMB, 500L);
        return;
        localButton.setEnabled(false);
      }
    }
  };

  private void CopyAssetsRom()
  {
    if (new File("/data/data/" + this.Pname + "/rom_zse.zip").exists())
    {
      this.isRomHave = true;
      return;
    }
    AssetManager localAssetManager = getAssets();
    try
    {
      localInputStream = localAssetManager.open("rom_zse.zip");
      localFileOutputStream = new FileOutputStream("/data/data/" + this.Pname + "/rom_zse.zip");
    }
    catch (Exception localException1)
    {
      try
      {
        InputStream localInputStream;
        FileOutputStream localFileOutputStream;
        copyFile(localInputStream, localFileOutputStream);
        localInputStream.close();
        localFileOutputStream.flush();
        localFileOutputStream.close();
        while (true)
        {
          label109: this.isRomHave = true;
          return;
          localException1 = localException1;
        }
      }
      catch (Exception localException2)
      {
        break label109;
      }
    }
  }

  private void bujuset()
  {
    LinearLayout localLinearLayout = new LinearLayout(this);
    localLinearLayout.setGravity(17);
    localLinearLayout.setOrientation(1);
    TextView localTextView = new TextView(this);
    localTextView.setId(1);
    localTextView.setWidth(-2);
    localTextView.setGravity(17);
    Button localButton1 = new Button(this);
    localButton1.setId(11);
    Button localButton2 = new Button(this);
    Button localButton3 = new Button(this);
    localTextView.setText("当前M币:0");
    localButton1.setText("开始游戏");
    localButton2.setText("去除广告");
    localButton3.setText("退出");
    localLinearLayout.addView(localTextView);
    localLinearLayout.addView(localButton1);
    localLinearLayout.addView(localButton2);
    localLinearLayout.addView(localButton3);
    setContentView(localLinearLayout);
    localButton1.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        String str = "/data/data/" + Aa.this.Pname + File.separator;
        if (Ac.point.floatValue() >= 5.0F)
        {
          Aa.this.subPoint(5.0F);
          FRW.saveFile("1", str, "261905");
        }
        while (true)
        {
          Ac.GameCanRun = true;
          Aa.this.startNewAct();
          return;
          FRW.saveFile("0", str, "261905");
        }
      }
    });
    localButton2.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        Aa.this.dialog();
      }
    });
    localButton3.setOnClickListener(new View.OnClickListener()
    {
      public void onClick(View paramAnonymousView)
      {
        Ac.GameCanRun = false;
        Aa.this.finish();
      }
    });
  }

  private void chushihua()
  {
    DianJinPlatform.initialize(this, 18283, "866f016094916c9709b9ed998e214c6b");
    DianJinPlatform.setAppActivatedListener(new AppActivatedListener()
    {
      public void onAppActivatedResponse(int paramAnonymousInt, Float paramAnonymousFloat)
      {
        switch (paramAnonymousInt)
        {
        default:
          Toast.makeText(Aa.this, "奖励M币:ERROR", 0).show();
          return;
        case 7:
          Toast.makeText(Aa.this, "奖励M币:" + String.valueOf(paramAnonymousFloat), 0).show();
          return;
        case 8:
        }
        Toast.makeText(Aa.this, "奖励M币:0", 0).show();
      }
    });
  }

  private void copyFile(InputStream paramInputStream, OutputStream paramOutputStream)
    throws IOException
  {
    byte[] arrayOfByte = new byte[1024];
    while (true)
    {
      int i = paramInputStream.read(arrayOfByte);
      if (i == -1)
        return;
      paramOutputStream.write(arrayOfByte, 0, i);
    }
  }

  private Float getPoint()
  {
    DianJinPlatform.getBalance(this, new WebServiceListener()
    {
      public void onResponse(int paramAnonymousInt, Float paramAnonymousFloat)
      {
        switch (paramAnonymousInt)
        {
        default:
          Toast.makeText(Aa.this, "未知错误,错误码为:" + paramAnonymousInt, 0).show();
          Ac.point = Float.valueOf(0.0F);
          return;
        case 0:
          Ac.point = paramAnonymousFloat;
          return;
        case -1:
        }
        Toast.makeText(Aa.this, "获取余额失败", 0).show();
        Ac.point = Float.valueOf(0.0F);
      }
    });
    return Ac.point;
  }

  private void startNewAct()
  {
    Intent localIntent = getBaseContext().getPackageManager().getLaunchIntentForPackage(getBaseContext().getPackageName());
    localIntent.addFlags(67108864);
    startActivity(localIntent);
  }

  private float subPoint(float paramFloat)
  {
    DianJinPlatform.consume(this, paramFloat, new WebServiceListener()
    {
      public void onResponse(int paramAnonymousInt, Integer paramAnonymousInteger)
      {
        switch (paramAnonymousInt)
        {
        default:
          Toast.makeText(Aa.this, "未知错误,错误码为:" + paramAnonymousInt, 0).show();
          return;
        case 0:
          Toast.makeText(Aa.this, "成功去掉广告", 0).show();
          return;
        case 6001:
          Toast.makeText(Aa.this, "去广告失败请重启游戏", 0).show();
          return;
        case 6002:
          Toast.makeText(Aa.this, "余额不足", 0).show();
          return;
        case 6003:
          Toast.makeText(Aa.this, "账号不存在", 0).show();
          return;
        case 6004:
          Toast.makeText(Aa.this, "订单号重复", 0).show();
          return;
        case 6005:
          Toast.makeText(Aa.this, "一次性交易金额超过最大限定金额", 0).show();
          return;
        case 6006:
        }
        Toast.makeText(Aa.this, "不存在该类型的消费动作ID", 0).show();
      }
    });
    return getPoint().floatValue();
  }

  protected void dialog()
  {
    AlertDialog.Builder localBuilder = new AlertDialog.Builder(this);
    localBuilder.setMessage("只需5个M币即可无广告运行");
    localBuilder.setIcon(2130837514);
    localBuilder.setTitle("好消息");
    localBuilder.setPositiveButton("免费获取M币", new DialogInterface.OnClickListener()
    {
      public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt)
      {
        paramAnonymousDialogInterface.dismiss();
        DianJinPlatform.showOfferWall(Aa.this, DianJinPlatform.Oriention.SENSOR, DianJinPlatform.OfferWallStyle.BLUE);
      }
    });
    localBuilder.setNegativeButton("精品推荐", new DialogInterface.OnClickListener()
    {
      public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt)
      {
        paramAnonymousDialogInterface.dismiss();
        DianJinPlatform.showOfferWall(Aa.this, DianJinPlatform.Oriention.SENSOR, DianJinPlatform.OfferWallStyle.BROWN);
      }
    });
    localBuilder.create().show();
  }

  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    this.Pname = getPackageName();
    CopyAssetsRom();
    chushihua();
    bujuset();
    this.mHan.post(this.sxMB);
  }

  protected void onDestroy()
  {
    DianJinPlatform.destroy();
    Ac.GameCanRun = false;
    super.onDestroy();
  }

  public boolean onKeyDown(int paramInt, KeyEvent paramKeyEvent)
  {
    if (paramInt == 4)
      new AlertDialog.Builder(this).setTitle("提示").setMessage("是否确认退出?").setCancelable(false).setPositiveButton("确定", new DialogInterface.OnClickListener()
      {
        public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt)
        {
          Ac.GameCanRun = false;
          Aa.this.finish();
        }
      }).setNegativeButton("取消", new DialogInterface.OnClickListener()
      {
        public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt)
        {
          paramAnonymousDialogInterface.cancel();
        }
      }).show();
    return true; 
  }

  protected void onResume()
  {
    getPoint();
    super.onResume();
  }
}

还有个类,Ac.class,看来关键就是这2个类了

package com.android.a;

public class Ac
{
  public static boolean GameCanRun = false;
  public static Float point;
}

再简单的看一下,这个Ac.point应该就是当前的M币.


观察到sxMB这个runnable对象:

 public void run()
    {
      TextView localTextView = (TextView)Aa.this.findViewById(1);
      if (Ac.point == null)
        Aa.this.getPoint();
      localTextView.setText("当前M币:" + Ac.point);

以及在Ac 类中 Float point ==null ,所以可以直接考虑为将Ac.point 赋予一个足够大的初值。


以及:

Button localButton = (Button)Aa.this.findViewById(11);
      if ((Aa.this.isRomHave) && (Ac.point != null))        //将isRomHave置1,使localButton为enable
        localButton.setEnabled(true);
      while (true)
      {
        Aa.this.mHan.postDelayed(Aa.this.sxMB, 500L);     //表示500ms运行一次sxMB ,直接删除掉
        return;
        localButton.setEnabled(false);

 下面将对此文件进行修改:


步骤四:修改

使用apktool 对此apk进行反汇编并修改,命令如下: apktool d  sanguo2.apk  ,对于apktool的使用,这里就不进行详细说明了




在apk的安装目录下,得到sanguo2这样一个文件夹,这里的smali 文件夹下就是前一篇文章所介绍的源文件的汇编代码。



打开Ac.smali文件: 我这里用的编辑器是sumblime text2

.class public Lcom/android/a/Ac;
.super Ljava/lang/Object;
.source "Ac.java"


# static fields
.field public static GameCanRun:Z

.field public static point:Ljava/lang/Float;


# direct methods
.method static constructor <clinit>()V
    .locals 1

    .prologue
    .line 4
    const/4 v0, 0x0

    sput-boolean v0, Lcom/android/a/Ac;->GameCanRun:Z

    .line 3
    return-void
.end method

.method public constructor <init>()V
    .locals 0

    .prologue
    .line 3
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

这个就是Ac.class对应的 汇编文件,smali的语法可参照google code上的smali项目主页: http://code.google.com/p/smali/

我们将point值赋予5000,修改后的smali文件如下图所示:

.class public Lcom/android/a/Ac;
.super Ljava/lang/Object;
.source "Ac.java"


# static fields
.field public static GameCanRun:Z

.field public static point:Ljava/lang/Float;


# direct methods
.method static constructor <clinit>()V
    .locals 1

    .prologue
    .line 4
    const/4 v0, 0x0

    sput-boolean v0, Lcom/android/a/Ac;->GameCanRun:Z

    .line 5

    const v0, 0x459c4000

    invoke-static {v0}, Ljava/lang/Float;->valueOf(F)Ljava/lang/Float;

    move-result-object v0

    sput-object v0, Lcom/android/a/Ac;->point:Ljava/lang/Float;
    
    return-void
.end method

.method public constructor <init>()V
    .locals 0

    .prologue
    .line 3
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method


在这里我简单解释下增加的几行代码:

    .line 5 
    //表示代码在原java文件中的行数,它只是为了方便调试,不是必须的,去掉也没关系                                            
     
     const v0, 0x459c4000
    //表示将0x459c400的值放入v0寄存器

    invoke-static {v0}, Ljava/lang/Float;->valueOf(F)Ljava/lang/Float;
    //表示调用静态方法valueof(f),L表示对象,java/lang/Float 表示包/类名, Ljava/lang/Float表示返回的类型

    move-result-object v0
    //将得到的值放入寄存器v0中

    sput-object v0, Lcom/android/a/Ac;->point:Ljava/lang/Float;
    //和上面类似,表示将v0中的值放入point这个变量中


在Aa.smali 中:

删除line53:


invoke-virtual {v0, v1}, Landroid/os/Handler;->post(Ljava/lang/Runnable;)Z

修改line30:

    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/android/a/Aa;->isRomHave:Z

为:

    const/4 v0, 0x1

    iput-boolean v0, p0, Lcom/android/a/Aa;->isRomHave:Z



修改完之后保存,并重新打包:

在cmd下输入apktool  b  sanguo2,如图所示:



这时会在这个文件夹下生成两个文件夹:存放中间文件的文件夹build和存放最后的apk文件的文件夹dist。

如果一切顺利的话,你就可以在dist文件夹中看到我们修改后的sanguo2.apk了。不过需要注意的是,这个APK文件是还没有签名的,所以无法安装运行。我们还需要进行最后一步,那就是对这个APK进行签名。


步骤五:重新签名

这里使用的工具是autosign,下载地址( http://pan.baidu.com/s/1o6I4Z9o ),双击sign.bat 即可使用。需要注意的是,要把签名文件改为update.zip , 然后才可以使用。

原因我们打开sign.bat 这个批处理文件就可以知道:

@ECHO OFF
Echo Auto-sign Created By Dave Da illest 1 
Echo Update.zip is now being signed and will be renamed to update_signed.zip

java -jar signapk.jar testkey.x509.pem testkey.pk8 update.zip update_signed.zip

Echo Signing Complete 
 
Pause
EXIT


就是调用 signapk 进行签名 ,update.zip 表示被签名的文件,update_signed.zip表示签名后的文件名。


这样,这次小小的破解就算完成了,让我们试试效果吧!

安装,运行游戏:



好了,开始游戏吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值