This is likely the same reason that you don't want to release a C/C++ program with the -g
flags. Unnecessary symbols are likely stripped away or obfuscated. The build system might leave out certain security measures that will make debugging the application significantly easier. Pro-guard build scripts are likely not included in a debug build.
The following are different types of data that can be kept in debug builds depending upon configuration. Hopefully you can see why these would be helpful to someone looking to reverse engineering your application.
Debug Symbols
Debug symbols are select pieces of information that are stored within the debug binary. All function names, file names, sometimes even line numbers are included. This is so that when a program crashes and a debugger is attached all of this information can be included in stack traces. It allows the programmer to pinpoint the line of code that is causing a bug. A debug stack trace might look like the following (taken from this answer):
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
A program without debug symbols will only know library definitions. Like the following:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7fd3700 (LWP 22532)]0x00007fffe10002b4 in ?? ()
(gdb) bt
#0 0x00007fffe10002b4 in ?? ()
#1 0x0000000000000202 in ?? ()
#2 0x00007fffe1000160 in ?? ()
#3 0x00007ffff66bd1a9 in execute_internal_vm_tests () at /home/bionix/Openjdk8/hotspot/src/share/vm/prims/jni.cpp:5128
#4 0x00007ffff7fd2550 in ?? ()
#5 0x00007ffff6b08bf8 in VM_Version::get_cpu_info_wrapper () at /home/bionix/Openjdk8/hotspot/src/cpu/x86/vm/vm_version_x86.cpp:395
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
ProGuard in Android
Within the Android build scripts (Gradle) you'll find references to ProGuard. ProGuard is used to optimize, reduce binary size, and obfuscate the code. Because Java can be completely decompiled Android uses ProGuard to obfuscate the code. Here's an example of code that has been obfuscated with ProGruard:
// Before
btnNew = changeButtonLabel(btnNew, language.getText("new"));
btnOpen = changeButtonLabel(btnOpen, language.getText("open"));
// After
d = a(d, n.a("new"));
e = a(e, n.a("open"));
You can view the full tutorial with links to source code.
From the Android documentation linked above:
ProGuard is integrated into the Android build system, so you do not
have to invoke it manually. ProGuard runs only when you build your
application in release mode, so you do not have to deal with
obfuscated code when you build your application in debug mode.
ProGuard is technically optional, but when using Android Studio it is enabled by default for Release builds.
Debug Logging
You should always have some mechanism in your code for removing debug messages from your release application. Debug messages are stored as strings within the binary, and are not obfuscated. Which means if you have a message like Log.d("Key " + super_secrey_key);
a person analyzing the binary will now know where "Key "
is referenced. You've now provided context, and an interesting place to start analyzing.
There are several ways that you can remove debug logs, and one of them is by configuring ProGuard to strip them out for you. There are also other libraries that you can use, or you can wrap your logging. Either way it is important to make the distinction between debug and release for your log messages.