首页
社区
课程
招聘
5
[原创]Apk伪加密实现与破解JAVA源码
发表于: 2013-7-5 18:52 43785

[原创]Apk伪加密实现与破解JAVA源码

2013-7-5 18:52
43785

伪加密的apk有那么难么?了解原理就没啥可怕的。放出java代码,python的早就有了。

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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package com.rover12421.apkutil;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.zip.ZipError;
 
import static com.rover12421.apkutil.ZipConstants.*;
 
public class ApkUtilTool {
     
    private FileChannel ch; // channel to the zipfile
    private FileChannel fc;
 
    /**
     * 修复zip伪加密状态的Entry
     * @param inZip
     * @param storeZip
     * @throws IOException
     */
    public void FixEncryptedEntry(File inZip, File fixZip) throws IOException {
        changEntry(inZip, fixZip, true);
    }
     
    /**
     * 修复zip伪加密状态的Entry
     * @param inZip
     * @param storeZip
     * @throws IOException
     */
    public void FixEncryptedEntry(String inZip, String fixZip) throws IOException {
        FixEncryptedEntry(new File(inZip), new File(fixZip));
    }
     
    /**
     * 修改zip的Entry为伪加密状态
     * @param inZip
     * @param storeZip
     * @throws IOException
     */
    public void ChangToEncryptedEntry(File inZip, File storeZip) throws IOException {
        changEntry(inZip, storeZip, false);
    }
     
    /**
     * 修改zip的Entry为伪加密状态
     * @param inZip
     * @param storeZip
     * @throws IOException
     */
    public void ChangToEncryptedEntry(String inZip, String storeZip) throws IOException {
        ChangToEncryptedEntry(new File(inZip), new File(storeZip));
    }
     
    /**
     * 更改zip的Entry为伪加密状态
     * @param inZip
     * @param storeZip
     * @param fix   ture:修复伪加密 false:更改到伪加密
     * @throws IOException
     */
    private void changEntry(File inZip, File storeZip, boolean fix) throws IOException {
        FileInputStream fis = new FileInputStream(inZip);
        FileOutputStream fos = new FileOutputStream(storeZip);
         
        byte[] buf = new byte[10240];
        int len;
        while ((len = fis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
         
        ch = fis.getChannel();
        fc = fos.getChannel();
         
        changEntry(fix);
         
        ch.close();
        fc.close();
         
        fis.close();
        fos.close();
    }
     
    // Reads zip file central directory. Returns the file position of first
    // CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
    // then the error was a zip format error and zip->msg has the error text.
    // Always pass in -1 for knownTotal; it's used for a recursive call.
    private void changEntry(boolean fix) throws IOException {
        END end = findEND();
         
        if (end.cenlen > end.endpos)
            zerror("invalid END header (bad central directory size)");
        long cenpos = end.endpos - end.cenlen;     // position of CEN table
 
        // Get position of first local file (LOC) header, taking into
        // account that there may be a stub prefixed to the zip file.
        long locpos = cenpos - end.cenoff;
        if (locpos < 0)
            zerror("invalid END header (bad central directory offset)");
 
        // read in the CEN and END
        byte[] cen = new byte[(int)(end.cenlen + ENDHDR)];
        if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
            zerror("read CEN tables failed");
        }
 
        int pos = 0;
        int limit = cen.length - ENDHDR;
        while (pos < limit) {
            if (CENSIG(cen, pos) != CENSIG)
                zerror("invalid CEN header (bad signature)");
            int method = CENHOW(cen, pos);
            int nlen   = CENNAM(cen, pos);
            int elen   = CENEXT(cen, pos);
            int clen   = CENCOM(cen, pos);
             
            if (fix) {
                if ((CEN***(cen, pos) & 1) != 0) {
                    byte[] name = Arrays.copyOfRange(cen, pos + CENHDR, pos + CENHDR + nlen);
                    System.out.println("Found the encrypted entry : " + new String(name) + ", fix...");
                    //b[n] & 0xff) | ((b[n + 1] & 0xff) << 8
                    cen[pos+8] &= 0xFE;
//                  cen[pos+8] ^= CEN***(cen, pos) % 2;
//                  cen[pos+8] ^= cen[pos+8] % 2;
//                    zerror("invalid CEN header (encrypted entry)");
                }
            } else {
                if ((CEN***(cen, pos) & 1) == 0) {
                    byte[] name = Arrays.copyOfRange(cen, pos + CENHDR, pos + CENHDR + nlen);
                    System.out.println("Chang the entry : " + new String(name) + ", Encrypted...");
                    //b[n] & 0xff) | ((b[n + 1] & 0xff) << 8
                    cen[pos+8] |= 0x1;
//                    zerror("invalid CEN header (encrypted entry)");
                }
            }
             
             
            if (method != METHOD_STORED && method != METHOD_DEFLATED)
                zerror("invalid CEN header (unsupported compression method: " + method + ")");
            if (pos + CENHDR + nlen > limit)
                zerror("invalid CEN header (bad header size)");
             
            // skip ext and comment
            pos += (CENHDR + nlen + elen + clen);
        }
         
        writeFullyAt(cen, 0, cen.length, cenpos);
         
        if (pos + ENDHDR != cen.length) {
            zerror("invalid CEN header (bad header size)");
        }
    }
     
    // Reads len bytes of data from the specified offset into buf.
    // Returns the total number of bytes read.
    // Each/every byte read from here (except the cen, which is mapped).
    final long readFullyAt(byte[] buf, int off, long len, long pos)
        throws IOException
    {
        ByteBuffer bb = ByteBuffer.wrap(buf);
        bb.position(off);
        bb.limit((int)(off + len));
        return readFullyAt(bb, pos);
    }
 
    private final long readFullyAt(ByteBuffer bb, long pos)
        throws IOException
    {
        synchronized(ch) {
            return ch.position(pos).read(bb);
        }
    }
     
    final long writeFullyAt(byte[] buf, int off, long len, long pos)
            throws IOException
        {
            ByteBuffer bb = ByteBuffer.wrap(buf);
            bb.position(off);
            bb.limit((int)(off + len));
            return writeFullyAt(bb, pos);
        }
     
    private final long writeFullyAt(ByteBuffer bb, long pos)
            throws IOException
    {
        synchronized(fc) {
            return fc.position(pos).write(bb);
        }
    }
     
    // Searches for end of central directory (END) header. The contents of
    // the END header will be read and placed in endbuf. Returns the file
    // position of the END header, otherwise returns -1 if the END header
    // was not found or an error occurred.
    private END findEND() throws IOException
    {
        byte[] buf = new byte[READBLOCKSZ];
        long ziplen = ch.size();
        long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0;
        long minPos = minHDR - (buf.length - ENDHDR);
 
        for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR))
        {
            int off = 0;
            if (pos < 0) {
                // Pretend there are some NUL bytes before start of file
                off = (int)-pos;
                Arrays.fill(buf, 0, off, (byte)0);
            }
            int len = buf.length - off;
            if (readFullyAt(buf, off, len, pos + off) != len)
                zerror("zip END header not found");
 
            // Now scan the block backwards for END header signature
            for (int i = buf.length - ENDHDR; i >= 0; i--) {
                if (buf[i+0] == (byte)'P'    &&
                    buf[i+1] == (byte)'K'    &&
                    buf[i+2] == (byte)'\005' &&
                    buf[i+3] == (byte)'\006' &&
                    (pos + i + ENDHDR + ENDCOM(buf, i) == ziplen)) {
                    // Found END header
                    buf = Arrays.copyOfRange(buf, i, i + ENDHDR);
                    END end = new END();
                    end.endsub = ENDSUB(buf);
                    end.centot = ENDTOT(buf);
                    end.cenlen = ENDSIZ(buf);
                    end.cenoff = ENDOFF(buf);
                    end.comlen = ENDCOM(buf);
                    end.endpos = pos + i;
                    if (end.cenlen == ZIP64_MINVAL ||
                        end.cenoff == ZIP64_MINVAL ||
                        end.centot == ZIP64_MINVAL32)
                    {
                        // need to find the zip64 end;
                        byte[] loc64 = new byte[ZIP64_LOCHDR];
                        if (readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
                            != loc64.length) {
                            return end;
                        }
                        long end64pos = ZIP64_LOCOFF(loc64);
                        byte[] end64buf = new byte[ZIP64_ENDHDR];
                        if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
                            != end64buf.length) {
                            return end;
                        }
                        // end64 found, re-calcualte everything.
                        end.cenlen = ZIP64_ENDSIZ(end64buf);
                        end.cenoff = ZIP64_ENDOFF(end64buf);
                        end.centot = (int)ZIP64_ENDTOT(end64buf); // assume total < 2g
                        end.endpos = end64pos;
                    }
                    return end;
                }
            }
        }
        zerror("zip END header not found");
        return null; //make compiler happy
    }
     
    static void zerror(String msg) {
        throw new ZipError(msg);
    }
     
 // End of central directory record
    static class END {
        int  disknum;
        int  sdisknum;
        int  endsub;     // endsub
        int  centot;     // 4 bytes
        long cenlen;     // 4 bytes
        long cenoff;     // 4 bytes
        int  comlen;     // comment length
        byte[] comment;
 
        /* members of Zip64 end of central directory locator */
        int diskNum;
        long endpos;
        int disktot;
         
        @Override
        public String toString() {
            return "disknum : " + disknum + "\n" +
                    "sdisknum : " + sdisknum + "\n" +
                    "endsub : " + endsub + "\n" +
                    "centot : " + centot + "\n" +
                    "cenlen : " + cenlen + "\n" +
                    "cenoff : " + cenoff + "\n" +
                    "comlen : " + comlen + "\n" +
                    "diskNum : " + diskNum + "\n" +
                    "endpos : " + endpos + "\n" +
                    "disktot : " + disktot;
        }
    }
}

[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 5
支持
分享
赞赏记录
参与人
雪币
留言
时间
心游尘世外
为你点赞~
2024-5-31 05:31
QinBeast
为你点赞~
2024-5-31 05:24
飘零丶
为你点赞~
2024-4-3 00:38
shinratensei
为你点赞~
2024-2-14 01:31
PLEBFE
为你点赞~
2023-3-7 00:31
最新回复 (14)
雪    币: 296
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
good job.谢谢分享!
2013-7-5 20:06
0
雪    币: 33
活跃值: (145)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
gooooooooooooooooood  job!
2013-7-5 21:25
0
雪    币: 80
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
大神啊啊
2013-7-6 13:14
0
雪    币: 738
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
回复mark一下,留着备用
2013-7-11 22:45
0
雪    币: 257
活跃值: (105)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
mark一下
2013-7-17 10:48
0
雪    币: 189
活跃值: (192)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
apktool wip2.0已经支持伪加密的apk解压.他用了第三方库org.apache.commons来处理
这个库有一个方法:
useEncryption(false);
可以设置是否检测加密信息的
2013-7-21 13:30
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
楼主你好,在Android4.3版本及之后的版本安装伪加密的apk失败。跟了了下发现java.util.zip.ZipEntry类中的构造方法已经被修改了,以前的这一句“it.seek(10);”修改为:
          it.seek(8);
        int gpbf = it.readShort() & 0xffff;

        if ((gpbf & ZipFile.GPBF_UNSUPPORTED_MASK) != 0) {
            throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
        }
有没有办法在Android4.3及之后的版本安装伪加密apk不报错呢。
2014-3-27 16:44
0
雪    币: 189
活跃值: (192)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
没有遇到这样的样本,你可以发样本给我看看.
2014-3-28 10:58
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
嗯,谢谢楼主!附件TestEncrypt.zip 中TestEncrypt.apk是未伪加密的,TestEncrypt_ent.apk是伪加密的apk,在Android4.3手机中安装抛的异常是“java.util.zip.ZipException: Invalid General Purpose Bit Flag: 2057”,我自己查看了下,在Android源码中java.util.zip包中的ZipEntry.java类导致的。附件中我附了ZipEntry.java类,(17)是Android4.2.2的源码,位置在362行;(18)是Android4.3的已经被修改过了,在360行。
上传的附件:
2014-3-30 18:43
0
雪    币: 9
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
留着备用,谢谢!!!
2014-4-1 16:27
0
雪    币: 189
活跃值: (192)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
看来是4.3校验了zip的伪加密,那这样4.3以后的系统就不能利用这个bug了
2014-4-1 16:42
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
谢谢楼主啦,看来得在学习,新的Android安全措施了。
2014-4-4 08:49
0
雪    币: 200
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
多谢啦。最近一个项目里读apk包名和版本,遇到几个这种伪加密的APK。先回复后收货,
2014-4-16 15:21
0
雪    币: 209
活跃值: (45)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
看不懂代码啊
能否用文字描述下apk的伪加密原理
2015-1-16 17:03
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册