markusfisch / BinaryEye

Yet another barcode scanner for Android
https://play.google.com/store/apps/details?id=de.markusfisch.android.binaryeye
MIT License
1.51k stars 121 forks source link

Crashes after a few quick scans #166

Closed OverkillGuy closed 3 years ago

OverkillGuy commented 3 years ago

Hello! I'm using your great little app for attempting something crazy: data exfiltration via QR codes. See qrxfil for the project details, but suffice to say I am aiming to scan dozens of QR codes of ~1KB payload in quick succession.

Issue: App crashes (back to android home) after ~15 to 20 QR code scans (in ~20 seconds)

Expected behaviour: Scan hundreds of QR codes without crash (at a rate of 1 to 2 per second)

Reproduction steps

Initially using BinaryEye version 1.37.0, reproduced on 1.39.0 (F-Droid)

Conjectures The crash seems to happen after I scan a few dozen codes, each of which shows a Toast containing the 1KB of decoded text (base64 strings), all overlapping with each other. It is possible the Toast creation logic gets an error when drawing one too many, doesn't handle it, and crashes? If so, disabling the Toast of QR contents would make the issue disappear. Worth investigating?

Note I have not captured any logs pointing to reason for homepage, and haven't done any investigation as to what's wrong, am just reporting the issue naively for now. Expect more digging.

Am happy providing logs if I can, and can share sample QR codes that trigger issues.

markusfisch commented 3 years ago

Hello, and thanks for submitting this issue! 👍 Off the top of my head, I have no idea what's going wrong here unfortunately. But this shouldn't happen for sure!

Regarding your guess about the Toast, I am not sure if this is the cause - at least I can't see how this could be the cause just by looking at the code. But disabling the Toast would sure be a good idea for that use case. Also, there's a 500ms delay after recognizing a code at the moment. Two things that should better be configurable for your use case I guess.

Unfortunately, I can't see anything related to the crash in Google's crash logs. And since it's not so easy to reproduce this crash, I'd really appreciate it if you could capture the logcat output. You'd need the adb tool for it (comes with Android Studio, the bare Android SDK, and may be available in your package manager, homebrew, apt, etc), and to enable "adb debugging" on your device.

Then you can do:

$ adb logcat

And look for errors. If you want to see the messages in color, you could use pidcat:

$ pidcat de.markusfisch.android.binaryeye

About your use case and your project: what do you do with all the scanned codes in Binary Eye? How do you export them?

Off topic, do you know QR Codes have a binary input mode? This way you could save up to 2,953 bytes in a single QR Code (also that would mean the QR Code would become harder to read because of its size). A quick and easy way to find a sweet spot would be to use qrencode (also available in your package manager) because it can produce QR Codes with binary content:

$ qrencode -8 -o outfile < infile

And Binary Eye can read barcodes with binary content, of course.

OverkillGuy commented 3 years ago

Right!

Logs: Culled a bit to be relevant time-wise. That's a lot of noise but not a lot of signal I can see. The back-to-homepage crash I witnessed was at 22:54:10, but may have crashed internally a few seconds earlier (timeouts, escalation of kill etc).

adb2.log

Best I can see is:

03-07 22:54:08.221   424  1080 E SurfaceFlinger: Failed to find layer (SurfaceView - de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity#0) in layer parent (no-parent).
03-07 22:54:08.227  3387 14521 E Camera2Client: notifyError: Error condition 0 reported by HAL, requestId -1
03-07 22:54:08.227  3851  4317 I ActivityManager: Process de.markusfisch.android.binaryeye (pid 20553) has died: fore TOP 
03-07 22:54:08.228  3851  4317 W ActivityManager: Force removing ActivityRecord{32d8097 u0 de.markusfisch.android.binaryeye/.activity.CameraActivity t18954}: app died, no saved state
03-07 22:54:08.239  3851  4036 W InputDispatcher: channel 'fa90e1c de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
03-07 22:54:08.239  3851  4036 E InputDispatcher: channel 'fa90e1c de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
03-07 22:54:08.298   424  1080 E BufferQueueProducer: [SurfaceView - de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity#0] queueBuffer: BufferQueue has been abandoned

I don't know where to go from there, but I am happy posting a few dozen Chunks for issue reproductibility.


Currently I export scans to file in semicolon CSVs, and do a little awk magic to export line-delimited decoded chunks, but I will switch to HTTP, which means my "receiver" can live-read chunks off scanner, show progress and report "missing chunk 365 and 450".

Regarding my project, I saw binary mode, and am excited to get to it, but my cheapo prototyping in bash had issue in decoder when exporting CSV of binary so I went with base64 as a stopgap, as that works everywhere and is readable as ASCII. I look forward to using binary QR code to triple my payload (currently 1KB of base64 ~=750 bytes decoded). I also noticed the big QR codes like version 40 was a lot more prone to decoding issues in binaryeye (finding EAN codebar instead, guessing something about a low-pass filter early in the pipeline?) so I limited myself to 1KB payload ~= QR version 26-ish?

Thank you for your interest in my crazy endeavour!

doronbehar commented 3 years ago

I too experience crashes - can't use the app at all:

https://user-images.githubusercontent.com/10998835/110359993-12fb9900-8036-11eb-9564-380afea6966e.mp4

markusfisch commented 3 years ago

@doronbehar This is a most probably a known RenderScript issue 😬 Sorry for that!

I guess you installed the app with F-Droid? Because F-Droid's build is broken for some devices running Android 6 🙈 Find the details in https://github.com/markusfisch/BinaryEye/issues/113

A possible workaround would be to install the app either from GitHub or Google Play.

markusfisch commented 3 years ago

@OverkillGuy Thanks a lot for the logcat! 👍 And yes, I know, it's a lot of noise 😬

I think the snippet you posted is just the aftermath of the real problem - before that there are a lot of camera errors 🤔 But I can't see exactly what's causing the error yet either.

So I took qrxfil and tried to reproduce the error by scanning a file from 43 parts. I tried two different devices (Pixel 2 on Android 11 and a Nextbit Robin on Android 7) and made multiple runs but the app didn't crash for me. I used the latest version from Google Play (1.39.0). May I ask what device and Android version you are experiencing the crashes on?

Now, to rule out the Toast hypothesis I just added a new setting to disable that Toast in when scanning continuously: https://github.com/markusfisch/BinaryEye/commit/47f95fc67a7c690eeefb7115b957680787c9e035

I don't know if you're interested, but if you could build and run the app from source, you could try that right away. You'd just need the Android SDK (or Android Studio). Then, it would be a simple as cloning the app and running make 😉 This should build and install the app if the Android SDK is available in $ANDROID_HOME. The debug app can be installed beside the release version, so you wouldn't loose anything.

If you're not interested in building the app yourself, I will happily publish a beta version for you to try that change.


PS: Just because I have now taken a closer look at scripts/qrexfil, which I used to test the app, and because I couldn't stop myself from tinkering with the idea, here's my version of it 😬 I hope you can forgive me for having to fiddle with it. scripts/qrexfil doesn't work on BSD (where I'm on) and this was the reason I started playing with it 😉

So, this works with GNU and BSD tools and is zipping the file before it's transferred. Also I used the 8-bit mode I mentioned before to make the QR Codes have as few modules as possible.

#!/usr/bin/env bash
(($# < 1)) && {
    echo "usage: ${0##*/} FILE..."
    exit
}
for F
do
    [ -r "$F" ] || {
        echo "file not found: $F" >&2
        exit 1
    }
    OUTDIR="$F.qrxf"
    mkdir "$OUTDIR" || exit $?
    BASENAME=${F##*/}
    gzip -c "$F" | (
        cd "$OUTDIR" || exit $?
        split -b "${CHUNK_SIZE:-1024}"
        for CF in x*
        do
            (
                echo "$BASENAME"
                echo "$CF"
                cat "$CF"
            ) | qrencode -8 -o "${CF}.png"
            rm "$CF"
        done
    )
done

Binary Eye would export binary data as a Hex Dump, which can be used with xxd to recover the file. Here's a simple test script that would use the script above (which I called just qrxf) to split and recombine a given file just like you'd do with data from Binary Eye:

 #!/usr/bin/env bash
readonly TEST_FILE=${TEST_FILE:-payload}
[ -r "$TEST_FILE" ] ||
    dd if=/dev/urandom of="$TEST_FILE" count="${COUNT:-8}" || exit $?
sed -e 's/qrencode.*/xxd -p | tr -d "\\n"; echo/' qrxf |
    bash /dev/stdin "$TEST_FILE" | while read -r
do
    echo "$REPLY" | xxd -r -p | (
        read -r BASENAME
        CHUNK_DIR="${BASENAME}.qrxt"
        [ -d "$CHUNK_DIR" ] || mkdir "$CHUNK_DIR" || exit $?
        read -r CHUNKNAME
        cat > "$CHUNK_DIR/$CHUNKNAME"
    )
done
rm -rf "${TEST_FILE}.qrxf"
for D in *.qrxt
do
    [ -d "$D" ] || continue
    RESTORED="restored_${D%.qrxt}"
    cat "$D/x"* | gzip -d > "$RESTORED"
    rm -rf "$D"
    diff "$TEST_FILE" "$RESTORED" && {
        echo "$TEST_FILE successfully restored"
        rm "$RESTORED"
    }
done
OverkillGuy commented 3 years ago

Am running a Nexus 5X (from 2016, getting a bit senile, worried the root cause is running out of RAM somehow) on LineageOS 15.1 (Android Oreo 8.1) non-rooted with F-Droid package version 1.39.0.

Here is a full unredacted logcat, crash around 00:14:35. I mostly truncated before because it looked like logcat captured stuff from my local wifi to bluetooth MACs, but meh.

For completeness here is the 581 chunk file I tried to scan qrxfil-0.1.0-amd64.deb.tar.gz, maybe the files matter...

Tried building, but the Ubuntu (pop OS) package android-sdk didn't work out easily so I gave up early (been a while since I tried Android package building, nice Makefile though!). If the log doesn't give hints, I will try harder with less clean install processes (the wget > file.sh && sudo ./file.sh solutions on Android guides, I shudder at the thought)


For the script, thanks for having a look, I'm just glad people are as excited as I am about breaking airgaps creatively. It does looks much simpler without base64, chunk integer identification, and more bash knowledge =)

Funny you mention compression, I created a few tickets to log bugs/ideas that were on my mind and nowhere else, including https://github.com/OverkillGuy/qrxfil/issues/8 and https://github.com/OverkillGuy/qrxfil/issues/10, those are indeed relevant to what you describe (I went for zstd in my mind though).

markusfisch commented 3 years ago

Thanks for the full logcat and the original chunk files. I just scanned all 581 chunks with a Nokia 5 (with 2 GB RAM, which is the same as your Nexus 5X has) on Android 9 without a crash. Hm. So it's probably not a memory issue.

Also, memory consumption should be stable over time - otherwise there would be a memory leak that I haven't noticed yet 😬 I will try more devices in the coming days and check for memory leaks just to be sure it's not a leak.

The last error before the crash in logcat comes from the SurfaceFlinger so maybe the crash has something to do with the Toasts.

And yes, I know installing the Android SDK on Ubuntu can be quite a hassle 😉 So I put the new version (1.40.0) in beta so you can try it without having to build the app (you can always go back from beta). This version, 1.40.0, should also be on F-Droid in a few hours, if you prefer to install it from F-Droid.

doronbehar commented 3 years ago

And yes, I know installing the Android SDK on Ubuntu can be quite a hassle wink So I put the new version (1.40.0) in beta so you can try it without having to build the app (you can always go back from beta). This version, 1.40.0, should also be on F-Droid in a few hours, if you prefer to install it from F-Droid.

I checked version 1.40.0 from here on GitHub and it ran fine for me. The F-Droid version is not updated to 1.40.0 yet.

OverkillGuy commented 3 years ago

Insight! Since F-Droid didn't update yet, I used the sideloaded Github release and tested again with my 581 chunks.

Just with the disabled "Toast" flag, I was able to scan over 95 chunks so far with 0 crash. All good.

When enabling the flag again (back to usual behaviour), I wasn't able to crash either but the Toasts were tiny now (spent some time horizontally scanning at first, which may have made OS truncate text?)

I was very confused, but crash wasn't triggered either with or without the flag, in this new 1.40.0 version!

But then as I was trying to add HTTP GET to a local server (python3 -m http.server) as target for the scan, I saw the toast is still used (despite flag) for showing HTTP GET result/url. This may count as oversight, but this was the key to me understanding this:

The Toast looked different.

Here's a screenshot of scanning with a toast in this new version (spent some time first in horizontal mode, where text got truncated, I expect it's the OS truncating this. Back to vertical made the toast stay short, and didn't crash) Screenshot_20210312-010431_Binary_Eye

With HTTP GET URL containing the text, note how it covers EVERYTHING on the screen. This is what the scanning looked like normally in version << 1.40. This DID crash after a while. Screenshot_20210312-010532_Binary_Eye

So. I am a bit crazed after all this, but this seems to hint at:

Let me know how much of what is written above is simply wrong, but I feel validated =D Also, thank you for your patience in investigating this bug, including scanning almost 600 pointless QR codes from a stranger.

markusfisch commented 3 years ago

Very interesting indeed! So it seems to be the Toast after all! 🤔 And you're right, this really fits the picture with the SurfaceFlinger errors.

About the truncation - I added this in the last version (in this commit) because I didn't like the Toast to cover everything 😉 But I did it just for this Toast, when I really should have done in for all the Toasts Binary Eye is opening. That's why the HTTP answer isn't truncated yet. I'll fix this soon and truncate all the Toasts.

Also I guess I should add another setting to switch off the HTTP Toast too. This is unrelated to scanning continousely and there might be use cases where someone wants one but not the other.

And you're welcome! I'm always happy to help. And I also don't like it when my apps crash 😉

OverkillGuy commented 3 years ago

Nice fixes, I'm closing this ticket now, since we've got both a likely root cause, and a fix in the pipes.

This was a fascinating course through Android debugging for me, and I'm you were enthusiast at my crazy project idea. Thanks again for the prompt reaction and patience! Keep up the good work, BinaryEye is a great tool.