原创文章,转载请注明:转载自All-iPad.net本文链接地址:如何用gdb找到Android so文件中的加密key

来自XDA developer的一篇文章,作者详细描述了如何使用gdb + ida找到Angry Birds RIO Android版中lua文件的加密key,虽然目前还不会反向工程,不过说不定哪一天会用上,先记录下来。

Well… I have attached a debugger to native code, set breakpoints, analyzed registers, memory, etc. It wasn’t that easy though. It took me several days to start debugging and get first key, but I got second one in about 1 hour.

Actually I don’t really need that key, I can’t even playAngry BirdsRio on my old G1, but it was challenging and I love challengesPlus I have learnt a LOT aboutgdb, assembler, ARM architecture, etc.

So I want to thank you, Goddchen, for giving me an opportunity to learn & play

Ok, let’s move on…

First, I have disassembled libangrybirds.so using IDA Pro 5.5 . I was able to examine code and attach IDA to gdbserver on a device, but unfortunately it wasn’t working properly. IDA was thinking that libangrybirds.so is a main binary of a process it attached to, but it should look into loaded shared libs instead. Weird, but I didn’t find a way to attach it properly. And this is pity, because IDA is a great tool and it would make debugging a pleasure, but I had to use gdb instead.

Second,Androidhas problems with debugging multi-threaded native code. MT support was added in NDK r5 and because of some bug it’s not possible on a system older than Gingerbread.

Third, you could attach gdb manually, but ndk-gdb script does great work for you. You will have to do some tricks to use it with 3rd party app though.

Fourth, it seems libangrybirds.so is a Java code compiled to native or something like that. There are objects like FileInputStream, ByteOutputStream, etc., but there are also some API differencies. We’ll see String and Array<uchar> objects, but it’s usually easy to find a pointer to simple uchar[].

Steps to start native code debugging:

  1. Upgrade to Gingerbread (Yeah, I had to do that. Hacking requires you to sacrifice yourself a bit). Or you could use an emulator.
  2. Install NDK >= r5 .
  3. Decode Angry Birds Rio using apktool. You could just unzip it, but decoded app is much more similiar to original sources, so it’s more compatible with NDK. For example ndk-gdb reads AndroidManifest.xml to get package name. Of course you could fake simple AndroidManifest.xml and other files if you want.
  4. Rename lib dir to libs.
  5. Fake jni/Android.mk file. I have copied one from hello-jni sample and didn’t even bother to modify module name:http://pastebin.com/HMBXt5cm.
  6. Copy libs/armeabi*/libangrybirds.so to obj/local/armeabi*/ . Normally this is done by ndk-build command.
  7. Fake libs/armeabi*/gdb.setup file. It should be something like:http://pastebin.com/BYm13RKz, but second line isn’t that important.
  8. Angry Birds Rio apk contains old gdbserver and you need one from NDK r5. Grab ${NDK_ROOT}/toolchains/arm-linux-androideabi-4.4.3/prebuilt/gdbserver and push it to /data/data/com.rovio.angrybirdsrio/lib .
  9. Ufff… you could now try to run: ndk-gdb –verbose –launch=com.rovio.ka3d.App .
  10. After few seconds you should see “(gdb)” prompt and game should be paused on the device.
  11. Run ‘info shared’ and check if libangrybirds.so is loaded. If not then something is wrong.

Ok, let’s find a key for levels lua files:

  1. Set a breakpoint for GameLua::loadLevel() – find this method in IDA Pro and copy its EXPORT name: Code:
    (gdb) br _ZN7GameLua9loadLevelEN4lang6StringEBreakpoint 1 at 0x80468e4c

  2. Resume game and open some level. You should hit a breakpoint: Code:
    (gdb) cContinuing.[New Thread 5857][Switching to Thread 5857]Breakpoint 1, 0x80468e4c in GameLua::loadLevel () from /home/brutall/t-angrybirds/com.rovio.angrybirdsrio-1/obj/local/armeabi/libangrybirds.so

  3. Look into IDA and note there are 2 lang::String objects passed as first arguments to method, so pointers are in R1 and R2 registers. We need to examine these objects and find pointers to raw char[]. Fortunately lang::String is very simple wrapper around char[], so pointer is first (and only one, I think) member of String: Code:
    (gdb) x/4x $r10x4395e66c:0x00a405f00x00153b280x804ec7780x00000000(gdb) x/s 0x00a405f00xa405f0: "levels/warehouse/Level190"

    Yey, finally we see something

  4. Let’s move to lang::AESUtil::decrypt() method. It’s named _ZN4lang7AESUtil7decryptERKNS_5ArrayIhEES4_RS2_, so: Code:
    (gdb) advance _ZN4lang7AESUtil7decryptERKNS_5ArrayIhEES4_RS2_0x80539894 in lang::AESUtil::decrypt () from /home/brutall/t-angrybirds/com.rovio.angrybirdsrio-1/obj/local/armeabi/libangrybirds.so

  5. As you can see decrypt() gets 3 Array<uchar> objects and 2 of them are const. It’s quite easy to guess they’re: key, encrypted data and container for decrypted data. Let’s check this: Code:
    (gdb) x/4x $r10x1592b0:0x001595280x000000200x000000200x7b206e65

    0×00000020 = 32 – yes, length of AES keyFirst 4 bytes of an Array object is a pointer to raw char[] and second 4 bytes contain length of an array. Now we could read contents of an Array:

    Code:
    (gdb) x/s 0x001595280x159528: "USCaPQpA4TSNVxMI1v9SK9UC0yZuAnb2a"

    As you can see there are 33 chars instead of 32. This is because Array stores its length, so char[] isn’t null-terminated. Ignore last “a” char.

  6. We could also look into second const Array to be sure that encoded string is exactly the same as contents of lua file: Code:
    (gdb) x/4x $r20x4395d6f4:0x009ca2480x000004a00x000004a00x00000378(gdb) x/4x 0x009ca2480x9ca248:0x3347b5dc0x260484460x1a0c12310x35d3f99c

    First 16 bytes are the same, length of data is also ok.

As you can see there is AES::BlockMode passed to AES:ecrypt(). It would be quite hard to interpret it without headers, so I was trying various block modes and I found that CBC with empty initial vector decodes to string starting with ’7z’. For me that meant: mission successfull

Ok, highscores.lua and settings.lua files now. Technique is very similar, but there are some differences:

  • Different keys.
  • They aren’t loaded using GameLua::loadLevel(), but GameLua::loadPersistentFile(). You could find this very easily, searching for “highscores.lua” in IDA.
  • If you examine GameLua::loadPersistentFile() method you will see it doesn’t load files using FileInputStream, but io::AppDataInputStream, so we have to be sure, what exactly is being decrypted.
  • Annoying thing is that gdb can’t catch highscores/settings loading, because they’re loaded too soon – before gdb attach itself.

Maybe there is a better solution to last problem, but I’ve decided to add some Thread.sleep() call just after System.loadLibrary(), so gdb will attach before highscores.lua loading.

  1. Open smali/com/rovio/ka3d/App.smali, and add 2 lines of code just after loadLibrary() call in onCreate() method: Code:
        invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V    const-wide/16 v0, 5000    invoke-static {v0, v1}, Ljava/lang/Thread;->sleep(J)V

  2. Run ndk-gdb –verbose –launch=com.rovio.ka3d.App .
  3. Set a breakpoint for GameLua::loadPersistentFile() method and check which file is being loaded: Code:
    (gdb) br _ZN7GameLua18loadPersistentFileERKN4lang6StringEBreakpoint 1 at 0x80457030(gdb) cContinuing.[New Thread 6735][Switching to Thread 6735]Breakpoint 1, 0x80457030 in GameLua::loadPersistentFile () from /home/brutall/t-angrybirds/com.rovio.angrybirdsrio-1/obj/local/armeabi/libangrybirds.so(gdb) x/s $r20x4395e3b8: "highscores.lua"

    I’m not sure why it’s R2, not R1 and why there is no lang::String, but char[] directly. I think this isn’t a pointer to String, but String itself, passed to method in registers, so its char[] is in R2.

  4. Now advance to lang::AESUtil::decrypt() method and read key as usual: Code:
    (gdb) advance _ZN4lang7AESUtil7decryptERKNS_5ArrayIhEES4_RS2_0x80539894 in lang::AESUtil::decrypt () from /home/brutall/t-angrybirds/com.rovio.angrybirdsrio-1/obj/local/armeabi/libangrybirds.so(gdb) x/4x $r10x159294:0x001596200x000000200x000000200x00159518(gdb) x/s 0x001596200x159620: "44iUY5aTrlaYoet9lapRlaK1Ehlec5i0"

  5. Because of that AppDataInputStream object, we need to check if encrypted data is the same as file contents. Pull highscores.lua file from a device and run: Code:
    (gdb) x/4x $r20x4395ddc4:0x0015bc000x000000400x000000400x00000001(gdb) x/16x 0x0015bc000x15bc00:0x2271b7770xe6f19f4c0x2489a3160xfae1aee20x15bc10:0x82e0ef380xe84fc25d0xb196adac0xbf0304390x15bc20:0xb6b9bade0x3046af120xe8eeeb0d0x20e8037c0x15bc30:0x1a405edf0xc218f7f60xc29209e20x9ad03e8c

    Yeah, this is my highscores.lua file.

  6. Same for settings.lua file to check if it’s encrypted with the same key. It is.
  7. After decrypting these files we’ll see some weird chars at the end of decoded data. Few seconds on the Wikipedia and we’ll know this is just PKCS7 padding scheme.

Now we have got everything we want

Ahh, not exactly everything… I would be really happy to know, how to properly attach IDA for debugging – it would be much easier, even if gdb interface is also very good.

哦,忘了说一下这篇文章的作者,Brut.all,看到这个名字你应该就能够联想到,如果你有一台Android智能手机的话。没错,这位就是Brut Google Maps的作者,我的手机上就有他的App,另外他还发布了ApkTool,在Google Code上的开源工具,专门用来对apk做反向工程,当然现在也还用不着。

原创文章,转载请注明:转载自All-iPad.net

本文链接地址:如何用gdb找到Android so文件中的加密key

更多相关文章

  1. android 动态加载布局文件三种方法
  2. Android(安卓)aapt自动打包工具详细介绍
  3. Android获取assets文件夹中的数据并写入SD卡示例
  4. Android中使用自定义的字体
  5. android stduio gradle参数配置说明
  6. 第一章:初入Android大门(Style 样式和Button事件)
  7. Mac OS X下搭建Android(安卓)Source编译环境的问题及解决方法
  8. Android:inflate.inflate()方法
  9. Android模拟器SD卡的使用

随机推荐

  1. Android开发者指南(1) ―― Android(安卓
  2. Android优缺点
  3. Android电话拨打流程源码分析
  4. Android高性能编码实战:网络框架优化
  5. 更新Android(安卓)SDK到3.0版本时,遇到Fai
  6. Android客户端与Tomcat服务器通信实现登
  7. Android技术点总结
  8. android之字体阴影效果
  9. Android菜单详解(一)——理解Android中的Me
  10. 【Android(安卓)Developer】2.Android的