3

I need to read a file around 2 MB into a byte array.

var fis = Java.use("java.io.FileInputStream");
var file = fis.$new("/data/local/file.txt");
var fileBytes = new Uint8Array(file.available());
var x;
while ((x = file.read()) != -1)
    fileBytes.push(x);

I'm using the code above but it's reading ~100 kb/s which takes ~20 seconds in my case.

I also tried with ByteArrayOutputStream with 1, 4, 16kb buffers but result isn't any different.

I believe a native Java function which takes Inputfilestream and returns a bytearray will fix my issue, but I couldn't find any.

How can I make this process faster?

I'm actually trying to use it with okio.ByteString which has a .read() method, but I can't access it from Frida. It's undefined.

2 Answers 2

2

The reason why your code is slow is not Frida but your code. Your code would be also slow when executed in plain J2SE Java.

The simple reason is because you are reading the file byte by byte using an unbuffered FileInputStream which is the worst way you can read a file. Typically IO operations work on blocks of at least 4096 bytes (a typical block site in a file-system).

Therefore a simple modification would be to wrap the FileInputStream in an BufferedInputStream:

var fis = Java.use("java.io.FileInputStream");
var bis = Java.use("java.io.BufferedInputStream");
var file = bis.$new(fis.$new("/data/local/file.txt"));
...

However this does not the read calls which will take significant time. Therefore the following code reads byte blocks and procesess them. This should be a lot faster:

var fis = Java.use("java.io.FileInputStream");
var file = fis.$new("/data/local/file.txt");
var fileBytes = new Uint8Array(file.available());
const buffer = Java.array('byte', new Array(4096).fill(0));
var x;
while ((x = file.read(buffer)) != -1) {
    for (int i = 0; i < x; i++) {
        fileBytes.push(buffer[i]);
    } 
}

The fasted way would be if your app would contain Java code that directly read a file to a byte array. The less Frida JavaScript calls you have to perform the faster the result.

Therefore check if your app contains a libraray like Apache common IO. Then you could directly execute org.apache.commons.io.IOUtils.toByteArray(java.io.InputStream).

1
  • Thanks for your answer, however I already tried with buffer previously and speed is almost the same. I had not tried Java.array but it doesn't seem to work too. I've posted an answer which worked for me
    – Cagri
    Jan 11, 2021 at 8:20
0

Unfortunately using buffer still didn't help me.

I guess doing JS -> Java -> JS -> Java takes a bit long. I have found this solution:

var Files = Java.use("java.nio.file.Files");
var Paths = Java.use("java.nio.file.Paths");
var URI = Java.use("java.net.URI");

var fileBytes = Files.readAllBytes(Paths.get(URI.create("file:///sdcard/file.jpg")));

It does reading in Java and returns a byte array which is pretty fast.

3
  • Indeed I forgot that Files.readAllBytes is a Java7 function and is therefore available on Android. But how to you convert this to an Uint8Array? I would assume that converting it byte for byte is still a very slow operation.
    – Robert
    Jan 11, 2021 at 8:29
  • @Robert I have checked and yes, new Uint8Array(fileBytes) is still slow. typeof fileBytes shows it as an object, so I'm not exactly sure about the type, and if we even need to convert it, because the converted one looks same. I'm actually passing this array (without converting) to okio.ByteString to use it in the Android application, which works pretty fast
    – Cagri
    Jan 11, 2021 at 11:39
  • This is not a forum; please use the answer box only for answers to the asked question.
    – Igor Skochinsky
    Jan 11, 2021 at 18:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.