📜 ⬆️ ⬇️

Using drivers from the Android application

Route gives almost absolute power over the Android device. Today I will tell you how to get even more inclined to programming and the desire to explore the system on your device. Who is interested - I ask under the cat.

Well, let's start in order.
What is necessary

  1. Minimum knowledge of C.
  2. Minimal knowledge of Java.
  3. Some insight into how the elements of the Android system interact.
  4. Rutted Android phone.
  5. IDE with support for Android SDK / NDK (in my case eclipse, it is very easy to configure to work with Android and described it many times).
  6. Tulchain for the cross-compilation of which the kernel was assembled on the target device.
  7. Assembled core for our device with the correct local version.


It is worth saying that I used Linux OS Ubuntu 11.10 and I will give all the examples for it.
The first 3 points are obvious, how to achieve 4 and 5 is easy to find on the Internet. Consider the last two in detail.

Choosing a toolchain for cross-compiling kernel modules (drivers)

In this article we do not consider the possibility of flashing a personally assembled kernel on your phone, so we must follow certain rules.
In order to find out how the compiler builds the kernel on our device, execute the command:
cat /proc/version 

using any terminal emulator or using adb utility:
 adb shell "cat /proc/version" 

As a result, we get a string like this:
 Linux version 3.0.69-g26a847e (blindnumb@iof303) (gcc version 4.7.2 20120701 (prerelease) (Linaro GCC 4.7-2012.07)) #1 PREEMPT Mon Mar 18 12:19:10 MST 2013 

We see that we have installed the kernel version 3.0.69 of the local version "-g26a847e" and compiled it with the linaro GCC 4.7-2012.07 toolchain. Knowing the version we find the necessary toolchain and unpack it into any folder. My path looked like this:
 /home/user/android/android_prebuilt_linux-x86_toolchain_arm-gnueabihf-linaro-4.7 

Kernel build

To begin, let us know which core our device uses. This can be done by executing the command indicated above or by entering the settings in the section “About the phone” on the device.
System Information


As mentioned above in my case, this is 3.0.69-g26a847e. Having a little picking on the firmware githaba (PACman for HTC Desire S) I determined that this is the core of AndromadusMod.
We copy the found ihodniki to my local machine (I first forked the necessary repository in my github and executed git clone, manufacturers like Google and manufacturers of custom firmware keep the kernel sources in the open source repositories, some just allow you to download the source as an archive). For me, it looked like this:
 /home/user/android/saga-3.0.69 

Now you need to find the configuration with which the core of our device is assembled. In most cases, the configuration lies on the device itself and you can get it using adb, unpack and copy to the folder with the kernel sources:
 adb pull /proc/config.gz . gunzip ./config.gz cp ./config /home/user/android/saga-3.0.69/arch/arm/my_device_defconfig 

It is also necessary to slightly change the configuration - install the local version to the identical one that we learned earlier and turn off the automatic assignment of the local version. This can be done using any text editor:
 CONFIG_LOCAL_VERSION="-g26a847e" CONFIG_LOCAL_VESION_AUTO=n 

After we go to the source folder, set up the environment variables for the build and build the kernel itself:
 cd /home/user/android/saga-3.0.69 export ARCH=arm export CROSS_COMPILE=/home/user/android/android_prebuilt_linux-x86_toolchain_arm-gnueabihf-linaro-4.7/bin/arm-eabi- export LOCALVERSION= all make my_device_defconfig make 

Now you can go to programming.
Code writing

Android app

Given the huge number of articles on the writing of Android applications, I will consider only the moments related to the task.
Our application will have a total of 1 Activity:
activity_main.xml
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:text="@string/btnText1" android:onClick="onClick"/> <EditText android:id="@+id/editText1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_below="@+id/button1" android:layout_marginTop="42dp" android:ems="10" android:textSize="16sp" android:inputType="textMultiLine" /> </RelativeLayout> 


It looks like this in the end:

We assign an event to the button that will receive information from our driver and write it in the text field:
MainActivity class
 public class MainActivity extends Activity { private EditText text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (EditText)findViewById(R.id.editText1); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } public void onClick(View view) { switch (view.getId()) { case R.id.button1: text.setText(IoctlWrapper.getData()); } } } 


Now we will create a wrapper class for our jni library:
 public class IoctlWrapper { public static native String getKData(); //   ,      public static String getData() { return getKData(); } static { System.loadLibrary("ioctlwrap"); } } 

Jni

Create a jni folder in the root of the Android application project.
Next, generate a C header for our native library:
 cd src javac -d /tmp/ com/propheta13/amoduse/IoctlWrapper.java cd /tmp javah -jni com.propheta13.amoduse.IoctlWrapper 

We get the header and copy it to the previously created folder, create the appropriate .c and Android.mk build configuration:
 cd [PATH TO ANDROIDPROJ]/jni cp /tmp/com_propheta13_amoduse_IoctlWrapper.h ./ioctlwrap.h touch ./ioctlwrap.c touch ./Android.mk 

Android.mk content:
 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ioctlwrap LOCAL_SRC_FILES := ioctlwrap.c 

The algorithm of the library:
  1. Open the driver node.
  2. Allocate buffer for information from driver
  3. Get information using ioctl request.
  4. Close the node.
  5. Convert the information into a Java string and pass it into a wrapper.

Full code:
ioctlwrap.c
 const char string[] = "Driver open failed."; #define BUF_SIZE 4096 JNIEXPORT jstring JNICALL Java_com_propheta13_amoduse_IoctlWrapper_getKData (JNIEnv *env, jclass jcl) { char *info_buf = NULL; int dfd = 0, rc = 0; dfd = open(TKMOD_DEV_PATH, O_RDONLY); if(dfd < 0) { jstring RetString = (*env)->NewStringUTF(env, string); goto exit; } info_buf = malloc(BUF_SIZE); rc = ioctl(dfd, TKMOD_IOCTL_GET_DATA, info_buf); if(rc < 0) { strerror_r(rc, info_buf, BUF_SIZE); } jstring RetString = (*env)->NewStringUTF(env, info_buf); free(info_buf); close(dfd); exit: return RetString; } 


Kernel driver

I will not fully describe the process of writing a driver; I will only make a couple of notes:
  1. The driver written for this article does nothing supernatural - it only returns the list of network interface names.
  2. The ioctl mechanism is used to communicate with the driver.
  3. The makefile for the build allows you to specify the kernel for which you want to build this driver, for this you need to correctly specify the environment variables and use the command:

 make KMODDIR=[path to kernel] 

Launch

To begin, fill in the assembled driver on the device, and install it into the kernel, at the same time we will make the driver node accessible for everyone:
 adb push ./test_kmod.ko /data/local/tmp root@android:/ # rmmod test_kmod root@android:/ # insmod /data/local/tmp/test_kmod.ko root@android:/ # chmod 777 /dev/tkmod_device 

If the kernel version is modified correctly and the kernel matches the one that was on the device, there should be no errors.
After that, you can run the Android application directly via eclipse or install it. Push a single button and get the result:

Kernel logs can be obtained with the command:
 root@android:/ # dmesg | grep [TEST_KMOD] #        . 

Kernel log
 <6>[ 8695.448028] [TEST_KMOD] == Module init == <7>[ 8775.583587] [TEST_KMOD] tkmod opened. Descriptor: 0xc2e98e00. <7>[ 8775.583770] [TEST_KMOD] TKMOD_IOCTL_GET_DATA request. <6>[ 8775.583923] [TEST_KMOD] name = lo <6>[ 8775.584167] [TEST_KMOD] name = dummy0 <6>[ 8775.584259] [TEST_KMOD] name = rmnet0 <6>[ 8775.584320] [TEST_KMOD] name = rmnet1 <6>[ 8775.584503] [TEST_KMOD] name = rmnet2 <6>[ 8775.584564] [TEST_KMOD] name = rmnet3 <6>[ 8775.584655] [TEST_KMOD] name = rmnet4 <6>[ 8775.584777] [TEST_KMOD] name = rmnet5 <6>[ 8775.584930] [TEST_KMOD] name = rmnet6 <6>[ 8775.585021] [TEST_KMOD] name = rmnet7 <6>[ 8775.585113] [TEST_KMOD] name = gre0 <6>[ 8775.585266] [TEST_KMOD] name = sit0 <6>[ 8775.585357] [TEST_KMOD] name = ip6tnl0 <7>[ 8775.585479] [TEST_KMOD] tkmod_ 0xc2e98e00 closed successfuly. 


Conclusion

The shown use of this bundle is not the only one. Using kernel drivers allows you to directly work with any device interfaces, influence the operation of any application and the system as a whole, also allows you to work with interfaces that are hidden deep in the system behind a whole bunch of APIs and frameworks - for example, a driver that will write the information you need directly into the video memory buffer devices. This solution is applicable not only for phones but also for any devices based on Android.
The complete sources are on github .
At this end, thank you for your attention. I hope that this material will be useful for someone.
Resources used:

1. developer.android.com - Android SDK / NDK and more.
2. www.vogella.com - pretty good and understandable articles on the development of Android applications.
3. blog.edwards-research.com/2012/04/tutorial-android-jni - tutorial on using JNI.
4. docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/functions.html - reference material on the JNI interface.
5. Linux Device Drivers, 3ed - the bible of the Linux Kernel programmer.
UPD

Corrected several typos, errors in the code. Thanks: bmx666 , Shirixae

')

Source: https://habr.com/ru/post/174077/


All Articles