android apk 防止反编译技术加壳技术(转)

2015/05/25 21:56
阅读数 125

一、加壳技术原理

所谓apk的加壳技术和pc exe的加壳原理一样,就是在程序的外面再包裹上另外一段代码,保护里面的代码不被非法修改或反编译,在程序运行的时候优先取得程序的控制权做一些我们自己想做的工作。

PC exe的加壳原理如下:

二、android apk加壳实现

  要想实现加壳需要解决的技术点如下:

1)怎么第一时间执行我们的加壳程序?

首先根据上面的原理我们在apk中要想优先取得程序的控制权作为android apk的开发人员都知道Application会被系统第一时间调用而我们的程序也会放在这里执行。

2)怎么将我们的加壳程序和原有的android apk文件合并到一起?

我们知道android apk最终会打包生成dex文件,我们可以将我们的程序生成dex文件后,将我们要进行加壳的apk和我们dex文件合并成一个文件,然后修改dex文件头中的checksumsignaturefile_size的信息,并且要附加加壳的apk的长度信息在dex文件中,以便我们进行解壳保证原来apk的正常运行。加完壳后整个文件的结构如下:

3)怎么将原来的apk正常的运行起来?

按照(2)中的合并方式在当我们的程序首先运行起来后,逆向读取dex文件获取原来的apk文件通过DexClassLoader动态加载。

具体实现如下:

1)修改原来apkAndroidMainfest.xml文件,假如原来apkAndroidMainfest.xml文件内容如下:

1.  <application  

2.      android:icon="@drawable/ic_launcher"  

3.      android:label="@string/app_name"  

4.      android:theme="@style/AppTheme" android:name="com.android.MyApplication" >  

5.  </application>

修改后的内容如下:

1.  <application  

2.      android:icon="@drawable/ic_launcher"  

3.      android:label="@string/app_name"  

4.      android:theme="@style/AppTheme" android:name="com.android.shellApplication" >  

5.  <meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.android.MyApplication"/>

6.  </application> 

com.android.shellApplication这个就是我们的程序的的application的名称,而

7.  <meta-data android:name="APPLICATION_CLASS_NAME" android:value="com.android.MyApplication"/>

是原来的apkapplication名称。

2)合并文件代码实现如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

public class ShellTool {

  /**

   * @param args

   */

  public static void main(String[] args) {

         // TODO Auto-generated method stub

         try {

                File payloadSrcFile = new File("payload.apk");//我们要加壳的apk文件

                File unShellDexFile = new File("classes.dex");//我们的程序生成的dex文件

                byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));

                byte[] unShellDexArray = readFileBytes(unShellDexFile);

                int payloadLen = payloadArray.length;

                int unShellDexLen = unShellDexArray.length;

                int totalLen = payloadLen + unShellDexLen +4;

                byte[] newdex = new byte[totalLen];

                //添加我们程序的dex

                System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);

                //添加要加壳的apk文件

                System.arraycopy(payloadArray, 0, newdex, unShellDexLen,

                              payloadLen);

                //添加apk文件长度

                System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);

                        //修改DEX file size文件头

                fixFileSizeHeader(newdex);

                //修改DEX SHA1 文件头

                fixSHA1Header(newdex);

                //修改DEX CheckSum文件头

                fixCheckSumHeader(newdex);

  

  

                String str = "outdir/classes.dex";

                File file = new File(str);

                if (!file.exists()) {

                       file.createNewFile();

                }

                 

                FileOutputStream localFileOutputStream = new FileOutputStream(str);

                localFileOutputStream.write(newdex);

                localFileOutputStream.flush();

                localFileOutputStream.close();

  

  

         } catch (Exception e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

         }

  }

   

  //直接返回数据,读者可以添加自己加密方法

  private static byte[] encrpt(byte[] srcdata){

         return srcdata;

  }

  

  

  private static void fixCheckSumHeader(byte[] dexBytes) {

         Adler32 adler = new Adler32();

         adler.update(dexBytes, 12, dexBytes.length - 12);

         long value = adler.getValue();

         int va = (int) value;

         byte[] newcs = intToByte(va);

         byte[] recs = new byte[4];

         for (int i = 0; i < 4; i++) {

                recs[i] = newcs[newcs.length - 1 - i];

                System.out.println(Integer.toHexString(newcs[i]));

         }

         System.arraycopy(recs, 0, dexBytes, 8, 4);

         System.out.println(Long.toHexString(value));

         System.out.println();

  }

  

  

  public static byte[] intToByte(int number) {

         byte[] b = new byte[4];

         for (int i = 3; i >= 0; i--) {

                b[i] = (byte) (number % 256);

                number >>= 8;

         }

         return b;

  }

  

  

  private static void fixSHA1Header(byte[] dexBytes)

                throws NoSuchAlgorithmException {

         MessageDigest md = MessageDigest.getInstance("SHA-1");

         md.update(dexBytes, 32, dexBytes.length - 32);

         byte[] newdt = md.digest();

         System.arraycopy(newdt, 0, dexBytes, 12, 20);

         String hexstr = "";

         for (int i = 0; i < newdt.length; i++) {

                hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16)

                              .substring(1);

         }

         System.out.println(hexstr);

  }

  

  

  private static void fixFileSizeHeader(byte[] dexBytes) {

  

  

         byte[] newfs = intToByte(dexBytes.length);

         System.out.println(Integer.toHexString(dexBytes.length));

         byte[] refs = new byte[4];

         for (int i = 0; i < 4; i++) {

                refs[i] = newfs[newfs.length - 1 - i];

                System.out.println(Integer.toHexString(newfs[i]));

         }

         System.arraycopy(refs, 0, dexBytes, 32, 4);

  }

  

  

  private static byte[] readFileBytes(File file) throws IOException {

         byte[] arrayOfByte = new byte[1024];

         ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();

         FileInputStream fis = new FileInputStream(file);

         while (true) {

                int i = fis.read(arrayOfByte);

                if (i != -1) {

                       localByteArrayOutputStream.write(arrayOfByte, 0, i);

                } else {

                       return localByteArrayOutputStream.toByteArray();

                }

         }

  }

  

  

}

3)在我们的程序中加载运行原来的apk文件,代码如下:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

public class shellApplication extends Application {

  

  

  private static final String appkey = "APPLICATION_CLASS_NAME";

  private String apkFileName;

  private String odexPath;

  private String libPath;

  

  

  protected void attachBaseContext(Context base) {

         super.attachBaseContext(base);

         try {

                File odex = this.getDir("payload_odex", MODE_PRIVATE);

                File libs = this.getDir("payload_lib", MODE_PRIVATE);

                odexPath = odex.getAbsolutePath();

                libPath = libs.getAbsolutePath();

                apkFileName = odex.getAbsolutePath() + "/payload.apk";

                File dexFile = new File(apkFileName);

                if (!dexFile.exists())

                       dexFile.createNewFile();

                // 读取程序classes.dex文件

                byte[] dexdata = this.readDexFileFromApk();

                // 分离出解壳后的apk文件已用于动态加载

                this.splitPayLoadFromDex(dexdata);

                // 配置动态加载环境

                Object currentActivityThread = RefInvoke.invokeStaticMethod(

                              "android.app.ActivityThread", "currentActivityThread",

                              new Class[] {}, new Object[] {});

                String packageName = this.getPackageName();

                HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(

                              "android.app.ActivityThread", currentActivityThread,

                              "mPackages");

                WeakReference wr = (WeakReference) mPackages.get(packageName);

                DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,

                              libPath, (ClassLoader) RefInvoke.getFieldOjbect(

                                            "android.app.LoadedApk", wr.get(), "mClassLoader"));

                RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",

                              wr.get(), dLoader);

  

  

         } catch (Exception e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

         }

  }

  

  

  public void onCreate() {

         {

  

  

                // 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。

                String appClassName = null;

                try {

                       ApplicationInfo ai = this.getPackageManager()

                                     .getApplicationInfo(this.getPackageName(),

                                                   PackageManager.GET_META_DATA);

                       Bundle bundle = ai.metaData;

                       if (bundle != null

                                     && bundle.containsKey("APPLICATION_CLASS_NAME")) {

                              appClassName = bundle.getString("APPLICATION_CLASS_NAME");

                       } else {

                              return;

                       }

                } catch (NameNotFoundException e) {

                       // TODO Auto-generated catch block

                       e.printStackTrace();

                }

  

  

                Object currentActivityThread = RefInvoke.invokeStaticMethod(

                              "android.app.ActivityThread", "currentActivityThread",

                              new Class[] {}, new Object[] {});

                Object mBoundApplication = RefInvoke.getFieldOjbect(

                              "android.app.ActivityThread", currentActivityThread,

                              "mBoundApplication");

                Object loadedApkInfo = RefInvoke.getFieldOjbect(

                              "android.app.ActivityThread$AppBindData",

                              mBoundApplication, "info");

                RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",

                              loadedApkInfo, null);

                Object oldApplication = RefInvoke.getFieldOjbect(

                              "android.app.ActivityThread", currentActivityThread,

                              "mInitialApplication");

                ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke

                              .getFieldOjbect("android.app.ActivityThread",

                                            currentActivityThread, "mAllApplications");

                mAllApplications.remove(oldApplication);

                ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke

                              .getFieldOjbect("android.app.LoadedApk", loadedApkInfo,

                                            "mApplicationInfo");

                ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke

                              .getFieldOjbect("android.app.ActivityThread$AppBindData",

                                            mBoundApplication, "appInfo");

                appinfo_In_LoadedApk.className = appClassName;

                appinfo_In_AppBindData.className = appClassName;

                Application app = (Application) RefInvoke.invokeMethod(

                              "android.app.LoadedApk", "makeApplication", loadedApkInfo,

                              new Class[] { boolean.class, Instrumentation.class },

                              new Object[] { false, null });

                RefInvoke.setFieldOjbect("android.app.ActivityThread",

                              "mInitialApplication", currentActivityThread, app);

  

  

                HashMap mProviderMap = (HashMap) RefInvoke.getFieldOjbect(

                              "android.app.ActivityThread", currentActivityThread,

                              "mProviderMap");

                Iterator it = mProviderMap.values().iterator();

                while (it.hasNext()) {

                       Object providerClientRecord = it.next();

                       Object localProvider = RefInvoke.getFieldOjbect(

                                     "android.app.ActivityThread$ProviderClientRecord",

                                     providerClientRecord, "mLocalProvider");

                       RefInvoke.setFieldOjbect("android.content.ContentProvider",

                                     "mContext", localProvider, app);

                }

                app.onCreate();

         }

  }

  

  

  private void splitPayLoadFromDex(byte[] data) throws IOException {

         byte[] apkdata = decrypt(data);

         int ablen = apkdata.length;

         byte[] dexlen = new byte[4];

         System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4);

         ByteArrayInputStream bais = new ByteArrayInputStream(dexlen);

         DataInputStream in = new DataInputStream(bais);

         int readInt = in.readInt();

         System.out.println(Integer.toHexString(readInt));

         byte[] newdex = new byte[readInt];

         System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt);

         File file = new File(apkFileName);

         try {

                FileOutputStream localFileOutputStream = new FileOutputStream(file);

                localFileOutputStream.write(newdex);

                localFileOutputStream.close();

  

  

         } catch (IOException localIOException) {

                throw new RuntimeException(localIOException);

         }

  

  

         ZipInputStream localZipInputStream = new ZipInputStream(

                       new BufferedInputStream(new FileInputStream(file)));

         while (true) {

                ZipEntry localZipEntry = localZipInputStream.getNextEntry();

                if (localZipEntry == null) {

                       localZipInputStream.close();

                       break;

                }

                String name = localZipEntry.getName();

                if (name.startsWith("lib/") && name.endsWith(".so")) {

                       File storeFile = new File(libPath + "/"

                                     + name.substring(name.lastIndexOf('/')));

                       storeFile.createNewFile();

                       FileOutputStream fos = new FileOutputStream(storeFile);

                       byte[] arrayOfByte = new byte[1024];

                       while (true) {

                              int i = localZipInputStream.read(arrayOfByte);

                              if (i == -1)

                                     break;

                              fos.write(arrayOfByte, 0, i);

                       }

                       fos.flush();

                       fos.close();

                }

                localZipInputStream.closeEntry();

         }

         localZipInputStream.close();

  

  

  }

  

  

  private byte[] readDexFileFromApk() throws IOException {

         ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream();

         ZipInputStream localZipInputStream = new ZipInputStream(

                       new BufferedInputStream(new FileInputStream(

                                     this.getApplicationInfo().sourceDir)));

         while (true) {

                ZipEntry localZipEntry = localZipInputStream.getNextEntry();

                if (localZipEntry == null) {

                       localZipInputStream.close();

                       break;

                }

                if (localZipEntry.getName().equals("classes.dex")) {

                       byte[] arrayOfByte = new byte[1024];

                       while (true) {

                              int i = localZipInputStream.read(arrayOfByte);

                              if (i == -1)

                                     break;

                              dexByteArrayOutputStream.write(arrayOfByte, 0, i);

                       }

                }

                localZipInputStream.closeEntry();

         }

         localZipInputStream.close();

         return dexByteArrayOutputStream.toByteArray();

  }

  

  

  // //直接返回数据,读者可以添加自己解密方法

  private byte[] decrypt(byte[] data) {

         return data;

  }



展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部