欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解決Android加殼過程中mprotect調(diào)用失敗的原因分析

 更新時(shí)間:2022年01月18日 09:56:12   作者:kxliping  
本文探討的主要內(nèi)容是mprotect調(diào)用失敗的根本原因,以及在加殼實(shí)現(xiàn)中的解決方案,通過本文的闡述,一方面能夠幫助遇到同類問題的小伙伴解決心中的疑惑,另一方面能夠給大家提供可落地的實(shí)現(xiàn)方案,需要的朋友可以參考下

問題原由

函數(shù)抽取殼是當(dāng)前最為流行的DEX加殼方式之一,這種加殼方式的主要流程包含兩個(gè)步驟:一、將DEX中需要保護(hù)的函數(shù)指令置空(即抽取函數(shù)體);二、在應(yīng)用啟動(dòng)的過程中,HOOK 類的加載過程,比如ClassLinker::LoadMethod函數(shù),然后及時(shí)回填指令。

筆者在實(shí)現(xiàn)抽取殼的過程中遇到了一個(gè)問題,即在步驟二回填指令之前,需要先調(diào)用mprotect將目標(biāo)內(nèi)存設(shè)置為“可寫”,但在初次嘗試過程中一直調(diào)用失敗,于是有了今天這篇文章。

本文探討的主要內(nèi)容是mprotect調(diào)用失敗的根本原因,以及在加殼實(shí)現(xiàn)中的解決方案,通過本文的闡述,一方面能夠幫助遇到同類問題的小伙伴解決心中的疑惑,另一方面能夠給大家提供可落地的實(shí)現(xiàn)方案。

調(diào)用mprotect修改內(nèi)存失敗的現(xiàn)象

以下代碼塊截取自自定義LoadMethod函數(shù),其目標(biāo)是將目標(biāo)函數(shù)指令所在內(nèi)存頁的屬性修改為可寫——通過mprotect函數(shù)的參數(shù)“PROT_WRITE”指定,實(shí)際結(jié)果是mprotect調(diào)用失敗了,返回”-1“,errno為”13“

int pagesize = sysconf(_SC_PAGESIZE);
int protectsize = pagesize;
byte *code_item_start = static_cast<byte *>(code_item_addr) + 16;
void *protectaddr = (void*) ((int) code_item_start - ((int) code_item_start % pagesize) - pagesize);
LOGD("process:%d,enter loadmethod:protectaddr:%p,protectsize:%d", getpid(), protectaddr, protectsize);
int result = mprotect(protectaddr, protectsize,  PROT_WRITE);
LOGD("mprotect return 0: %d, errno: %d", result, errno);

”13“號(hào)errno的符號(hào)為EACCES,查看linux手冊可知是權(quán)限問題。手冊中給出一個(gè)可能的場景,即如果使用mmap映射一個(gè)以”只讀“模式打開的文件,然后使用mprotect嘗試修改內(nèi)存屬性為可寫,就會(huì)返回EACCES錯(cuò)誤。

EACCES The memory cannot be given the specified access.  This can
              happen, for example, if you mmap(2) a file to which you
              have read-only access, then ask mprotect() to mark it
              PROT_WRITE.

接下來我們將沿著這個(gè)可能的場景,首先驗(yàn)證DEX文件是否以只讀模式打開,然后再進(jìn)行下一步分析。

mprotect調(diào)用失敗的原因分析

使用strace跟蹤應(yīng)用的系統(tǒng)調(diào)用,驗(yàn)證了DEX文件的打開模式為只讀模式——"O_RDONLY",然后通過mmap2將DEX文件映射進(jìn)內(nèi)存,內(nèi)存屬性為只讀的私有映射。

[pid 13190] openat(AT_FDCWD, "/storage/emulated/0/payload.dex", O_RDONLY|O_LARGEFILE) = 49
mmap2(NULL, 2121728, PROT_READ, MAP_PRIVATE, 49, 0) = 0xcef7a000

為了進(jìn)一步證實(shí)并徹底理清背后的邏輯,我研究了下mprotect的設(shè)計(jì)文檔[1]。mprotect是用戶空間PAX的一部分,它的核心目標(biāo)是緩解可利用內(nèi)存漏洞被利用的情況,所以我理解mprotect實(shí)際上就是“memory protect”,它的主要目的是從安全的角度保護(hù)內(nèi)存:

The goal of MPROTECT is to help prevent the introduction of new executable

   code into the task's address space. This is accomplished by restricting the

   mmap() and mprotect() interfaces.

mprotect通過內(nèi)存屬性控制內(nèi)存的訪問權(quán)限,其中安全狀態(tài)良好的屬性組合包括如下幾種:

VM_WRITE

VM_MAYWRITE

VM_WRITE | VM_MAYWRITE

VM_EXEC

VM_MAYEXEC

VM_EXEC | VM_MAYEXEC

即內(nèi)存要么是“可寫”的,要么是“可執(zhí)行”的,“可寫”與“可執(zhí)行”必須互斥,這樣才能阻斷“寫入并執(zhí)行”的內(nèi)存攻擊。

理解了mprotect的設(shè)計(jì)理念之后,我們再回到本文所遇到的問題本身:為什么以只讀方式打開的DEX文件映射到內(nèi)存之后,無法使用mprotect修改為“可寫”內(nèi)存?

根據(jù)mprotect設(shè)計(jì)文檔的闡述,mprotect主要通過VM_MAYWRITE控制內(nèi)存是否可被設(shè)置為“可寫”,該屬性的設(shè)置時(shí)機(jī)在mmap調(diào)用之時(shí):

VM_WRITE | VM_MAYWRITE or VM_MAYWRITE if PROT_WRITE was requested at

mmap() time

mmap首先將所有可能的屬性標(biāo)致置位,然后再進(jìn)行合法性檢查:

kernel/msm/+/refs/heads/android-msm-vega-4.4-oreo-daydream/mm/mmap.c

/* Do simple checking here so the lower-level routines won't have
 * to. we assume access permissions have been handled by the open
 * of the memory object, so we don't do any here.
 */
vm_flags |= calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;

如果文件打開時(shí)未設(shè)置“可寫”屬性,則清除“VM_MAYWRITE”屬性。

kernel/msm/+/refs/heads/android-msm-vega-4.4-oreo-daydream/mm/mmap.c

if (!(file->f_mode & FMODE_WRITE))
 
vm_flags &= ~(VM_MAYWRITE | VM_SHARED);

最后mprotect會(huì)對(duì)相關(guān)屬性進(jìn)行檢查,如果VM_MAYWRITE沒有被設(shè)置,則不可通過mprotect設(shè)置內(nèi)存的寫屬性,返回EACCES錯(cuò)誤標(biāo)識(shí):

kernel/msm/+/refs/heads/android-msm-vega-4.4-oreo-daydream/mm/mprotect.c

/* newflags >> 4 shift VM_MAY% in place of VM_% */
if ((newflags & ~(newflags >> 4)) & (VM_READ | VM_WRITE | VM_EXEC)) {
error = -EACCES;
goto out;
}

通過strace日志可以證實(shí)mmap DEX文件到內(nèi)存的過程中并沒有設(shè)置VM_MAYWRITE和VM_WRITE,所以直接使用mprotect設(shè)置內(nèi)存為“可寫”的行為會(huì)被拒絕。

	
mmap2(NULL, 2121728, PROT_READ, MAP_PRIVATE, 49, 0) = 0xcef7a000

綜上,mprotect修改內(nèi)存為可寫的整個(gè)邏輯如下:

系統(tǒng)以只讀模式打開DEX文件,所以mmap在映射文件時(shí)清除了VM_MAYWRITE標(biāo)志,導(dǎo)致接下來在調(diào)用mprotect修改內(nèi)存為可寫的過程中,mprotect檢測目標(biāo)內(nèi)存未設(shè)置VM_MAYWRITE標(biāo)志,返回EACCES錯(cuò)誤代碼。

兩種可行的解決方案

在研究清楚原因之后,我們再來聊聊可能的解決方案。我這里給出兩種經(jīng)過驗(yàn)證的思路:

1)hook openat函數(shù),設(shè)置文件打開時(shí)的屬性為可讀寫——O_RDWR;

if(strstr(pathname,"payload")){
        LOGD("[myopenat] path: %s, flags: %d", (char*)pathname, flags);
        flags &= (~O_RDONLY);
        flags |= O_RDWR;
    }

2)hook mmap函數(shù),或者在mmap之前修改傳入mmap的標(biāo)簽,直接將內(nèi)存屬性修改為“可寫”。這里我們以后面一種思路為例,HOOK MemMap::MapFileAtAddress函數(shù),在調(diào)用mmap映射文件之前修改prot參數(shù):

art/runtime/mem_map.cc

void* myMapFileAtAddr(int expected_ptr, int byte_count, int prot, int flags, int fd, int start, int low_4gb, int reuse, char *filename, int error_msg){
    if(strstr(filename, "payload"))
    {
        LOGD("[myMapFileAtAddr] file name contains 'payload': %s, prot: %d, flags: %d, fd: %d", filename, prot, flags, fd);
        prot |= PROT_WRITE;
    }
    void* res = oriMapFileAtAddr(expected_ptr, byte_count, prot, flags, fd, start, low_4gb, reuse, filename, error_msg);
    return res;
}

小結(jié)

網(wǎng)絡(luò)上很多關(guān)于抽取殼實(shí)現(xiàn)的教程都沒有提過mprotect的問題,默認(rèn)mprotect修改內(nèi)存是成功的,這可能是因?yàn)榇蠖鄶?shù)人都是通過模擬器進(jìn)行實(shí)驗(yàn)。然而,如果我們要做線上的加殼產(chǎn)品,面向生產(chǎn)環(huán)境進(jìn)行開發(fā)的話,mprotect調(diào)用失敗的問題大概率會(huì)遇到,希望本文能有所幫助。

參考:

[1].mprotect設(shè)計(jì)文檔:https[:][/][/]pax[.]grsecurity[.]net[/]docs[/]mprotect[.]txt

到此這篇關(guān)于Android加殼過程中mprotect調(diào)用失敗的原因及解決方案的文章就介紹到這了,更多相關(guān)Android加殼mprotect調(diào)用失敗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • android中多線程下載實(shí)例

    android中多線程下載實(shí)例

    本文以實(shí)例的方式為大家介紹下android中的多線程下載附源碼,感興趣的朋友可以學(xué)習(xí)下哈
    2013-06-06
  • Android調(diào)用高德地圖定位的方法

    Android調(diào)用高德地圖定位的方法

    本篇文章主要介紹了Android調(diào)用高德地圖定位的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • 利用Android實(shí)現(xiàn)比較炫酷的自定義View

    利用Android實(shí)現(xiàn)比較炫酷的自定義View

    自定義View、多線程、網(wǎng)絡(luò),被認(rèn)為是Android開發(fā)者必須牢固掌握的最基礎(chǔ)的三大基本功,這篇文章主要給大家介紹了關(guān)于如何利用Android實(shí)現(xiàn)比較炫酷的自定義View的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • Android中使用itemdecoration實(shí)現(xiàn)時(shí)間線效果

    Android中使用itemdecoration實(shí)現(xiàn)時(shí)間線效果

    這篇文章主要介紹了Android中使用itemdecoration實(shí)現(xiàn)時(shí)間線效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • ProxyWidget和Element更新的正確方式詳解

    ProxyWidget和Element更新的正確方式詳解

    這篇文章主要為大家介紹了ProxyWidget和Element更新的正確方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Android自定義橡皮擦效果

    Android自定義橡皮擦效果

    這篇文章主要為大家詳細(xì)介紹了Android自定義橡皮擦效果,橡皮擦擦圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Android TextView實(shí)現(xiàn)圖文混合編排的方法

    Android TextView實(shí)現(xiàn)圖文混合編排的方法

    這篇文章主要為大家詳細(xì)介紹了Android TextView實(shí)現(xiàn)圖文混合編排的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • Android 開發(fā)中fragment預(yù)加載問題

    Android 開發(fā)中fragment預(yù)加載問題

    這篇文章主要介紹了Android 開發(fā)中fragment預(yù)加載問題的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-01-01
  • 詳解Android 掃描條形碼(Zxing插件)

    詳解Android 掃描條形碼(Zxing插件)

    本篇文章主要對(duì)Android 掃描條形碼(Zxing插件)進(jìn)行實(shí)例解析,相信對(duì)大家的學(xué)習(xí)會(huì)有很好的幫助,需要的朋友一起來看下吧
    2016-12-12
  • Android中斷并重啟一個(gè)Thread線程的簡單方法

    Android中斷并重啟一個(gè)Thread線程的簡單方法

    下面小編就為大家?guī)硪黄狝ndroid中斷并重啟一個(gè)Thread線程的簡單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02

最新評(píng)論