Tuesday 9 June 2015

Fixing Manual Focus - A Cracker's way

Background:
Soon after providing find7 HAL for OnePlus One, people started complaining of broken RAW image capture functionality (i'm neither RAW image user nor any expert in camera sensor architecture). An obvious reason is find 7 HAL uses different format for RAW images, which seems broken (again i'm no expert here). Lots of formats are specified for RAW images. This can be easily fixed by tweaking camera to use desired (in our case, which is used by CM HAL) format. This is simple one line code using set(String, String) method of camera parameters. But this will require modification in all applications. I believe in providing full compatibility so i thought about modifying CM HAL to fix Manual Focus functionality.

I was aware that our HAL is same as find7's and thus it also supports Manual Focus because running dumpsys media.camera shows that min-focus-pos-index and max-focus-pos-index parameters are set by HAL. Professional mode plug in utilizes these two params (there is another one also). Also if you have noticed, when you slide MF slider from near to infinity and vice versa, you'll see focus moving, a little bit but it responds.

So, i again dumped camera parameters and checked values for min-focus-pos-index and max-focus-pos-index. It was 40 and 60 respectively. If you look at the code for MF Slider, it builds up slider in such a way that its value will jump between min-focus-pos-index and max-focus-pos-index. This made everything clear. Since slider was causing value to jump between 40 and 60 it had only a small effect on focus. Besides i loaded find7 HAL and checked values for these two params and it was 0 and 300 respectively.

So, conclusion is we need to change value of min-focus-pos-index from 40 to 0 and value of max-focus-pos-index from 60 to 300.

I haven't tested, but we can simply change values of these params by set method of camera parameters, but again it requires to be done from all applications so i left that way. All parameters are explicitly defined by HAL which can be overridden by Application Layer as per requirement. I started looking into QCOM Camera HAL source for understanding how they are utilized. All parameters related operations are defined under QCameraParameters.cpp (this one is from Nexus 6's HAL). If you look further, default parameters and values are set by initDefaultParameters() function. So now we know what we need to do. We need to hook into this function and set proper values for target parameters.

Note: This is not as simple as modifying smali code because of various reasons. It requires deep understanding of ARM architecture and assembly code. So be prepared with a cup of coffee :)



The real challange:
So let's start with patching HAL's blob. We'll use IDA PRO for analyzing/understanding code and HxD for modifying blob (hex editing). Here i assume you know how to use IDA (No worries if you don't. It's easy. You can comment here if you need any help) So, open up IDA PRO and load camera.vendor.bacon.so (our stock camera HAL, you can find it on /system/lib/hw).
As we already know we need to look for initDefaultParameters() function, search it from Function Window. Now double click and open up function.
Now let's find our target parameter keys. Press ALT+T to bring up search window and search max-focus-pos-index. Look for the match found at 0x0006169E and 0x0006168A for min-focus-pos-index. Notice that instruction to be executed after both these line is
BLX             _ZN7android16CameraParameters3setEPKci
This relates to call to function set(char const*,int). Obviously this is the place from where values for min-focus-pos-index and max-focus-pos-index are set. Now we need to find the address of the values which are being passed as parameter to this function. As we already know the value (40 & 60), we'll just search it (else you need to trace down parameter registers & function calls)
See the highlighted lines in screenshot just some lines above, it loads 0x28 (40) in R0 and 0x3c (60) in R1.
Conclusion: We need to replace 0x28 with 0x0 and 0x3c with 0x12c.
Let's start with replacing 0x28 with 0x0. Open up HxD and load camera.vendor.bacon.so. Now in IDA notice the line
.text:0006166A MOVS R0, #0x28
From this, we can see that offset is 0x6166A. Click on this line and open up Hex view-1 tab in IDA. You'll see two blocks high lighted. We are going to replace these two. Open up HxD and jump to given offset. We know that we want to replace this line with
.text:0006166A MOVS R0, #0x0
Here 20 is instruction code (Little endian format) for MOVS R0 and 28 is the Hex value to be stored. So replace 28 with 00 and its all done.
Now its turn for replacing 0x3c with 0x12c. This is not as simple as replacing 0x28 with 0x0 because each register can store number in range of 0-255 only. To store larger numbers we need to use wider instructions like MOV.W. MOV.W which are 4-byte instructions so 2-byte instructions like MOV can't be replaced by them. See STR.W is at 0x6166E. Such cases require use of Code Caves. Finding and utilizing code cave is hard and very complex job and requires huge amount of efforts. So for this tutorial i won't be including this topic. In next tutorial i'll write about how i achieved it.

If you want to try this by yourself, you can replace 0x3c with 0xFF (255) the same way we did with 0x28 to see practical effects. There is nothing wrong with it but focus will move between 0-255 in this case.

Hope i was able to make you understand everything.
You can share your thoughts, doubts, questions in comments :)

Have a nice day...