[Android Software/Hacking General [Developers Only]] xd.bx:
Hello folks,

This thread is about sharing tricks about porting Android on new devices, and in particular how to reverse-engineer proprietary files with specific tools. Specifically, I'll use my experience on the camera part of the HTC ChaCha as an example.

Prerequisites

Install or reinstall the stock ROM

Make sure your device is rooted. If not, you might need to unlock the bootloader (for example, with the XTC Clip for HTC phones), install ClockworkMod and finally flash the Superuser package. There are many tutorials elsewhere on this so be sure to use the search button ;)

Install adb from the SDK and (if using Windows) the required drivers for communicating with adbd on the phone or tablet. For HTC phones, here is a direct link to the driver: http://goo-inside.me/tools/USB_driver_20101122_release.zip

Modify your PATH so that adb is in it (optional but useful)

Install the NDK. Go into clockworkmod, run "adb shell mount /system", then "adb push /opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/gdbserver /system/bin/" and finally "adb shell chmod 755 /system/bin/gdbserver".

You will need to replace the path to gdbserver above with the correct path to your NDK installation.

Make a CWM backup of the stock ROM, so that you can switch easily from between stock and your CyanogenMod / AOSP build.

Install the free evaluation version of IDA Pro, see http://www.hex-rays.com/products/ida/support/download_demo.shtml

The general idea

We mostly use binary libraries from the stock ROM, so the important part is to understand how to communicate with them properly.

Note: the exception is the Linux kernel, because we don't use binary kernels from stock ROMs in CM7 and AOSP as they are generally incompatible and lack features (overclocking, pure bluetooth stack, ...). I'll probably make another thread about hacking kernel sources.

So we have to understand how things communicate with each other & the order and content of messages that are passed between components of the system. Reading the sources of Android is generally the best way to begin, to trace the interactions from the Java side of things up to the kernel.

Reverse-engineering of APKs with apktool, dex2jar & jd-gui

I'll complete this part shortly.

Static reverse-engineering of libcamera.so

In the case of the camera, a quick analysis of the source shows the Camera application uses the android.hardware.Camera class, which is mostly a bridge to the C++ file android_hardware_Camera.cpp, itself another bridge to the libcamera_client, which in turns calls the camera service inside the process "mediaserver" through a Binder (an Android-specific IPC mechanism). This architecture in theory allows concurrent access to the camera (but who does that?)

So the actual part that talks to the hardware is in libcameraservice, loaded by mediaserver at runtime. Examining the code in CameraService.cpp shows that is communicates with the proprietary libcamera.so through a C++ interface, CameraHardwareInterface.h.

This is where the stuff from HTC in the ChaCha starts to diverge from the original Android sources. Loading libcamera.so in IDA Pro allows us to look at the actual CameraHardwareInterface virtual table. It is actually easy to locate in IDA by searching for " `vtable for'android::QualcommCameraHardware". However IDA does not automatically detect it's a table of function pointers, so use the Edit->Array with a size of 46 and an entry size of 4 (the size of a pointer).

By manually comparing the list of pointers to the CameraHardwareInterface.h in the CM7 sources, one can see two functions that can be added with the USE_GETBUFFERINFO in BoardConfig.mk define: getBufferInfo and encodeData. There is however another third function not present in CameraHardwareInterface.h, setFaceDetectionState(), just after getParameters(). Thus we have to add this function to CameraHardwareInterface.h so that the virtual table matches the one in libcamera.so.

Now it's also interesting to compare the list of symbols between libraries from different ROMs. In this case, we can try to extract the camera parameters in HTC's ROM, and see if they match the symbols in CM7. The supported list of parameters is provided in libcamera_client.so. Use the program objdump from the NDK to retrieve the list of symbols and have them sorted (if using Windows, you'll need Cygwin):

/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/arm-linux-androideabi/bin/objdump -T libcamera_client-cm7.so |cut -d '_' -f 2- > sym-camera_client-cm7
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/arm-linux-androideabi/bin/objdump -T libcamera_client-htc.so |cut -d '_' -f 2- > sym-camera_client-htc
diff -u sym-camera_client-cm7 sym-camera_client-htc

There are a bunch of new interesting symbols not present in CM7. Some of them seem related to HTC's Ola face detection engine, whilst others are unknown:

+ZN7android16CameraParameters27KEY_PREVIEW_FRAME_RATE_MODEE
+ZN7android16CameraParameters16KEY_CAPTURE_MODEE
+ZN7android16CameraParameters17KEY_PICTURE_COUNTE
+ZN7android16CameraParameters27KEY_MAX_BURST_PICTURE_COUNTE
+ZN7android16CameraParameters19KEY_TOUCH_INDEX_AECE
+ZN7android16CameraParameters18KEY_TOUCH_INDEX_AFE
+ZN7android16CameraParameters16KEY_SCENE_DETECTE
+ZN7android16CameraParameters26KEY_SUPPORTED_SCENE_DETECTE
+ZN7android16CameraParameters23KEY_TAKING_PICTURE_ZOOME
+ZN7android16CameraParameters22KEY_SELECTABLE_ZONE_AFE
+ZN7android16CameraParameters32KEY_SUPPORTED_SELECTABLE_ZONE_AFE
...

Debugging libcamera.so

At this point it would be a bit time-consuming to statically check all code paths within the stock ROM to see what parameters are actually used when taking a normal picture. A easier way is to break into the setParameter function within libcamera to inspect at runtime the arguments. We'll use gdb for this.

Run "adb forward tcp:1234 tcp:1234" to forward the TCP port used by gdbserver. Then run an adb shell, then "su" to become root, then list the processes with "ps", and finally run "gdbserver :1234 --attach <pid of mediaserver>".

Not on the phone, but on the host, extract the libraries and mediaserver, then run gdb:

mkdir lib
cd lib
adb pull /system/lib
adb pull /system/bin/mediaserver
adb pull /system/bin/linker
/opt/android-ndk-r7/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-gdb mediaserver

In the gdb command prompt, enter "set height 0", "set solib-search-path ./" and then "target remote 127.0.0.1:1234". gdb should then show the loading of all .so files, such as "Reading symbols from /root/chacha/system/lib/libarimedia.so...

(no debugging symbols found)...done.". Sometimes nothing is shown, if so start over (exit gdb, reattach gdbserver, restart gdb).

Now we can set breakpoints on the functions that interest us. Open libcamera.so in IDA Pro, also have a look at the list of symbols with objdump -T. The following functions are of particular interest:

_ZN7android16CameraParameters3setEPKci
_ZN7android16CameraParameters3setEPKcS2_

In the ARM binary calling convention, parameters are passed in registers r4 to r8 (instead of say, 32-bit x86 where parameters are pushed on the stack). Let's examine what they point to at runtime:

(gdb) break _ZN7android16CameraParameters3setEPKci
Breakpoint 5 at 0xaba8eef4
(gdb) break _ZN7android16CameraParameters3setEPKcS2_
Breakpoint 6 at 0xaba8ed14
(gdb) cont
Continuing.
[New Thread 923]
[Switching to Thread 923]

Breakpoint 6, 0xaba8ed14 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
(gdb) x/1s $r4
0xaba9100c <_ZN7android16CameraParameters16KEY_PREVIEW_SIZEE>:	"preview-size"
(gdb) x/1x $r5
0xafd4d6e8 <__stack_chk_guard>:	0x10997eaa
(gdb) x/1s $r5
0xafd4d6e8 <__stack_chk_guard>:	"�~\231\020"
(gdb) x/1s $r6
0x411139cc:	"640x384"
(gdb) x/1s $r7
0x30d0c:	"h8��\210\f\003"
(gdb) x/1s $r8
0xa811d251 <__dso_handle+512417>:	"�\205h\203�\ahFh�h����"
(gdb) cont
Continuing.

So we see the first parameter is passed in r4 and the second in r6. Likewise, for breakpoint 5we can examine the registers and see the parameters r7 and r5. Now let's enable logging and automatically dump the arguments each time a breakpoint is hit, then resume execution:

(gdb) set logging on
Copying output to gdb.txt.
(gdb) commands 5
Type commands for when breakpoint 5 is hit, one per line.
End with a line saying just "end".
>x/1s $r7
>x/1s $r5
>cont
>end
(gdb) commands 6
Type commands for when breakpoint 6 is hit, one per line.
End with a line saying just "end".
>x/1s $r4
>x/1s $r6
>cont
>end

Finally, here's the juicy bits we wanted :)

(gdb) cont
Continuing.

Breakpoint 5, 0xaba8eef4 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
0xaba9106c <_ZN7android16CameraParameters33KEY_SUPPORTED_PREVIEW_FRAME_RATESE>:	"preview-frame-rate-values"
0x411139dc:	"15"

Breakpoint 6, 0xaba8ed14 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
0xaba914a4 <_ZN7android16CameraParameters22KEY_VIDEO_FRAME_FORMATE>:	"video-frame-format"
0xa7912c16 <__dso_handle+4262342>:	"yuv420sp"

Breakpoint 6, 0xaba8ed14 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
0xaba91030 <_ZN7android16CameraParameters18KEY_PREVIEW_FORMATE>:	"preview-format"
0xa7912c16 <__dso_handle+4262342>:	"yuv420sp"

Breakpoint 6, 0xaba8ed14 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
0xaba91110 <_ZN7android16CameraParameters16KEY_PICTURE_SIZEE>:	"picture-size"
0x411139cc:	"2592x1952"

Breakpoint 6, 0xaba8ed14 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
0xaba91134 <_ZN7android16CameraParameters18KEY_PICTURE_FORMATE>:	"picture-format"
0xa79120a5 <__dso_handle+4259413>:	"jpeg"

Breakpoint 6, 0xaba8ed14 in android::CameraParameters::set () from /root/chacha/system/lib/libcamera_client.so
0xaba911f8 <_ZN7android16CameraParameters16KEY_JPEG_QUALITYE>:	"jpeg-quality"
0xa7912bb9 <__dso_handle+4262249>:	"100"

... and so on

If mediaserver crashes or stop responding, as a worst case you may have to reboot the phone, as the Linux kernel doesn't always properly cleanup dead debugged processes.

Then the operation can be repeated but with CM7 instead of stock ROM, and the gdb.txt output files compared for any modifications. Now this is just the beginning, but hopefully I've showed you a taste of how to do reverse-engineering on Android and I hope it'll help make this area of work less obscure to newcomers :)