(原创)Java解压odex和oat 代码
aide吧
全部回复
仅看楼主
level 7
ha1vk 楼主
目前解压这两种文件的方法都是用apktool,然而还有一种简单的方法。
odex和oat是dex文件的一种优化,但是解压他们的方法是相同的。
首先用WinHex打开一个odex文件,我们可以看到dex 035这个字符串,其实这个地方就是dex文件开始的位置,根据dex文件的数据结构可知向后再偏移32个字节就是dex文件的大小。于是从头的偏移位置向后取出dex大小个字节就是dex文件的内容。
我们再来看boot.oat这个文件,打开后搜索字符串dex,找到dex 035,一个boot.oat中有多个dex文件头,说明boot.oat中有多个dex文件,而普通oat文件里一般只有一个。我们可以用同样的方法将他们解压出来
于是我们可以写一个java程序,用于解压odex和oat文件。基本思想是在内存中搜索dex文件头,记录下偏移。再获取dex文件大小。然后获取这块区域的字节,写出文件。
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class DEODEX {
/**
* 从内存中搜索数据,并返回在内存中的偏移
***/
public static int findPos(MappedByteBuffer data, int offset, byte[] found) {
for (int i = offset; i < data.capacity(); i++)
{
boolean bFound = true;
for (int j = 0; j < found.length; j++) {
bFound = bFound && data.get(i + j) == found[j];
}
if (bFound) {
return i;
}
}
return -1;
}
private static void write_to_file(MappedByteBuffer buffer, int offset, int dexsize, int file_count, String out)
throws IOException {
RandomAccessFile file = new RandomAccessFile(String.format("%s%02d.dex", out, file_count), "rw");
FileChannel channel = file.getChannel();
byte[] data = new byte[dexsize];
buffer.position(offset);
buffer.get(data);
ByteBuffer bw = ByteBuffer.wrap(data);
channel.write(bw);
channel.close();
file.close();
}
public static void oat2dex(String in, String out) throws IOException {
RandomAccessFile raf = new RandomAccessFile(in, "rw");
// 内存映射文件
FileChannel channel = raf.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());
int file_count = 0;
int length = (int) raf.length();
//dex头
byte[] magic = { 0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00 };
int offset = 0;
while (offset != -1) {
offset = findPos(buffer, offset + 8, magic);
if (offset == -1) { // 未发现dex magic
break;
}
int dexsize = (buffer.get(offset + 35) << 24) | (buffer.get(offset + 34) & 0xff) << 16
| (buffer.get(offset + 33) & 0xff) << 8 | (buffer.get(offset + 32) & 0xff);
if (offset + dexsize > length)
continue;
write_to_file(buffer, offset, dexsize, ++file_count, out);
}
channel.close();
raf.close();
}
}
调用这个方法后,我们得到了dex文件
OK,这是boot.oat中的文件,随便拿一个出来测试一下。转换成jar,用jdgui打开
大功告成
2017年02月02日 11点02分 1
level 12
涨姿势了,感谢楼主的帖子[哈哈]
2017年02月02日 13点02分 3
level 11
感谢分享,还有一个叫oat2dex的开源工具,也是类似的实现,可以用它配合jadx实现反编译。
2017年02月02日 13点02分 5
level 10
涨姿势了,感谢楼主的帖子[哈哈]
2017年02月02日 13点02分 6
level 12
不错,很有想法[滑稽]
2017年02月02日 14点02分 8
level 15
谢谢分享
2017年02月02日 16点02分 9
level 12
谢谢楼主的思路,我去试试先[滑稽]
--------------
2017年02月03日 17点02分 13
level 13
厉害了
2017年02月04日 00点02分 20
level 13
66666666666666666666
2017年02月04日 00点02分 21
level 7
厉害了
2017年02月06日 15点02分 26
level 10
[阴险]突然找到以前写插件时的oat转dex的
,直接一个个读,java版,
package FormatFa;
import java.io.*;
import java.util.*;
public class DexDump
{
public static byte[] magic={0x64,0x65,0x78,0xa};
public static void dump(File in) throws FileNotFoundException, IOException
{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(in));
byte[] buff=new byte[4];
int num=0;
while(bis.read(buff)!=-1)
{
if(Arrays.equals(buff,magic))
{
File out=null;
if(num!=0)
out=new File(in.getParentFile(),in.getName()+num+".dex");
else
out=new File(in.getParentFile(),in.getName()+".dex");
System.out.println("保存到:"+out.getAbsolutePath());
OutputStream os=new FileOutputStream(out);
//复制头
os.write(magic);
byte[] head=new byte[32-4];
bis.read(head);
os.write(head);
bis.read(buff);
int dexSize=bytesToInt(buff,0);
System.out.println("Dex size:"+dexSize);
os.write(buff);
byte[] dex2=new byte[dexSize-32];
bis.read(dex2);
os.write(dex2);
os.close();
num+=1;
}
}
}
public static int bytesToInt(byte[] des, int offset)
{
int value;
value = (int) (des[offset] & 0xff | ((des[offset + 1] & 0xff) << 8) | ((des[offset + 2] & 0xff) << 16) | (des[offset + 3] & 0xff) << 24);
return value;
}
}
2017年04月29日 22点04分 28
level 10
还有个C版的,[阴险]好像那时2有bug,那时写来放在终端模拟器运行[阴险]
2017年04月29日 22点04分 29
level 11
吃瓜群众[滑稽][滑稽]
2017年05月01日 00点05分 30
1