Hello Habr!
After writing the
first part , throughout the week I was pondering whether to write about what I promised or at the request of some readers to give explanations and explanations about some, in my opinion basic, things.
I would not want to, but some of you will have to be upset. I don’t like to write copy-paste the most, and I’ll make a lot of links to different resources from the article.
For example, describing how Android downloads in this article would be inappropriate. If you know the principle of initializing the hardware of your computer in the BIOS, and then loading it through the kernel of the system, then Android is no different in this regard. The only difference is in processor architecture. File system structure? Well, gentlemen, this is a clean UNIX system, and writing where and how is stored is absurd! Edit build.prop is system tuning. Yes, this is a modification, but most of these parameters can be made third-party applications, and more convenient to use, such as
System Tuner .
')
It took me a couple of months to understand the very principles of the Android system, it will take as much time and write articles to cover all the basic things. So let's better write about specific examples of how to parse the Dalvik code and create “use” conveniences on the phone.
And so, let's go! Today I will tell you how I implemented the functionality of automatic recording of telephone conversations by native means.
Preamble
The legislation of some countries prohibits the recording of telephone conversations by technical means. For example, in the United States it is prohibited to record personal telephone conversations without prior consent of the parties. And in our post-Soviet space it is allowed to record a conversation in which you are one of the parties, without warning the other participants in the conversation. In the same China, a record is simply simply welcomed and “knocking” on a neighbor is an ideological principle. And this applies not only to records of conversations. The use of the Internet, sending SMS messages, the use of social networks, encryption algorithms, phones with two SIM cards and much more are also individually regulated by the legislation of different countries. Now imagine that you are a business owner and export your IT products around the world. Of course, you will “write” your software in one branch, not several, but the configurations will differ from region to region. For the CIS countries one thing, for Europe - another. Also phone manufacturers are coming. The Android operating system is being finalized by only one group of programmers and into the main branch, and for each region, specific configuration files are used to compile the final releases. I say this with confidence, since I worked at one time with one of the vendors.
As proof of this, there is a good
guide on how to port the firmware to someone else's device.
Go!
Reading the disassembled Java code Phone.apk, I accidentally stumbled upon an entertaining flag. There is actually a lot of interesting, but hidden functionality from us. Literally today I discovered that on my phone there are parameters for the Japanese operator
KDDI . A specially crafted SMS message from the provider can cause my phone to sound heart-rending and vibrating for several minutes in the event of an earthquake or tsunami. But back to our flag.
public static final boolean IS_INCALL_RECORDING_ENABLE = false;
Interesting, I thought. If there is such a flag, then it is used somewhere. But where - not clear. However, I suggested that the call recording button should appear during a call. Things are easy! I changed FALSE to TRUE, re-recorded the patched Phone.apk, called my home phone, picked up the phone and saw the “Start Recording” button. But before it was not !!!

Once again I will tell you how to do such things for the assimilation of the material:
- Create a hotel folder and put there the Phone.apk file and to it smali and backsmali
- Rattle the file to parse the Dalvik code with the
java -Xmx512m -jar baksmali.jar -a -d -o Phone -x Phone.apk
— API Android. JB — 16
— , .
, Phone\com\android\phone\util\VoiceRecorderHelper.smali
command java -Xmx512m -jar baksmali.jar -a -d -o Phone -x Phone.apk
— API Android. JB — 16
— , .
, Phone\com\android\phone\util\VoiceRecorderHelper.smali
java -Xmx512m -jar baksmali.jar -a -d -o Phone -x Phone.apk
— API Android. JB — 16
— , .
, Phone\com\android\phone\util\VoiceRecorderHelper.smali
.field public static final IS_INCALL_RECORDING_ENABLE:Z = false
on .field public static final IS_INCALL_RECORDING_ENABLE:Z = true
Putting our file back: java -Xmx512m -jar smali.jar -a 16 Phone -o classes.dex
Replace the received classes.dex in the original file with any archiver
Overwrite Phone.apk in phone
Having tested the recording of several calls, I found that the phone records conversations with a standard built-in voice recorder and is in very, very good quality. The file name is generated automatically and contains the number or name of the interlocutor, as well as the date and duration of the call. The file saving format can also be configured in the native application. And why do you ask me do I need these call records?
Well, firstly, it is very convenient. I called my spouse and told a list of products to buy, but there is nowhere to record it, then a voice recorder will help.
Secondly, on the phone I have to talk with customers and customers, and sometimes the necessary and important information can slip away or be not heard. Or, for example, an important conversation took place, which needs to be heard and analyzed, and timely decisions made or actions taken together with a business partner or someone else.
Thirdly, a very convenient evidence base in work and life.
You can also ask, why not use third-party applications, of which there are many open and free access?
- I do not trust third-party and not proven applications. Often, because of them the battery sits down, as each such application hangs constantly in the phone’s memory and eats processor time.
- The quality of the recording does not always match the promise.
- I'm picky about the interface. The application may be rich in functionality, but if the GUI is not convenient for me, I will not use it. This is a limp of many domestic developments, unfortunately.
How does this even work?
All Android developers know that the system is full of various standard broadcast messages. Whatever happens in the system, any application can receive it, if you implement "your" recipient of such a message. You can make your own broadcast messages, only the recipient of this message will be the application itself or the entire application that you have made yourself, and they somehow process this message. I saw this in practice in GoDialer and GoSMSPro.
Third-party call recording applications work in the same way. As soon as the message has passed that the call has been established, the recording starts. As soon as the call has stopped, the recording stops and the buffer is written to the file.
My task was to find the place where this message is formed or processed and force recording of the call without any extra movements. After all, it often happens that either you forget to turn on or simply do not have time. How to find the right place in the ton of the firmware code in general is the topic of the next article, but for now let's move on to “our place”.
The processor, or rather two, were in the file \ com \ android \ phone \ CallNotifier.java
The decompiled code (only part of the code is shown here) from Dalvik to Java was as follows:
private void onCallConnected(AsyncResult paramAsyncResult) { Connection localConnection = (Connection)paramAsyncResult.result; String str = ((IfConnection)localConnection).getDialString(); VLog.d("onCallConnected() dialed number:" + str); removeMessages(120000); removeMessages(120001); this.mIsEccNeedRetry = false; this.mEccIsSwitchingForRetrying = false;
and
private void onDisconnect(AsyncResult paramAsyncResult) { Phone.State localState = this.mCM.getState(); if (CallNotifier.VDBG) super.log("onDisconnect()... CallManager state: " + this.mCM.getState()); VLog.d(this, "onDisconnect()"); removeMessages(120000); removeMessages(120001);
The task is complicated compared to the previous article. If last time we had to fix a simple function, then here we will not be able to fix it, since we do not have the source code to rewrite it. Here we need to implant our code.
What is Dalvik?
This is briefly written here and here . Speaking in an even simpler language - the byte code of the virtual machine is based on registers (allocated memory for operating variables) and a set of instructions and operators. The meaning and principle of operation is quite simple: you write down some value in the register and then perform operations on it, returning the result of the operation to where you applied for action. More details about all operators and instructions can be found here: Bytecode for the Dalvik VM .
We implant our code
In order to enter something, we need to know what to write there. You can lick the code from the "Start Write" button handler.
Find where the handler is stored, even for novice programmers for Android, it is not difficult. We will later return to what and how to look for in future articles. The essence of the first articles to explain the principles.
When the button is pressed, the following code is triggered:
VoiceRecorderHelper localVoiceRecorderHelper = VoiceRecorderHelper.getInstance(); if (!localVoiceRecorderHelper.isRecording()) { localVoiceRecorderHelper.start(); }
That is, to automate the recording of all calls, you need to add this code to the onCallConnected handler.
Dalvik code for this entry looks like
invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper; move-result-object v1 invoke-virtual/range {v1 .. v1}, Lcom/android/phone/util/VoiceRecorderHelper;->isRecording()Z move-result v2 const/4 v3, 0x0 if-ne v3, v2, :cond_a9 invoke-virtual/range {v1 .. v1}, Lcom/android/phone/util/VoiceRecorderHelper;->start()Z :cond_a9
Let's sort the code line by line:
- invoke-static invokes an instance of the VoiceRecorderHelper class
- save the instance in the register v1
- call a method of this class called isRecording, which returns true or false
- The result is written to the register v2.
- Write to the register v3 value 0
- Make a comparison between the two registers v2 and v3. Logic: v2! = V3. If isRecording returns TRUE, then v2 will have the value 1 and if FALSE, then vice versa. If the condition does NOT work, then jump to the cond_a9 marker. If not, then
- The start method of the class instance is called, which is stored in v1
- Our conversation began to be recorded.
Go back to our onCallConnected. Its dalvik code looks like this:
.method private onCallConnected(Landroid/os/AsyncResult;)V .registers 8 .parameter "r" .prologue .line 2302 iget-object v0, p1, Landroid/os/AsyncResult;->result:Ljava/lang/Object; check-cast v0, Lcom/android/internal/telephony/Connection; .local v0, c:Lcom/android/internal/telephony/Connection; move-object v2, v0
Let us also analyze this code so that it is clear what is related to
.registers 8
- the number of memory registers needed and used for this functionparameter "r"
is the name of the parameter that was used in the source code. It rarely interests us.prologue
- the beginning of the function algorithm.line 2302
- line number in the source code. This is for debugging only.iget-object v0, p1, Landroid/os/AsyncResult;->result:Ljava/lang/Object;
the next line matches (Connection)paramAsyncResult.result;
check-cast v0, Lcom/android/internal/telephony/Connection;
corresponds to Connection
.local v0, c:Lcom/android/internal/telephony/Connection;
matches localConnection
move-object v2, v0
- cloning a local variable v0 into the register v2- etc.
Parse the code is not so difficult if you refer to the description and compare with Java code.
It would seem that we only need to copy the code from the button click handler and paste it into the beginning of our call handler and that’s it. It was not there. Sometimes it works, but mostly not. The fact is that the registers where we write the data can be used in the further program code and if at the beginning we take the wrong register and write something into it, then during the execution of the program errors can get out and the entire algorithm breaks. Our case is simple in that we enter functions at the beginning and can use any registers that are not initialized at the beginning, because they will be overwritten later. But often the code has to be implanted somewhere in the middle of the program and you need to be careful with registers. About this, too, in future articles.
Modification of registers
Our first two lines of implantable code have the following:
invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper; move-result-object v1
The easiest way to search for suitable register numbers is to search in the method itself which kinds of data are written into the necessary register numbers. If we look in the code, we find that the move-result-object is recorded in v2 and v 3 as well.
Accordingly, all of our v1 in the implanted code will be replaced by v2 or v3
Having done all the operations for replacing register numbers in the implanted code, we get the following picture:
invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper; move-result-object v3 invoke-virtual/range {v3 .. v3}, Lcom/android/phone/util/VoiceRecorderHelper;->isRecording()Z move-result v4 const/4 v5, 0x0 if-ne v5, v4, :cond_27 invoke-virtual/range {v3 .. v3}, Lcom/android/phone/util/VoiceRecorderHelper;->start()Z :cond_27
It should be noted that we changed the marker cond_a9 to cond_27. The fact is that the cond_a9 marker already existed in the file where we implanted the code and the second time such a marker cannot be used. Marker number is a hexadecimal code and can be any, the main thing is unique.
Now in the source file, replace the line .line 2302
with our implantable code and get
.method private onCallConnected(Landroid/os/AsyncResult;)V .registers 8 .parameter "r" .prologue invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper; move-result-object v3 invoke-virtual/range {v3 .. v3}, Lcom/android/phone/util/VoiceRecorderHelper;->isRecording()Z move-result v4 const/4 v5, 0x0 if-ne v5, v4, :cond_27 invoke-virtual/range {v3 .. v3}, Lcom/android/phone/util/VoiceRecorderHelper;->start()Z :cond_27 .line 2302 iget-object v0, p1, Landroid/os/AsyncResult;->result:Ljava/lang/Object; check-cast v0, Lcom/android/internal/telephony/Connection; .local v0, c:Lcom/android/internal/telephony/Connection; move-object v2, v0
It now remains to compile our code using the java -Xmx512m -jar smali.jar -a 16 Phone -o classes.dex
, replace it in Phone.apk and test it.
In the Java version, our work began to look like this:
private void onCallConnected(AsyncResult paramAsyncResult) { VoiceRecorderHelper localVoiceRecorderHelper = VoiceRecorderHelper.getInstance(); if (!localVoiceRecorderHelper.isRecording()) { localVoiceRecorderHelper.start(); } Connection localConnection = (Connection)paramAsyncResult.result; String str = ((IfConnection)localConnection).getDialString(); VLog.d("onCallConnected() dialed number:" + str); removeMessages(120000); removeMessages(120001);
Everything is good, everything works, but the only problem, after the end of the conversation, call recording continues indefinitely.
To do this, we need to prescribe a similar code at the beginning of the onDisconnect function (method), only with inverse logic.
VoiceRecorderHelper localVoiceRecorderHelper = VoiceRecorderHelper.getInstance(); if (localVoiceRecorderHelper.isRecording()) { localVoiceRecorderHelper.stop(); }
By analogy with the beginning of the record, we write a small procedure, replacing the numbers of registers
.method private onDisconnect(Landroid/os/AsyncResult;)V .registers 41 .parameter "r" .prologue invoke-static {}, Lcom/android/phone/util/VoiceRecorderHelper;->getInstance()Lcom/android/phone/util/VoiceRecorderHelper; move-result-object v34 invoke-virtual/range {v34 .. v34}, Lcom/android/phone/util/VoiceRecorderHelper;->isRecording()Z move-result v4 if-eqz v4, :cond_33 invoke-virtual/range {v34 .. v34}, Lcom/android/phone/util/VoiceRecorderHelper;->stop()Z .line 2487 :cond_33
We collect our changes, replace them in the phone and voila - everything works as it should.
Epilogue
I am sure that this material was several times more complicated and confusing than the previous article. Some kind of registers, operators, modifiers ... It looks like nonsense. I myself for the first time when I saw Dalvik - I was horrified, closed the page and did not open it for half a year. When pressed against the wall, within two weeks I quickly figured out what was happening and how to put it into practice. What pleases, for all the practice I have never received a brick.
Not for advertising sake, I want to advise two resources where you can gather a lot of information:
Russian-speaking and English-speaking
On both resources I am present with the same nickname.
In the meantime, until the next article, I hope in a week.