Monday, August 17, 2015

Android - Using Crypto extensions

I had some problems compiling ASM code as part of an Android kernel module.

As part of my system, I need to encrypt/decrypt data inside the kernel and I decided to use AES CBC with 128 bits keys. I am also using a Nexus 9 (arm64 v8), which means that I have access to crypto extensions, including instructions for AES operations :-)

Considering this scenario, I implemented 3 options (of course, I picked the most efficient one at the end):
  1. A "standard" AES CBC implementation in C (the worst performance);
  2. The Linux kernel Crypto lib with crypto extensions set to ON (good performance and very easy to use);
  3. The AES CBC asm implementation from the OpenSSL lib (good performance and it took me extra time to integrate it into my code);
At the end, I took the option #03. Both #02 and #03 have good performance, but #03 is slightly better.

Let's discuss #02 and #03 in this post.

As the very first step, check if your kernel has the proper options enabled for the crypto extension usage. The following ones should be set to "y":

CONFIG_CRYPTO_HW
CONFIG_ARM64_CRYPTO
CONFIG_CRYPTO_AES_ARM64_CE
CONFIG_CRYPTO_AES_ARM64_CE_CCM
CONFIG_CRYPTO_AES_ARM64_CE_BLK

The Crypto lib

The Linux kernel provides the Crypto library, with several cryptography operations, such as AES, including different block/streams ciphers. The following code use AES 128-bits CBC, and a 0-ed IV (don't do that!)

static int pc_aes_encrypt_kernel(const void *key, int key_len,
                                        void *dst, size_t *dst_len,
                                        const void *src, size_t src_len)
{
        struct crypto_blkcipher *blkcipher = NULL;
        char *cipher = "__cbc-aes-ce";
        char iv[AES_BLOCK_SIZE];
        char *charkey = (unsigned char *) key;

        unsigned int ivsize = 0;
        char *scratchpad_in = NULL, *scratchpad_out = NULL; 
        struct scatterlist sg_in, sg_out;
        struct blkcipher_desc desc;
        int ret = -EFAULT;
        int len = ((int)(src_len/16) + 1) * 16;

        memset(iv, 0x00, AES_BLOCK_SIZE); // set the iv to a proper value!!

        blkcipher = crypto_alloc_blkcipher(cipher, 0, 0);
        if (IS_ERR(blkcipher)) {
                PC_LOGV("could not allocate blkcipher handle for %s\n", cipher);
                return -PTR_ERR(blkcipher);
        }

        if (crypto_blkcipher_setkey(blkcipher, charkey, key_len)) {
                PC_LOGV("key could not be set\n");
                ret = -EAGAIN;
                goto out;
        }

        ivsize = crypto_blkcipher_ivsize(blkcipher);
        if (ivsize) {
                if (ivsize != AES_BLOCK_SIZE)
                        PC_LOGV("IV length differs from expected length. It should be : %d\n",ivsize);
                crypto_blkcipher_set_iv(blkcipher, iv, ivsize);
        }

        scratchpad_in = kmalloc(src_len, GFP_KERNEL);
        if (!scratchpad_in) {
                PC_LOGV("could not allocate scratchpad_in for %s\n", cipher);
                goto out;
        }

        scratchpad_out = kmalloc(len, GFP_KERNEL);
        if (!scratchpad_out) {
                PC_LOGV("could not allocate scratchpad_in for %s\n", cipher);
                goto out;
        }

        memcpy(scratchpad_in,src,src_len);

        desc.flags = 0;
        desc.tfm = blkcipher;
        sg_init_one(&sg_in, scratchpad_in, src_len);
        sg_init_one(&sg_out, scratchpad_out, len);

        crypto_blkcipher_encrypt(&desc, &sg_out, &sg_in, src_len);

        // for decryption, use the following
        // crypto_blkcipher_decrypt(&desc, &sg_out, &sg_in, src_len);

        memcpy(dst,scratchpad_out,sg_out.length);

        *dst_len = sg_out.length;

        ret = 0;
        goto out;

out:
        if (blkcipher)
                crypto_free_blkcipher(blkcipher);
        if (scratchpad_out)
                kzfree(scratchpad_out);
        if (scratchpad_in)
                kzfree(scratchpad_in);

        return ret;

}

For decryption, you can use the same code, but use the function crypto_blkcipher_decrypt instead.

Integrating a *.S file as part if your module built process

As said, I used an existing implementation from OpenSSL, which uses the crypto extensions of arm64-v8. I only had to change the "includes" of the asm file. In addition, I included the object file into my Makefile, like this:

pc_module-objs := ...
                  src/pc_utility.o src/asm/aesv8-armx-64.o \
                  src/crypto/aes.o src/crypto/crypto.o


However, I had some problems with different asm files that I was testing. For example, for the OpenSSL library, some of them will not compile if you use GCC 4.8/4.9. The point is that they use a different architectural syntax (Apple) and you'll see several "Error: unknown mnemonic" error messages.

So, you can use LLVM to compile the asm files with the Apple architectural syntax. LLVM is available in the Android NDK. Then, you can copy the *.o files into your code and build your project. The symbols should match as a charm.

The following code shows the usage of the functions defined in the file aesv8-armx-64.S (available into the folder /external/openssl/)

static int pc_aes_encrypt_hw(const void *key, int key_len,
                                        void *dst, size_t *dst_len,
                                        const void *src, size_t src_len)
{
        AES_KEY enc_key;

        unsigned char enc_out[src_len];
        unsigned char iv[AES_BLOCK_SIZE];
        unsigned char *aes_input = (unsigned char *) src;
        unsigned char *aes_key = (unsigned char *) key;

        memset(iv, 0x00, AES_BLOCK_SIZE);

        HWAES_set_encrypt_key(aes_key, key_len*8, &enc_key);
        HWAES_cbc_encrypt(aes_input, enc_out, src_len, &enc_key, iv, AES_ENCRYPT);

        *dst_len = src_len;
        memcpy(dst,&enc_out[0],src_len);

        return 0;
}

static int pc_aes_decrypt_hw(const void *key, int key_len,
                                        void *dst, size_t *dst_len,
                                        const void *src, size_t src_len)
{
        AES_KEY dec_key;

        unsigned char iv[AES_BLOCK_SIZE];
        unsigned char dec_out[src_len];
        unsigned char *aes_input = (unsigned char *) src;
        unsigned char *aes_key = (unsigned char *) key;

        memset(iv, 0x00, AES_BLOCK_SIZE);

        HWAES_set_decrypt_key(aes_key, key_len*8, &dec_key);
        HWAES_cbc_encrypt(aes_input, dec_out, src_len, &dec_key, iv, AES_DECRYPT);

        *dst_len = src_len;
        memcpy(dst,&dec_out[0],src_len);

        return 0;
}


The functions HWAES_set_encrypt_key, HWAES_set_decrypt_key, HWAES_cbc_encrypt and HWAES_cbc_decrypt are implemented in a asm file (aesv8-armx-64.S) and defined in a header file (aes_cbc.h). Again, I used AES 128-bits CBC, with 0-ed IV (don't do that!).


Saturday, July 18, 2015

New syscalls for armv8-64 kernel

I decided to use a different target platform for my project. Initially, I was using Android Kitkat + Galaxy Nexus (ARMv7 + TI OMAP 4460). To obtain best performance with some crypto operations, I am now using Android Lollipop + Nexus 9 (ARMv8 64bits). I did some tests with basic OpenSSL examples (for example, AES-128 and SHA1) and the numbers are impressive.

The goal of this post is to document some problems that I had building the Android image and its respective kernel. You might think that I am being redundant, as you [might] have the same issues with a traditional Linux kernel. I've done this only for Android platform, so I don't know for other kernels. I'm also having some other problems, such as ARMv8-64 asm file compilation inside the kernel, but this will be described in another poster.

Basically, in my project, I have to create new syscalls and also change the sys_call_table to intercept existing ones, such as NR_write, NR_open, etc. You can check another post that describes how to create new syscalls and how to intercept existing syscalls. So, what is different?

1. Some syscalls have changed, some have been created and some others are not supported anymore. 


 __NR_open, __NR_link, __NR_chmod, __NR_rename, ... are not supported anymore. Instead, you should use __NR_openat, __NR_mkdirat, ...

For example, check the syscalls under the #def body __ARCH_WANT_SYSCALL_DEPRECATED and __ARCH_WANT_SYSCALL_NO_AT (file include/uapi/asm-generic/unistd.h)

In addition, the syscall number has also been changed. For example, NR_write changed from 3 to 63. Remember to change your code in userpsace that has any reference to one of these numbers.

2. Much easier than before: now, you only need to change less files. Add the definition of your syscalls in the following files:


include/linux/syscalls.h

 ...
 asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
                            const char __user *uargs);

 asmlinkage long sys_my_syscall(void __user *buf);
 ...

include/uapi/asm-generic/unistd.h

 ...
 #define __NR_seccomp 277
 __SYSCALL(__NR_seccomp, sys_seccomp)

 #define __NR_my_syscall 278
 __SYSCALL(__NR_my_syscall, sys_my_syscall)

 #undef __NR_syscalls
 #define __NR_syscalls 279
 ...

kernel/my_syscall_code.c (this file should contain the implementation of your syscall)

kernel/Makefile

 obj-y = ....
 ...
         async.o range.o groups.o lglock.o smpboot.o \
         my_syscall_code.o
 ...

3. Don't forget to add asm/unistd.h as part of your head files.


4. Kernel modules are implemented the same way as before. To build it, remember to set cflags properly


There's just a small change on building the module. Call make with CFLAGS_MODULE=-fno-pic

5. And of course, the syscall table address table has changed as well. 


Check the symbol sys_call_table in the file System.map.

As said, I am having other problems with it. In my case, I want to add some assembly files into my module and compile them all together. The point is that they are written in a specific format that GCC does not understand, but clang. So, things would be solved if I could use clang to build my module, but it doesn't work like that :-( Let's what comes next!

EDIT (31/07/2015) : this is a good guide for 32 bits -> 64 bits porting

Sunday, June 14, 2015

UEvents in Android - from kernel events to notifications

In my current project, the system has to notify the user about a certain action that actually takes place inside the kernel (in this case, transfer of security keys). I don't want to get into too much details about the tasks, but let's consider that the kernel and the Dalvik are the only trustworthy components and the security keys are stored inside the kernel. I've also seen some pieces of code that helps to implement this, but I could not find a end-to-end solution.

[This post is a bit long, as it contains a lot of code and I tried to explain some details of it.]

Ok, let's proceed. This is the flow:

1) The kernel is asked to perform a certain syscall (transfer securely stored keys inside the kernel);
2) The kernel has to notify the user that some application has asked to do something suspicious, for example, to have access to some security keys;
3) The event is sent all the way to the userspace and a notification appears to the user (UI).

First, we have to generate one event in the kernel space (you can create a module and insert your code there). For this, we use an UEVENT. In addition, we can also add extra pieces of information into the event, for example strings, integers and so forth. The following code shows a simple UEVENT generated from the kernel space. It contains one integer field called "etype".

Some pieces of code were taken from the kernel itself (linux/samples/kobject/kset-example.c), and I made small changes to keep it even simpler. For more information, check the original one.

Initially, we need to create a kobject to embedded an attribute, for instance (https://www.kernel.org/doc/Documentation/kobject.txt). Once the kobject is registered, we broadcast it to other components of the system using kobject_uevent(). The files foo_kobject.c/h and foo_event.c/h contain code to broadcast an UEvent using a kobject.

foo_kobject.h (kernel code, for example, as part of your module)
#ifndef FOO_KOBJECT_
#define FOO_KOBJECT_

/* 
 * Partially copied from linux/samples/kobject/kset-example.c
 *
 * Released under the GPL version 2 only.
 */

/*
 * This is our "object" that we will create and register it with sysfs.
 */
struct foo_obj {
 struct kobject kobj;
 int foo;
};
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

struct foo_obj *
create_foo_obj(const char *name);

int 
foo_kobj_init(void);

void
foo_kobj_exit(void);

#endif

foo_kobject.c (kernel code, for example, as part of your module)
/*
 * Partially copied from linux/samples/kobject/kset-example.c
 *
 * Released under the GPL version 2 only.
 *
 */

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>

#include "foo_kobject.h"

/*
 * This module shows how to create a kset in sysfs called
 * /sys/kernel/foo
 * Then one kobject is created and assigned to this kset, "foo".  
 * In this kobject, one attribute is also
 * created and if an integer is written to these files, it can be later
 * read out of it.
 */

/* a custom attribute that works just for a struct foo_obj. */
struct foo_attribute {
 struct attribute attr;
 ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
 ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)

/*
 * The default show function that must be passed to sysfs.  This will be
 * called by sysfs for whenever a show function is called by the user on a
 * sysfs file associated with the kobjects we have registered.  We need to
 * transpose back from a "default" kobject to our custom struct foo_obj and
 * then call the show function for that specific object.
 */
static ssize_t foo_attr_show(struct kobject *kobj,
        struct attribute *attr,
        char *buf)
{
 struct foo_attribute *attribute;
 struct foo_obj *foo;

 attribute = to_foo_attr(attr);
 foo = to_foo_obj(kobj);

 if (!attribute->show)
  return -EIO;

 return attribute->show(foo, attribute, buf);
}

/*
 * Just like the default show function above, but this one is for when the
 * sysfs "store" is requested (when a value is written to a file.)
 */
static ssize_t foo_attr_store(struct kobject *kobj,
         struct attribute *attr,
         const char *buf, size_t len)
{
 struct foo_attribute *attribute;
 struct foo_obj *foo;

 attribute = to_foo_attr(attr);
 foo = to_foo_obj(kobj);

 if (!attribute->store)
  return -EIO;

 return attribute->store(foo, attribute, buf, len);
}

/* Our custom sysfs_ops that we will associate with our ktype later on */
static const struct sysfs_ops foo_sysfs_ops = {
 .show = foo_attr_show,
 .store = foo_attr_store,
};

/*
 * The release function for our object.  This is REQUIRED by the kernel to
 * have.  We free the memory held in our object here.
 *
 * NEVER try to get away with just a "blank" release function to try to be
 * smarter than the kernel.  Turns out, no one ever is...
 */
static void foo_release(struct kobject *kobj)
{
 struct foo_obj *foo;

 foo = to_foo_obj(kobj);
 kfree(foo);
}

/*
 * The "foo" file where the .foo variable is read from and written to.
 */
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
   char *buf)
{
 return sprintf(buf, "%d\n", foo_obj->foo);
}

static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
    const char *buf, size_t count)
{
 sscanf(buf, "%du", &foo_obj->foo);
 return count;
}

static struct foo_attribute foo_attribute =
 __ATTR(foo, 0666, foo_show, foo_store);

/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *foo_default_attrs[] = {
 &foo_attribute.attr,
 NULL, /* need to NULL terminate the list of attributes */
};

/*
 * Our own ktype for our kobjects.  Here we specify our sysfs ops, the
 * release function, and the set of default attributes we want created
 * whenever a kobject of this type is registered with the kernel.
 */
static struct kobj_type foo_ktype = {
 .sysfs_ops = &foo_sysfs_ops,
 .release = foo_release,
 .default_attrs = foo_default_attrs,
};

static struct kset *example_kset;

struct foo_obj *create_foo_obj(const char *name)
{
 struct foo_obj *foo;
 int retval;

 /* allocate the memory for the whole object */
 foo = kzalloc(sizeof(*foo), GFP_KERNEL);
 if (!foo)
  return NULL;

 /*
  * As we have a kset for this kobject, we need to set it before calling
  * the kobject core.
         */
 foo->kobj.kset = example_kset;

 /*
  * Initialize and add the kobject to the kernel.  All the default files
  * will be created here.  As we have already specified a kset for this
  * kobject, we don't have to set a parent for the kobject, the kobject
  * will be placed beneath that kset automatically.
  */
 retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
 if (retval) {
  kobject_put(&foo->kobj);
  return NULL;
 }

 /*
  * We are always responsible for sending the uevent that the kobject
  * was added to the system.
  */
 kobject_uevent(&foo->kobj, KOBJ_ADD);

 return foo;
}

static void destroy_foo_obj(struct foo_obj *foo)
{
 kobject_put(&foo->kobj);
}

int
foo_kobject_init(void)
{
 /*
  * Create a kset with the name of "kset_foo",
  * located under /sys/kernel/
  */
 example_kset = kset_create_and_add("kset_foo", NULL, kernel_kobj);
 if (!example_kset)
  return -ENOMEM;

}

void 
foo_kobject_exit(void)
{
 destroy_foo_obj(foo_kobj);
 kset_unregister(example_kset);
}

foo_uevent.h (kernel code, for example, as part of your module)
#ifndef FOO_UEVENT_
#define FOO_UEVENT_

enum FOO_event_type {
  FOO_GET = 1,
  FOO_SET
};

struct foo_event {
  enum foo_event_type etype;
};

int foo_init_events(void);

int foo_send_uevent(struct foo_event *fooe);

#endif


In the following code, we send an event string as part of our new UEvent. In our case, just the type of the event (FOO_GET or FOO_SET). foo_uevent.c (kernel code, for example, as part of your module)
#include <linux/kobject.h>
#include "foo_kobject.h"
#include "foo_uevent.h"

static struct foo_obj *foo_kobj;

int foo_init_events(void)
{
  int ret;

  ret = example_init();
  if (ret) 
  { 
    printk("error - could not create ksets\n");  
    goto foo_error;
  } 
 
  foo_kobj = create_foo_obj("foo");
  if (!foo_kobj)
  {
    printk("error - could not create kobj\n"); 
    goto foo_error;
  }
 
  return 0;

foo_error:
  return -EINVAL;
}

int foo_send_uevent(struct foo_event *pce)
{
  char event_string[20];
  char *envp[] = { event_string, NULL };

  if (!foo_kobj)
    return -EINVAL;

  snprintf(event_string, 20, "FOO_EVENT=%d", pce->etype);

  return kobject_uevent_env(&foo_kobj->kobj, KOBJ_CHANGE, envp);
}

So far, we've only made changes in the kernel level. But also, we need to listen to UEVENTs in the userspace. For that, I changed a little bit the Dalvik and added the following binder to listen to UEvents and send notifications, so that the user can see the event.

For more details on how to add a system service in Android, check this link. In this case, I added a Binder, as we are not providing any service (setting an integer, for instance). In our example, we simply broadcast events to the user interface.

To add the following Binder, create a file that contains the code into framework/base/services/java/com/android/server/. You also have to change the file Android.mk at framework/base/services/jni/Android.mk

...
LOCAL_SRC_FILES:= \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
     com_android_server_connectivity_Vpn.cpp \
+    com_android_server_pm_PackageManagerService.cpp \
     onload.cpp
...

Also, don't forget to register the service (check the link that I mentioned before!), by changing the file framework/base/services/java/com/android/server/SystemServer.java

class ServerThread extends Thread {
...
         WifiP2pService wifiP2p = null;
         WifiService wifi = null;
         NsdService serviceDiscovery= null;
+        MyServiceBinder myService = null;
         IPackageManager pm = null;
         Context context = null;
         WindowManagerService wm = null;
...
         class ServerThread extends Thread {
...
             }

             try {
+                Slog.i(TAG, "My Service - Example");
+                myService = MyServiceBinder.create(context);
+                ServiceManager.addService(
+                        Context.MY_SERVICE, myService);
+            } catch (Throwable e) {
+                reportWtf("starting Privacy Capsules Service", e);
+            }
+
...

Finally, here is the Java code. This Binder simply listens to UEvents that contains the string "FOO_EVENT", which also embeds the integer as part of the UEvent. Then, we create a UI notification and we attach an Intent to the notification, so that when the user clicks on it, a Activity or a Dialog is launched.
...
public class MyServiceBinder extends Binder {

    private final int FOO_GET = 1;
    private final int FOO_SET = 2;

    private MyServiceWorkerThread mWorker;
    private MyServiceWorkerHandler mHandler;
    private Context mContext;
    private NotificationManager mNotificationManager;

    /*
     * We create an observer to listen to the UEvent we are interested in. 
     * See UEventObserver#startObserving(String). From the UEvent, we extract 
     * an existing integer field and we propagate create a notification to 
     * be seen by the user.
     */
    private final UEventObserver mUEventObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            final int eventType = Integer.parseInt(event.get("FOO_EVENT"));
            sendMyServiceNotification(eventType);
        }
    };

    public MyServiceBinder(Context context) {
        super();
        mContext = context;
        mNotificationManager = (NotificationManager)mContext.getSystemService(
                               Context.NOTIFICATION_SERVICE);

         /* We have to specify a filter to receive only certain UEvents from the 
          * kernel. In this case, the UEVENT that we want to listen to contains 
          * the string "FOO_EVENT".
          */
        mUEventObserver.startObserving("FOO_EVENT=");

        mWorker = new MyServiceWorkerThread("MyServiceWorker");
        mWorker.start();
    }

    /*
     * Add potential initialization steps here
     */
    public static MyServiceBinder create(Context context) {
        MyServiceBinder service = new MyServiceBinder(context);
        return service;
    }
    
    /*
     * Sends notification to the user. In this case, we are create notifications. 
     * If the user clicks on the notification, we send an Intent so that an system 
     * application is launched. For example, if we are listening to notifications 
     * for USB UEvents, the USB Settings application can be launched when the 
     * notification is clicked.
     */
    private final void sendNotificationToUser(int type) {
        String notificationMessage = "";
        Resources r = mContext.getResources();
        switch(type) {
                case FOO_GET:
                // do something if the event type is A
                notificationMessage = "This is event type FOO_GET";
                break;
                case FOO_SET:
                // do something if the event type is B
                notificationMessage = "This is event type FOO_SET";      
                break;
                default:
                break;
        }  

        String notificationTitle = r.getString(R.string.my_service_notification_title);
        long notificationWhen = System.currentTimeMillis();
        int requestID = (int) notificationWhen;
 
        Intent intent = new Intent(Intent.ACTION_FOO_EVENT);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        intent.putExtra(FooManager.EXTRA_ETYPE, type);
        intent.putExtra(FooManager.EXTRA_MESSAGE, notificationMessage);

        PendingIntent pi = PendingIntent.getActivityAsUser(mContext, requestID,
                                intent, 0, null, UserHandle.CURRENT);

        Notification notification = new Notification.Builder(mContext)
                        .setSmallIcon(R.drawable.indicator_input_error)
                        .setContentTitle(notificationTitle)
                        .setContentText(notificationMessage)
                        .setPriority(Notification.PRIORITY_HIGH)
                        .setDefaults(0)
                        .setWhen(notificationWhen)
                        .setOngoing(true)
                        .setContentIntent(pi)
                        .setAutoCancel(true)
                        .build();

        mNotificationManager.notifyAsUser(null,
                        R.string.my_service_notification_title,
                        notification, UserHandle.ALL);
    }


    /*
     * Runner for the 
     *
     */
    private void sendMyServiceNotification(final int type) {
        
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                sendNotificationToUser(type);
            }
        });

    }

    private class MyServiceWorkerThread extends Thread {

        public MyServiceWorkerThread(String name) {
            super(name);
        }

        public void run() {
            Looper.prepare();
            mHandler = new MyServiceWorkerHandler();
            Looper.loop();
        }

    }
 
    private class MyServiceWorkerHandler extends Handler {

        private static final int MESSAGE_SET = 0;

        @Override
        public void handleMessage(Message msg) {
            try {
                if (msg.what == MESSAGE_SET) {
                    Log.i(TAG, "set message received: " + msg.arg1);
                }
            } catch (Exception e) {
                // Log, don't crash!
                Log.e(TAG, "Exception in MyServiceWorkerHandler.handleMessage:", e);
            }
        }

    }
}

Sunday, May 10, 2015

Android Linux Kernel module building/installation

Let's now show how to install a module into the just compiled Android kernel (see this post for more information)

For compiling the module, it's important that you use the same kernel source that is installed in your device. Otherwise, you cannot install the module.

a. Go to the code that contains an example of kernel module for Android (for instance, [your_code]/module/intercept);
b. Update the makefile to point to your kernel source code;
c. You need to set some env variables, including the cross-compiler. In this case, you can use the ones provided by the Android source, in the folder prebuilts:

   @desktop:$ export CROSS_COMPILE=[android_sdk]/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-
   @desktop:$ export ARCH=arm
   @desktop:$ export PATH=$PATH:/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/
   @desktop:$ make
   (a kernel module file will be generated ([your_module].ko) and this is the one that we need to install in our rooted device)

d. Copy the .ko file to the device using the following command:

   @desktop:$ adb push [your_module].ko /data/local/tmp

e. Install the kernel module using the following commands:

   @desktop:$ adb shell
   @android:$ su
   @android:$ cd /data/local/tmp
   @android:$ insmod [your_module].ko

f. You might get some errors, such as "function not implemented". To check more details about what's wrong, you can check the log file by typing the following command.

   @android:$ dmesg

Friday, May 8, 2015

Some other tips on building your AOSP for development

As you need to implement your solution into Android system, you end up learning a lot about the different Android layers (kernel, OS and applications) and how to integrate them. I decided to add the following list with some tips, as these small things took me some precious time to get it solved. The list will be often edited:

#01 - Make sure that you're flashing the device with the proper kernel image

This is what happened to me: I had previously built the kernel (something like two months before). Then, I had to build the OS image from scratch, that is, cleaning up the previous build (with make clobber). When I used the command make bootimage, including setting the variables properly, the output always had the wrong kernel image (not the one that I had previously built, but the existing one in the directory prebuilts). The build process won't take too old kernel images. Therefore, make sure that the compressed kernel image is always new. Even if you don't make any change on the kernel source, do make again to generate a new file.

Saturday, August 2, 2014

Problem on linking OpenSSL into your NDK application

This week, I tried to compile a simple NDK application and link it with the OpenSSL library. Most of libraries (including OpenSSL) are not supported by the NDK, what makes it a bit more complicated to use. So, in this post, I describe what I usually do to properly compile applications that need external libs.

The project structure is as follows:

my_project/
    + jni/my_code.c
    + jni/Android.mk
    + jni/Application.mk
    + libs/system

Problem #01: How my Android.mk looks like?

In this example, I need two libraries: libcrypto.so and libssl.so. So, the final Android.mk looks like this

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := my_exec
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := my_code.c
LOCAL_C_INCLUDES += $(ANDROID_SRC)/external/openssl/include \ 
                    $(ANDROID_SRC)/external/openssl/crypto \ 
                    $(KERNEL_SRC)/kernel/module
LOCAL_LDLIBS += -L$(LOCAL_PATH)/../libs/system
LOCAL_SHARED_LIBRARIES := libandroid libdl libz libcrypto libssl
LOCAL_LDLIBS += -landroid -ldl -lz


include $(BUILD_EXECUTABLE) 

See, as an example, that we also include the headers for the library (in ANDROID_SRC/external/openssl/include).

NDK does not provide support for libcrypto.so and libssl.so -- we need to have access to the libraries somehow. So, you should create a folder (for example, my_project/libs/system) and push the files /system/lib/libcrypto.so and /system/lib/libssl.so from the device to such folder.

Problem #02: Where do I get the libs from?

You shall get all of them (including libc.so) from your rooted device. The point is that, as I said, NDK does not provide support for libcrypto.so and libssl.so. Therefore, you need to get such libraries from the device. However, there's another problem: most likely, the libcrypto.so and libssl.so libraries don't recognize the symbols from the NDK libc.so and libstdc++.so libraries. Regarding this problem, it will be discussed on item #04.

Problem #03: Where do I get the headers from?

Usually, you get them from the android source code. For OpenSSL, they are located inside the folder ANDROID_SRC/external.

Problem #04: Why does my lib complain about libc symbols?

As I described in topic #02, you need to copy the libraries from the device. However, libcrypto.so and libssl.so do not recognize some symbols from the libc.so and libstdc++.so libraries (most likely, the libraries provided by NDK are not compatible with the ones in the device). Usually, you will have the following compilation error:

/home/raul/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__strlen_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__memcpy_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__memset_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: 
/home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__strchr_chk'
/home/raul/android_development/android-ndk-r10/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-
x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /home/raul/my_project/jni/../libs/system/libcrypto.so: error: undefined reference to '__strcat_chk'

That's easy to solve. Here comes the trick: you have to replace NDK's libraries by those ones from the device. NDK contains several libraries for distinct platforms, as we can see from the path NDK_FOLDER/platforms:

@desktop:$ ls NDK_FOLDER/platforms
android-12  android-13  android-14  android-15  android-16  android-17  
android-18  android-19  android-3   android-4   android-5   android-8  android-9

 Considering that your application is using NDK for platform android-17 (you can define that in file Application.mk), replace the main libraries of folder NDK_PATH/platforms/android-17

@desktop:$ ls NDK_FOLDER/platforms/android-17/arch-arm/usr/lib
crtbegin_dynamic.o  crtend_android.o  libc.a    libEGL.so       libjnigraphics.so libm_hard.a         libOpenSLES.so    libthread_db.so
crtbegin_so.o       crtend_so.o       libc.so   libGLESv1_CM.so  liblog.so       libm.so             libstdc++.a       libz.so

crtbegin_static.o   libandroid.so     libdl.so  libGLESv2.so     libm.a           libOpenMAXAL.so     libstdc++.so

So, push the libraries /system/lib/libc.so, and /system/lib/libstdc++.so from the device to the folder NDK_FOLDER/platforms/android-17/arch-arm/usr/lib. After that, compile the application again and voilĂ  -- all problems solved :-) 


Monday, July 28, 2014

Android image/kernel building/flashing - A *VERY* short guide :-)

This week, I had to go through the process of Android OS/Kernel building/installation. And it was a lot much better and 6 months ago (maybe, because I built it for a device and not for the emulator?). I compiled the images in Ubuntu 12.04 and I used a Samsung Galaxy Nexus device (maguro with tuna as kernel). Therefore, I decided to summarize the steps that I took. This mini-tutorial is a lot shorter and simpler (and really works!!).

Update (13/07.2015): info for arm64 => http://seandroid.bitbucket.org/BuildingKernels.html

1. Android OS

1.0 Setting up the building environment

Check this instructions (here and here) to set up the basic environment and download the code. I used the branch [android-4.3_r1.1].

1.1 Compiling the Android OS

a. Download and unpack the manufacturer drivers from this link. They have to be unpacked into the directory [android_source_code]/vendors -- but don't worry, as the .zip files contain a script that does all the work for you.

b. Once the drivers are in the proper place, run the following commands:

  @desktop:$ cd [android_source_code]
  @desktop:$ make clobber
  @desktop:$ lunch full_maguro-userdebug
  @desktop:$ make -j4

It takes a long time to compile the image.

After these steps, the Android OS is ready.

1.2 Flashing the device with the new Android OS

Now, you need two tools from the Android SDK: adb and fastboot. These tools are located in the folder [androis_sdk]/platform-tools.

a. Reboot the device in the bootloader mode -- hold VolumeDown and VolumeUp and then press the PowerUp button.

b. Connect the USB cable.

c. Run the following commands:

  @desktop:$ export PATH=$PATH:[android_sdk]/platform-tools
  @desktop:$ cd [android_source_code]
  @desktop:$ sudo fastboot format cache
  @desktop:$ sudo fastboot format userdata
  @desktop:$ sudo ANDROID_PRODUCT_OUT=[android_source_code]/out/target/product/maguro/ fastboot -w flashall

After these steps, reboot the device. A clean installation will take place. To check the new version of you device, go to "Settings" - - > "About Phone" and check "Model number": now, it should be "AOSP on Maguro" (check attached image)



2. Android Kernel

Ok. Now, we have the AOSP in place and we need to compile a new kernel. But why do you need to compile and install a new kernel? Oh, well, let's say that you want to apply some patches or that you need to change the kernel to enable Linux module support (the default Android Linux Kernel does not support modules).

2.0 Setting up the building environment

If you have built the Android OS before, you don't need anything special for the kernel building. I used the official code from https://android.googlesource.com/kernel/omap.git, branch android-omap-tuna-3.0-jb-mr2.

2.1 Compiling the Kernel

First, you need to set some variables that are important for the building process (ARCH and CROSS_COMPILE):

  @desktop:$ export ARCH=arm
  @desktop:$ export CROSS_COMPILE=[android_source_code]/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-

Now, you have to generate a .config which contains all the options for the kernel building. By running the following command you generate a basic .config file for Android.

  @desktop:$ cd [android_kernel_code]
  @desktio:$ make tuna_defconfig

Sometimes, you need to set some specific entries of the .config to enable/disable certain features of the kernel. For this specific example, let's set the option CONFIG_MODULES to y (the entry in the .config file should be CONFIG_MODULES=y). With CONFIG_MODULES set to y, it is possible to insert/remove kernel modules. Now, let's build the kernel

  @desktop:$ cd [android_kernel_code]
  @desktop:$ make

(it takes some time to compile the kernel)

2.2 Preparing the kernel for installation

The kernel image is almost ready: it's still necessary to wrap it up properly to flash it into the device. The Android source code contains scripts that do the work for us. Consider that the image was generated at [android_kernel_code]/arch/arm/boot/zImage.

  @desktop:$ cd [android_source_code]
  @desktop:$ export TARGET_PREBUILT_KERNEL= [android_kernel_code]/arch/arm/boot/zImage
  @desktop:$ make bootimage

At the end, a custom image is ready for installation at [android_source_code]/out/target/product/maguro/boot.img

2.3 Flashing the device with the new Kernel
 
Now, everything is in place and we can finally flash our kernel image. To do so:

a. You need to boot the device in bootloader mode (hold VolumeDown and VolumeUp and then press the PowerUp button)

b. Connect the USB cable

c. Run the following commands

  @desktop:$ cd [android_source_code]
  @desktop:$ sudo ANDROID_PRODUCT_OUT=[android_source_code]/out/target/product/maguro/ fastboot flash boot [android_source_code]/out/target/product/maguro/boot.img

After these steps, reboot the device. A clean installation will take place. To check the new version of you kernel, go to "Settings" - - > "About Phone" and check "Kernel version": you will see a different name for you kernel image (as for the previuos image).