Category Archives: C/C++

Examining and exploiting android vendor binder services – part1

Vendor binder services proved to be an interesting part of android devices nature. They usually remains close-source, but sometimes open new attack surface for privilege escalation. In these articles I will first describe how to locate interesting binder service and a reversing quirk, then two typical CVEs will be discussed about their nature and exploitation, and how to find them.

Locating interesting binder services

Before Android N, all binder services were registered to servicemanager, and communicated with each other under /dev/binder. After Android N, binder domains are splitted to normal domain under /dev/binder, vendor domain under /dev/vndbinder, and hardware domain under /dev/hwbinder. Normal untrusted_app access is restricted to /dev/binder.

There’re possibly more than 200 binder services on some vendor devices, and most of them are Java services. Java code may introduce common android logic problems, which we will not cover in this blog post. Currently we are only interested in memory corruption bugs lies in native code.

So first question arises: how do we deduce which services may have interesting native code? Where are these services running?J previously released a tool named bindump, by reading binder data in debugfs, the tool can iterate which process owns a process and which processes are using this service. However days  have passed and android has involved pretty much, major problems including

  • debugfs is no longer readable by normal process so you will need root to run
  • Binder node now have context so you have to distinguish between different domain context
  • linking symbols changes in every version so one may not be able to reuse the binary and need to recompile the source on top of corresponding AOSP source branch

To solve problem 2 and 3, I rewrite the tool in Java and encapsulated it into a standalone jar, its source and binary can be found here.

With the tool to find hosting process for binder service, we move to look binder services in native processes.

CVE-2018-9143: buffer overflow in visiond service

There’s an arbitrary length/value overflow in the platform’s visiond daemon, which runs under system uid. visiond exposes a service named media.air, multiple dynamic libraries are used by this service, including visiond, libairserviceproxy, libairservice, libair. libairserviceproxy is the library with server-side method implementations, namely BnAIRClient::onTransact, BnEngine::onTransact, BnAIRService::onTransact.

Missing vtable entries?

An interesting fact worth noticing when reversing these libraries is that, in IDA at the address where we should find vtables, except top-offset and virtual-base offset, all other virtual method pointer items are all zero. However at runtime, the corresponding memory locations become normal. This does not appear on Nexus or Pixel image libraries when opened with IDA.

hedan vtable1

At first glance it may seem like a binary protection for anti-reversing. However after some digging in we found it’s acutally a new Android linker feature that was not supported by IDA. To understand this symposym, one should first be clear that the vtable function addresses does not naturally resides in Their offsets are actually stored in relocation entries at .rela.dyn with type R_AARCH64_RELATIVE or others. IDA and other disassembler tools do the job of linker, read, calculate and store the value into their respective locations. If IDA failed to parse the relocation entries, the target addresses will be left untouched, as seen in the screenshot.

So what’s the offending linker feature? Quoting from chromium docs:

Packed Relocations
All flavors of lib(mono) enable “packed relocations”, or “APS2 relocations” in order to save binary size.
Refer to this source file for an explanation of the format.
To process these relocations:
Pre-M Android: Our custom linker must be used.
M+ Android: The system linker understands the format.
To see if relocations are packed, look for LOOS+# when running: readelf -S
Android P+ supports an even better format known as RELR.
We'll likely switch non-Monochrome apks over to using it once it is implemented in lld.


This feature encodes binary data in SLEB128 format in order to save space for large binaries. The detailed implementation, including encoding and decoding algorithm can be found here( At runtime, the linker decodes this segment, rearranging other segment addresses and extracts normal relocation entries. Then everything goes back to normal. IDA at that time does not supported this encoding so all relocation infos are lost in IDA. One can use the relocation_packer tool itself to recover the addresses.

Update: Two years after Android introduced this feature IDA 7.3 has finally supported APS2 relocation, which can be seen in the following screenshot.

+ ELF: added support for packed android relocations (APS2 format)


AirService copies in the air

BnAirServiceProxy provides two methods for managing AirClient passed from client process. One accepts a StrongBinder (AirClient) and an int option, returns a binder handle (pointing to BnAIR) for client process to invoke.If the int option is 0, it will create a FileSource thread. If the option is 1, it will create a CameraSourceThread(only this can trigger this vulnerability)

BnAIR::transact (in has many functions, the functions relating to this exp is android::AIRService::Client::configure, start and enqueueFrame. We must call these functions in order of configure->start->enqueueFrame to do a property setup.The specific vulnerable function is enqueueFrame. enqueueFrame receives an int and IMemory, and the IMemory content is copied out to a previously allocated fixed IMemory in Frame object.

android::RefBase *__fastcall android::FrameManager::enqueueFrame(__int64 someptr, __int64 imemory)
 v4 = (android::FrameManager::Frame *)operator new(0x38uLL);
 android::FrameManager::Frame::Frame(v4, v5, *(_DWORD *)(v2 + 0x88), *(_DWORD *)(v2 + 140), 17, *(_DWORD *)(v2 + 144));
 v16 = v4;

 android::RefBase::incStrong(v4, &v16);

 (*(void (**)(void))(**(_QWORD **)v3 + 0x20LL))(); //offset and size is retrived

 v6 = (*(__int64 (**)(void))(*(_QWORD *)v16 + 88LL))(); //v6 = Frame->imemory->base();

 v7 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)imemoryheap + 40LL))(imemoryheap); //v7 = imemoryheap->base();

 memcpy(v6, v7 + v15, v14);//memcpy(frame->imemory->base(), imemoryheap->base() + offset, imemoryheap->size());//overflow here
 if ( imemoryheap )
     (android::RefBase *)(imemoryheap + *(_QWORD *)(*(_QWORD *)imemoryheap - 24LL)),
 result = v16;
 if ( v16 )
   result = (android::RefBase *)android::RefBase::decStrong(v16, &v16);

 return result;


The detailed steps are as follows:

  1. We use first transact with code=3 from untrusted_app to /system/bin/visond service, to trigger android::AIRService::Client::configure(int) in This transact is needed to init some parameters
  2. The second transact with code=4, which starts an AIRService Client, android::AIRService::Client::start() start the Client to accept the final transaction
  3. The final transact, with code=7, actually passes an IMemory with attacker controlled length/content, to trigger android::AIRService::Client::enqueueFrame(int, android::spandroid::IMemory const&).

The mmaped area in Frame itself is usually 2M, so any passed in Imemory with size > 2M will trigger overflow.

    fpsr 00000000  fpcr 00000000
    #00 pc 000000000001b014  /system/lib64/ (memcpy+332)
    #01 pc 0000000000029b5c  /system/lib64/ (_ZN7android12FrameManager12enqueueFrameERKNS_2spINS_7IMemoryEEE+188)
    #02 pc 0000000000030c8c  /system/lib64/ (_ZN7android10AIRService6Client12enqueueFrameEiRKNS_2spINS_7IMemoryEEE+72)
    #03 pc 000000000000fbf8  /system/lib64/ (_ZN7android5BnAIR10onTransactEjRKNS_6ParcelEPS1_j+732)
    #04 pc 000000000004a340  /system/lib64/ (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+132)
    #05 pc 00000000000564f0  /system/lib64/ (_ZN7android14IPCThreadState14executeCommandEi+1032)
    #06 pc 000000000005602c  /system/lib64/ (_ZN7android14IPCThreadState20getAndExecuteCommandEv+156)
    #07 pc 0000000000056744  /system/lib64/ (_ZN7android14IPCThreadState14joinThreadPoolEb+128)
    #08 pc 0000000000074b70  /system/lib64/
    #09 pc 00000000000127f0  /system/lib64/ (_ZN7android6Thread11_threadLoopEPv+336)
    #10 pc 00000000000770f4  /system/lib64/ (_ZL15__pthread_startPv+204)
    #11 pc 000000000001e7d0  /system/lib64/ (__start_thread+16)


This may be similar to Project Zero’s bitunmap bug, since overflow occurs in mmaped area. When android::AIRService::Client::configure is called, a new thread is created in By allocating Frame, and removing Frame we can create a hole in highmem area. Allocate Frame again, trigger overflow and we can override the thread mmaped area to get PC control.

However, SElinux still enforces strict limitation (no execmem, no executable file loading, cannot lookup PackageManagerService in servicemanager), on visiond, although it’s a system-uid process. How can we utilize this elevated privilege to do things malicious like installing arbitrary app, if we even cannot access PackageManagerService?

Note that although SELinux prohibits visiond from looking up the PMS service, neither it nor the PMS itself has additional restrictions or checks when PMS’s transactions are actually invoked, if we have the service handle. So we may use the following steps to bypass this restriction:

  • Attacking app (untrusted_app context) obtains StrongBinder handle of PMS
  • Attacking app passes the handle to visiond. Any transaction method accepting and storing StrongBinder will work.
  • Attacking app triggers the vulnerability, gains code execution. Payload searches the specific memory location in step 2 to find the handle
  • Payload calls PMS’s installPackage method with the handle


So this’s CVE-2018-9143. Samsung has already posted advisory and pushed out FIX via firmware OTA. In next article I will describe CVE-2018-9139, a heap overflow in sensorhubservice, and how we examined and fuzzed the target to find this vulnerability, with a PC-controlled POC.

Bonus: POC for this CVE and vulnerable binary has been posted at for your reference.

一个矩形pwn掉整个内核系列之一 – zone的舞蹈

一个矩形pwn掉整个内核系列之一 – zone的舞蹈

一个矩形pwn掉整个内核?这听起来很马德里不思议,然而这真实地发生在了今年3月份温哥华的Pwn2Own赛场。这一系列文章会向大家分享我们这次沙箱逃逸用到的Blitzard CVE-2016-1815的发现和利用经历。我们通过三步走最终完成了这个利用,本文将先问大家介绍第二和第三步 – kalloc.48的舞蹈kalloc.8192 重剑无锋,在最后一篇文章中,我们会回到本源,介绍这个漏洞的起因。

Continue reading

The Journey of a complete OSX privilege escalation with a single vulnerability – Part 1

The Journey of a complete OSX privilege escalation with a single vulnerability – Part 1

In previous blog posts Liang talked about the userspace privilege escalation vulnerability we found in WindowServer. Now in following articles I will talk about the Blitzard kernel bug we used in this year’s pwn2own to escape the Safari renderer sandbox, existing in the blit operation of graphics pipeline. From a exploiter’s prospective we took advantage of an vector out-of-bound access which under carefully prepared memory situations will lead to write-anywhere-but-value-restricted to achieve both infoleak and RIP control. In this article we will introduce the exploitation methods we played with mainly in kalloc.48 and kalloc.4096.

First we will first introduce the very function which the overflow occurs, what we can control and how these affect our following exploitation.

Continue reading

使用Win32 API获取当前活动TCP/UDP连接端口对应进程信息

主要使用的API: GetTcpTable2和GetExtendedUdpTable(GetExtendedTcpTable)
Continue reading


第一个函数GetEncoderClsid属于GDI+,根据类型参数返回编码方式,然后调用GDI+ Bitmap对象的Save函数保存文件。
GDI+可以提供转化到png bmp gif jpeg tiff等多种格式,而不仅仅是GDI的bmp。
以下是代码,注意调用时要首先初始化GDI+,否则可能在GetEncoderClsid中会出现Access Violation
Continue reading