2019独角兽企业重金招聘Python工程师标准>>>

一、加壳技术原理

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

PC exe的加壳原理如下:

二、android apk加壳实现

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

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

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

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

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

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

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

具体实现如下:

(1)修改原来apk的AndroidMainfest.xml文件,假如原来apk的AndroidMainfest.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"/>

是原来的apk的application名称。

(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;

}

转载于:https://my.oschina.net/jesonzhang/blog/419849

android apk 防止反编译技术加壳技术(转)相关推荐

  1. 转: android apk 防止反编译技术(1~5连载)

    转: android apk 防止反编译技术 做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习. ...

  2. android apk 防止反编译技术第三篇-加密apk

    经过了忙碌的一周终于有时间静下来写点东西了,我们继续介绍android apk防止反编译技术的另一种方法.前两篇我们讲了加壳技术和运行时修改字节码,如果有不明白的可以查看我的博客的前两篇中关于这两种技 ...

  3. android apk 防止反编译技术第三篇-加密

    经过了忙碌的一周终于有时间静下来写点东西了,我们继续介绍android apk防止反编译技术的另一种方法.前两篇我们讲了加壳技术(http://my.oschina.net/u/2323218/blo ...

  4. Android APK包反编译详细步骤教程

    下载安卓apk包反编译软件: https://download.csdn.net/download/LordForce/87485642 1. 解压 dex2jar-2.0.rar 文件. 2. 将 ...

  5. Android apk破解反编译应用-EasyDump

    大家好,今天与大家分享一个很easy的反编译应用-EasyDump,它主要有以下优点: 1.支持加固后的编译,主包包括360加固.百度加固.腾讯加固.爱加密.梆梆加固. 2.不需要手机root就可以反 ...

  6. android apk 文件反编译

    Android 反编译APK 的好处 第一,当自己代码加混淆的时候,代码找不到CLASS 可以反编译下看看,到底是没有屏蔽,还是有其他的问题 第二,参考下别人的代码是怎么写的 使用工具 就一个 htt ...

  7. 对Android APK文件反编译查看其源码

    概述 作为android的开发者,相信你会有那么一刻,比如,对于手机中某一款App,有一项功能或者效果你蛮喜欢的,想学习一下它具体是怎么实现的.追求高一点的话,我想深究它这个App的项目架构,本着一探 ...

  8. 新发的日常小实验——使用.NET Reactor对.NET程序进行加壳保护(反编译、加壳、混淆)

    文章目录 一.前言 二.关于.Net Rector 三..Net Rector下载 四..Net Rector的简单使用 1.主界面 2.选择程序及(.exe) 3.Quick Settings(快速 ...

  9. Android APK加壳技术方案----代码实现

    本文章由Jack_Jia编写,转载请注明出处. 文章链接:http://blog.csdn.net/jiazhijun/article/details/8746917 作者:Jack_Jia    邮 ...

最新文章

  1. 深入浅出解释FFT(七)——fft求频谱图和功率谱密度图
  2. r语言中mpg数据_R语言数据筛选整理包dplyr
  3. 【数据结构与算法】之深入解析“二叉搜索树中的插入操作”的求解思路与算法示例
  4. VS2005 ASP.NET2.0安装项目的制作(包括数据库创建、站点创建、IIS属性修改、Web.Config文件修改)
  5. WSARecv参数lpNumberOfBytesRecvd的一个变态问题
  6. 基础知识回顾——通用序列操作
  7. floor mod sqlserver_ORACLE和SQLServer-SQL语句的区别
  8. 淮阴工学院计算机系在哪个校区,2021年淮阴工学院有几个校区,大一新生在哪个校区...
  9. 题164.pta数据结构题集-04-树7 二叉搜索树的操作集 (30 分)
  10. 宝塔面板6.9.0一键破解脚本
  11. Grubbs准则建模与分析 C与Matlab实现
  12. 《软件工程导论》知识点期末复习整理
  13. 关于google拼音输入法的坑爹问题-IE浏览器浏览网页蓝屏等问题
  14. 十六进制和字符串的转换
  15. java画一个八卦_View绘制系列(9)-Canvas八卦图绘制
  16. Spark 练习之疫情分析
  17. HO-PLGA-COOR,酯封端聚(D,L-丙交酯-co-乙交酯)共聚物
  18. metasploit中用shodan模块进行网络摄像头查找
  19. 老卫带你学---DDSM乳腺癌数据研究
  20. 如何从瘫痪windows系统里面找到原来的IP地址设置

热门文章

  1. PHP语法像C,PHP编程语法的三个魅力之处
  2. jdbctemplate mysql 配置_Spring Boot 初级入门教程(十四) —— 配置 MySQL 数据库和使用 JdbcTemplate 测试...
  3. 便利删除_知名便利店凉了?!刚刚道歉
  4. python压缩教程_无需压缩软件,用python帮你操作压缩包
  5. Vue遍历对象,数组,v-if、v-if-else、v-else
  6. 20200329:K 个一组翻转链表(leetcode25)
  7. 20191019:(leetcode习题)第K个语法符号
  8. php读取西门子plc_AB PLC和西门子PLC之间需要交换数据
  9. mysql怎么用sb文件_初识mysql数据库
  10. mysql某个表被行锁了_一文搞懂MySQL行锁、表锁、间隙锁详解