<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    給安卓12內核增加個syscall

    VSole2023-01-10 10:37:53

    給android 12增加個syscall,沒想到這里面涉及東西還挺多的,網上的東西都太過陳舊(尤其是bionic這里),中間踩了不少坑。單獨寫一篇文章記錄下怎么給android 12添加個syscall。(現在依舊還是有問題,printk的日志dmesg看不到)

    環境:lineageos 19.1

    編譯環境:Ubuntu 20.04

    實際上我操作的順序是本篇倒著來的,正序寫比較方便觀看。

    1. 給libc添加個導出符號(可不做)

    直接在linux內核里添加了調用,直接syscall(_NR_ID)內核,是可以實現自定義syscall的,主要是這里也了解到了就順手記下。

    bionic調用gensyscall.py,從SYSCALL.TXT中讀文件,判斷哪些函數要生成syscall的匯編指令。

    在unistd.h中添加syscall id,跟著別的寫就行了,sys_antidebug_update是linux內核自定義函數,下面會提到。

    比如我在這里加了__antidebug_update函數,將會調用linux內核里的antidebug_update函數,在arm64架構下的。

    這句話的含義:

    return_type func_name[|alias_list]:syscall_name[:socketcall_id] arch_list

    # int __antidebug_update:antidebug_update(pid_t) arm64int getrusage(int, struct rusage*)  allint __getpriority:getpriority(int, id_t)  all
    

    生成的結果在:

    生成了這個函數的匯編,就可以在代碼里以extern c的方式直接調用__antidebug_update函數(其實就是syscall),我這里不想改makefile直接用了一個現成的內核cpp。

    然后添加libc的導出符號,在這個路徑下,編譯即可直接調用libc的函數實現封裝好的syscall。

    bionic工具生成的map文件在這里能看到:

    踩坑1

    syscall的編號_NR_xxx在bionic里面是從external\kernel-headers\original\uapi\asm-generic\unistd.h拷貝過來的,所以要改這個文件,直接改include下的是沒用的。這里我加了限定arm64生成syscall的宏,在下面linux內核里要做同樣的限定。

    踩坑2

    如果不想給arm的libc導出自己的函數,只想在arm64導出,函數聲明這里一定要加上預編譯宏。我在unistd.h里已經加了限定arm64架構才會生成antidebug_update的宏,這里如果不加,bionic那么就會嘗試arm架構的antidebug_update syscall匯編,因為找不到而報錯。

    后面再user層就可以先用extern c 聲明下 aadebug,然后直接調用了。

    2. 在linux內核里添加syscall

    先看異常分發,kernel\xiaomi\sm8250\arch\arm64\kernel\entry.S

    調用syscall會產生中斷,在arm64的匯編是svc,中斷產生異常后,進入svc handler。

    在kernel\xiaomi\sm8250\arch\arm64\kernel\syscall.c下,我復制一段關鍵代碼。

    asmlinkage void el0_svc_handler(struct pt_regs *regs){    sve_user_discard();    el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);} static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,               const syscall_fn_t syscall_table[]){       unsigned long flags = current_thread_info()->flags;     regs->orig_x0 = regs->regs[0];    regs->syscallno = scno;         ...    invoke_syscall(regs, scno, sc_nr, syscall_table);        ...}static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn){    return syscall_fn(regs);} static void invoke_syscall(struct pt_regs *regs, unsigned int scno,               unsigned int sc_nr,               const syscall_fn_t syscall_table[]){    long ret;    if (scno < sc_nr) {        syscall_fn_t syscall_fn;        syscall_fn = syscall_table[array_index_nospec(scno, sc_nr)];        ret = __invoke_syscall(regs, syscall_fn);    } else {        ret = do_ni_syscall(regs, scno);    }     regs->regs[0] = ret;}
    

    總結起來就一句話:

    syscall_table[scno](regs);
    

    編譯時生成syscall_table,根據id索引到handler的地址(所以hook syscall函數可以直接替換這個syscall_table[id]尋址得到的函數地址,主要是要找到syscall_table的地址,和call回原函數處理)。

    所以要添加個syscall,就需要做兩件事,定義個handler(SYSCALL_DEFINEx 宏包起來的聲明,x是參數數量,具體看其他函數實現就知道了),增加個NR_ID(如果上面第1點也做了需要bionic的NR_ID和linux的NR_ID一致)并聲明syscall。

    (1)定義handler

    圖方便還是用了先有文件kernel\xiaomi\sm8250\kernel\sys.c

    增加NR_ID,看好路徑,因為我再bionic也新增了syscall,這里要對應上sys_antidebug_update,這個前綴sys是SYSCALL_DEFINEx 宏添加的,所以這里面要加上sys,看其他syscall聲明或者展開下宏就知道了。

    添加完后build編譯不報錯,刷機,這里可以直接寫個demo驗證下。

    用 aarch64-linux-gnu-gcc 編譯(ubuntu 可以直接apt裝arm的交叉編譯工具),不要忘記-static選項,刷機到手機上運行下,可以看到函數邏輯是走了的。

    傳入參數123,return 123+100 ,所以syscall沒有問題。

    但我就是看不到printk打的日志去哪了,打算封裝個函數把日志寫到文件去了。unistd.h到處都是,看得人暈。

    修改源碼重新編譯,時間成本比較大,安裝驅動(.ko)是比較常用的了,有需要就syscall hook下,畢竟擴展性比較好,我也試過,內核編譯選項把強制校驗簽名關了,可以隨便安裝未簽名的驅動了,但同樣是printk打不出來日志。不過自己的環境定制內核還是最方便的,本著目的就是“一勞永逸”。

    kernel
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    kernel-pwn之ret2dir利用技巧
    Kernel-Pwn-FGKASLR
    2023-07-10 10:24:04
    是KASLR的加強版,增加了更細粒度的地址隨機化
    Kernel PWN從入門到提升
    2023-03-23 10:17:57
    所以我決定用此文章結合一道不錯的例題盡可能詳細的來講一下kernel pwn從入門過渡到較高難度的部分,供想要學習kernel pwn的小伙伴們參考。文件系統kernel題一般都會給出一個打包好的文件系統,因此需要掌握常用到的打包/解包命令。
    可是當我們開啟了smap保護之后,內核態就沒有辦法訪問用戶態的數據,此時當我們再hijack tty_operation到我們的用戶態時,我們的kernel就會panic,更別說劫持執行流到用戶態上執行rop了。當我們調用msgsnd時,在linux內核中會調用do_msgsnd。
    然而在內核態中,堆內存的分配策略發生了變化。并把這個slab劃分為一個個object,并將這些object組成一個單向鏈表進行管理,這里需要注意slub系統把內存塊當成object看待,而不是伙伴系統中的頁。本次選擇演示的例題是2019-SUCTF的sudrv例題,查看start.sh中的信息可以發現開啟了kaslr保護與smep保護。
    條件競爭類型的漏洞
    筆者分享的兩種利用方式都不算困難,但是需要注意
    前言Kernel ROP本質上還是構造ropchain來控制程序流程完成提權,不過相較于用戶態來說還是有了一些變化,這里選取的例題是2018年強網杯的賽題core,本來覺得學起來會很快的但是沒想到還是踩了不少坑。iretq指令則用來恢復用戶態的cs、ss、rsp、rip、rflags的信息。
    Kernel從0開始
    2021-12-10 13:42:20
    網上一大堆教編譯內核的,但很多教程看得特別迷糊。第一次編譯內核時,沒設置好參數,直接把虛擬機編譯炸開了。所以就想著能不能先做個一鍵獲取內核源碼和相關vmlinux以及bzImage的腳本,先試試題,后期再深入探究編譯內核,加入debug符號,所以就有了這個一鍵腳本。
    Kernel pwn CTF 入門 – 1
    2021-10-21 16:39:08
    01簡介內核 CTF 入門,主要參考 CTF-Wiki。02環境配置調試內核需要一個優秀的 gdb 插件,這里選用 gef。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类