日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

一种linux平台下算法库二进制文件加密方法探讨

發(fā)布時(shí)間:2024/3/26 linux 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一种linux平台下算法库二进制文件加密方法探讨 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

? ? ? ? 最近做項(xiàng)目遇到一個(gè)需求,需要把我們的圖像算法庫(kù)提供給客戶使用,為防止算法庫(kù)被對(duì)方濫用和逆向破解,需要對(duì)算法庫(kù)二進(jìn)制文件做加密處理以及加密狗綁定,同時(shí)防止庫(kù)文件被反調(diào)試跟蹤。算法庫(kù)加密可以借助開源軟件 openssl實(shí)現(xiàn),加密狗的使用也很簡(jiǎn)單,從加密狗官方渠道即可拿到arm平臺(tái)下的支持庫(kù)和簡(jiǎn)單的示例程序。接下來就是如何構(gòu)建對(duì)二進(jìn)制庫(kù)文件的加解密以及如何安全的映射至進(jìn)程地址空間中,一個(gè)最容易想到的思路是對(duì)算法庫(kù)文件套上一層外殼,由外殼程序完成算法庫(kù)的自動(dòng)解密以及完全脫離文件系統(tǒng)的進(jìn)程內(nèi)存映射,整個(gè)過程中加密后的算法由外殼進(jìn)程直接解密到內(nèi)存中,并由驅(qū)動(dòng)程序直接映射至調(diào)用進(jìn)程的地址空間中,完全不會(huì)暴露給文件系統(tǒng),配合一定的反調(diào)試技術(shù),使得用戶很難直接拿到算法庫(kù)的二進(jìn)制代碼,從而大大提高對(duì)算法庫(kù)文件的保護(hù),防止惡意破解。

1. 借助內(nèi)核編譯initramfs的方式將加密后的算法庫(kù)二進(jìn)制文件嵌入到外殼中

? ? ? ? 利用開源的openssl開發(fā)例程很容易實(shí)現(xiàn)對(duì)算法庫(kù)文件的加密和解密,接下來的問題是如何將加密后的算法庫(kù)文件套上外殼,多年的內(nèi)核編譯經(jīng)驗(yàn)讓我想到內(nèi)核的一些奇技淫巧,早些年簡(jiǎn)單研究過內(nèi)核使用cpio打包initramfs的一些原理,內(nèi)核也是將打包后的文件系統(tǒng)嵌套進(jìn)vmlinux中的,于是借鑒一下代碼,套殼的第一步就完成了,代碼如下:

.section .init.data,"a" .globl __idata_start __idata_start: .incbin "../deplib/encrypt.so" .globl __idata_end __idata_end:

? ? ? ? 代碼里的關(guān)鍵指令是incbin匯編命令,該命令可以用來包含可執(zhí)行文件及其他任意數(shù)據(jù),文件內(nèi)容將按字節(jié)逐一添加到當(dāng)前 ELF 節(jié)中,原樣包含不進(jìn)行任何匯編,同時(shí)代碼中定義必要的全局變量定位該段位置。這樣就可以將加密后的算法文件encrypt.so嵌入到外殼程序的ELF段中,并在外殼程序初始化時(shí)將其解密至外殼進(jìn)程的內(nèi)存中。

2. 常規(guī)的動(dòng)態(tài)庫(kù)載入方法和問題

? ? ? ? 解密后的算法庫(kù)文件位于內(nèi)存中,如何將其作為動(dòng)態(tài)庫(kù)文件導(dǎo)入到進(jìn)程地址空間中是接下來要處理的問題。常規(guī)的動(dòng)態(tài)庫(kù)加載方案主要有2種,第一種是最常用的方法,程序編譯時(shí)由gcc指定依賴的動(dòng)態(tài)庫(kù)信息,該信息會(huì)記錄到ELF文件中,并可由readelf -d獲取,應(yīng)用程序加載時(shí)ld.so會(huì)根據(jù)指定的路徑加載相應(yīng)的庫(kù)。第二種方法是利用libdl庫(kù)的dlopen/dlclose/dlsym等接口,在程序執(zhí)行過程中動(dòng)態(tài)載入庫(kù)文件并解析其內(nèi)部符號(hào)等。這2種方法有一個(gè)共同的問題是動(dòng)態(tài)庫(kù)的載入必須通過文件系統(tǒng)的方式導(dǎo)入進(jìn)來,而一旦將動(dòng)態(tài)庫(kù)暴露給文件系統(tǒng),技術(shù)人員就很容易通過文件系統(tǒng)拿到算法庫(kù)的二進(jìn)制文件進(jìn)行反編譯,且這2種方式都可以通過cat /proc/{pid}/maps直接定位到導(dǎo)入的動(dòng)態(tài)庫(kù)的具體位置,如下所示:

cat /proc/3639/maps 00400000-00402000 r-xp 00000000 b3:02 797096 /root/test/shtest 00411000-00412000 rw-p 00001000 b3:02 797096 /root/test/shtest 00412000-00433000 rw-p 00000000 00:00 0 [heap] ... 7f9b56d000-7f9b57c000 ---p 00017000 b3:02 393672 /lib/aarch64-linux-gnu/libpthread-2.23.so 7f9b57c000-7f9b57d000 r--p 00016000 b3:02 393672 /lib/aarch64-linux-gnu/libpthread-2.23.so 7f9b57d000-7f9b57e000 rw-p 00017000 b3:02 393672 /lib/aarch64-linux-gnu/libpthread-2.23.so

3. 間接借助文件系統(tǒng)的幫助將動(dòng)態(tài)庫(kù)導(dǎo)入進(jìn)程地址空間

? ? ? ? 如何不通過文件系統(tǒng)將動(dòng)態(tài)庫(kù)映射到進(jìn)程的地址空間就是接下來需要面對(duì)的問題,顯而易見的想法是硬杠glibc,直接改寫dlopen等接口的實(shí)現(xiàn),將其動(dòng)態(tài)庫(kù)的載入脫離文件系統(tǒng),后來發(fā)現(xiàn)自己迷失在glibc盤根錯(cuò)節(jié)的層層套用和復(fù)雜的符號(hào)解析引用中。之后還考慮借鑒內(nèi)核的vdso及uselib機(jī)制,但對(duì)ELF文件的符號(hào)解析并不是一件短時(shí)間內(nèi)容易做到并可以確保無誤的事情。最好的方法就是仍然沿用dlopen那一套機(jī)制,由glibc來處理動(dòng)態(tài)庫(kù)的符號(hào)解析和引用問題。躊躇之際突然想到驅(qū)動(dòng)程序的設(shè)備節(jié)點(diǎn)也是一種文件,也有自己的file_operations操作,也支持read/write/mmap等基本的文件操作接口,把它用作動(dòng)態(tài)庫(kù)的代理入口交由dlopen處理,由驅(qū)動(dòng)程序配合完成dlopen對(duì)動(dòng)態(tài)庫(kù)的所有操作,用這種斗轉(zhuǎn)星移的方法,將處于用戶態(tài)內(nèi)存中的算法庫(kù)映射到用戶進(jìn)程地址空間中,間接脫離文件系統(tǒng)的支持。

3.1 將算法庫(kù)二進(jìn)制文件導(dǎo)入到內(nèi)核空間

? ? ? ? 首先通過ioctl將用戶態(tài)下解密后的算法庫(kù)文件導(dǎo)入到驅(qū)動(dòng)程序申請(qǐng)的一段內(nèi)存空間中,如果內(nèi)存足夠,使用dma_alloc_coherent直接從CMA區(qū)域拿到一段連續(xù)內(nèi)存即可。更加普適的方法是使用alloc_page申請(qǐng)足夠的物理頁(yè)幀(成功概率高于連續(xù)內(nèi)存段),將用戶態(tài)算法庫(kù)數(shù)據(jù)分頁(yè)拷貝至內(nèi)核空間中,然后使用vmap機(jī)制將page頁(yè)面數(shù)組對(duì)應(yīng)的物理內(nèi)存映射到vmalloc地址空間中,核心代碼摘錄如下:

wxcoder_dev->pages = kmalloc(sizeof(struct page *) * npages, GFP_KERNEL);if (!wxcoder_dev->pages)goto oom;for (i = 0; i < npages; i++) {struct page *p;p = alloc_page(GFP_KERNEL);if (!p){goto oom;}wxcoder_dev->pages[i] = p;if (copy_from_user(page_address(p), (const void __user *)usr_data->algo_start+i*PAGE_SIZE, (usr_data->algo_len - i*PAGE_SIZE) > PAGE_SIZE ? PAGE_SIZE : (usr_data->algo_len - i*PAGE_SIZE))){debug("err copy %d pages, left: %d pages\n", i, npages - i);ret = -EFAULT;goto oom;}}wxcoder_dev->vbase = vmap(wxcoder_dev->pages, npages, 0, PAGE_KERNEL);if (!wxcoder_dev->vbase){ret = -EFAULT;goto oom;}

? ? ? ? 算法庫(kù)二進(jìn)制文件拷貝到內(nèi)核空間后,接下來驅(qū)動(dòng)程序需要支撐dlopen的實(shí)現(xiàn),首先需要了解dlopen的具體執(zhí)行過程,涉及到哪些系統(tǒng)調(diào)用,最簡(jiǎn)單的方法是使用strace調(diào)試工具追蹤dlopen的調(diào)用過程,其中dlopen使用RTLD_NOW參數(shù)調(diào)用,結(jié)果如下:

... openat(AT_FDCWD, "/dev/wxcoder", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\340\351\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFCHR|0600, st_rdev=makedev(10, 41), ...}) = 0 mmap(NULL, 515904, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7ca5e000 mprotect(0x7f7cac8000, 65536, PROT_NONE) = 0 mmap(0x7f7cad8000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6a000) = 0x7f7cad8000 mmap(0x7f7cadb000, 3904, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7cadb000 close(3) = 0 ...

? ? ? ? 可以看出dlopen先后調(diào)用了openat、read、mmap、mprotect和close等系統(tǒng)調(diào)用,看起來dlopen讀取了動(dòng)態(tài)庫(kù)的程序頭表等信息,分析其內(nèi)部段信息后將其映射到進(jìn)程地址空間中。顯而易見,我們只需在驅(qū)動(dòng)里實(shí)現(xiàn)read/mmap接口即可支撐dlopen的功能。read的實(shí)現(xiàn)比較簡(jiǎn)單,只要利用copy_to_user函數(shù)配合vmap返回的內(nèi)核虛擬地址即可將dlopen需要的數(shù)據(jù)返回給用戶態(tài)空間,要注意的是dlopen完成后需關(guān)閉驅(qū)動(dòng)的read和mmap通道,防止數(shù)據(jù)通過設(shè)備節(jié)點(diǎn)外泄給惡意用戶,另外還可以在外殼程序和驅(qū)動(dòng)程序間使用ioctl時(shí)加上簡(jiǎn)易的口令,以進(jìn)一步保護(hù)數(shù)據(jù)。

3.2 自定義mmap實(shí)現(xiàn)算法庫(kù)文件到進(jìn)程地址空間的映射

? ? ? ? mmap的實(shí)現(xiàn)有多種方式,如果先前用的是dma_alloc_coherent申請(qǐng)的一段連續(xù)物理內(nèi)存,mmap實(shí)現(xiàn)最簡(jiǎn)單,只要使用remap_pfn_range函數(shù)將相應(yīng)的物理頁(yè)幀映射到用戶進(jìn)程地址空間的vma段即可,該函數(shù)的底層實(shí)現(xiàn)即建立物理頁(yè)幀至用戶空間vma段的頁(yè)表實(shí)例,代碼如下:

static int wzalgo_mmap(struct file *filp, struct vm_area_struct *vma) {unsigned long size = PAGE_ALIGN(vma->vm_end - vma->vm_start);debug("wzalgo_mmap - start: 0x%lx, end: 0x%lx, size: %ld\n\tpgoff: %lu, flags: %lu\n", \vma->vm_start, vma->vm_end, size, vma->vm_pgoff, vma->vm_flags);if (false == readable || false == loadok){return -EINVAL;}...vma->vm_pgoff += (wxcoder_dev->rxdma_addr >> PAGE_SHIFT);return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot); }

? ? ? ? 如果先前采用的是alloc_page申請(qǐng)的一組物理不連續(xù)頁(yè)幀保存的算法庫(kù)二進(jìn)制文件,除了采用分頁(yè)單獨(dú)remap_pfn_range映射的方法,還可以借助缺頁(yè)中斷來實(shí)現(xiàn),代碼如下:

static vm_fault_t wzcoder_mapping_fault(struct vm_fault *vmf) {struct vm_area_struct *vma = vmf->vma;pgoff_t pgoff;struct page **pages;debug("wzcoder_mapping_fault vmf->pgoff: %lu\n", vmf->pgoff);pages = vma->vm_private_data;for (pgoff = vmf->pgoff; pgoff && *pages; ++pages)pgoff--;if (*pages) {struct page *page = *pages;get_page(page);vmf->page = page;return 0;}return VM_FAULT_SIGBUS; }static const struct vm_operations_struct wzcoder_mapping_vmops = {.close = wzcoder_mapping_close,.fault = wzcoder_mapping_fault, };static int wzalgo_mmap(struct file *filp, struct vm_area_struct *vma) {unsigned long size = PAGE_ALIGN(vma->vm_end - vma->vm_start);if (false == readable || false == loadok || !wxcoder_dev->vbase){return -EINVAL;}debug("wzalgo_mmap - start: 0x%lx, end: 0x%lx, size: %ld\n\tpgoff: %lu, flags: %lu\n", vma->vm_start, vma->vm_end, size, \vma->vm_pgoff, vma->vm_flags);vma->vm_ops = &wzcoder_mapping_vmops;vma->vm_private_data = (void *)wxcoder_dev->pages;return 0; }

? ? ? ? 在用戶態(tài)進(jìn)程mmap該段vma區(qū)域時(shí),注冊(cè)vm_operations_struct結(jié)構(gòu),缺頁(yè)中斷處理函數(shù)wzcoder_mapping_fault把vmf->pgoff對(duì)應(yīng)的物理頁(yè)幀返回給vmf->page,其中vmf->pgoff指示當(dāng)前頁(yè)在vma區(qū)域中邏輯頁(yè)的偏移量,由于該段vma的邏輯頁(yè)和實(shí)際的物理頁(yè)幀是一一對(duì)應(yīng)的,所以很容易找到對(duì)應(yīng)的page實(shí)例。

? ? ? ? 更深入一點(diǎn)的方法可以參考remap_pfn_range的底層實(shí)現(xiàn),自己建立所需的頁(yè)表結(jié)構(gòu),強(qiáng)化對(duì)內(nèi)核建立頁(yè)表過程的理解,代碼摘錄如下:

#define pte_alloc_wz(mm, pmd, address) \(unlikely(pmd_none(*(pmd))) && __pte_alloc_wz(mm, pmd, address))#define pte_alloc_map_lock_wz(mm, pmd, address, ptlp) \(pte_alloc_wz(mm, pmd, address) ? NULL : pte_offset_map_lock(mm, pmd, address, ptlp))int __pte_alloc_wz(struct mm_struct *mm, pmd_t *pmd, unsigned long address) {spinlock_t *ptl;pgtable_t new = pte_alloc_one(mm, address);if (!new)return -ENOMEM;smp_wmb(); /* Could be smp_wmb__xxx(before|after)_spin_lock */ptl = pmd_lock(mm, pmd);if (likely(pmd_none(*pmd))) { /* Has another populated it ? */mm_inc_nr_ptes(mm);pmd_populate(mm, pmd, new);new = NULL;}spin_unlock(ptl);if (new)pte_free(mm, new);return 0; }int __pmd_alloc_wz(struct mm_struct *mm, pud_t *pud, unsigned long address) {spinlock_t *ptl;pmd_t *new = pmd_alloc_one(mm, address);if (!new)return -ENOMEM;smp_wmb(); /* See comment in __pte_alloc */ptl = pud_lock(mm, pud); #ifndef __ARCH_HAS_4LEVEL_HACKif (!pud_present(*pud)) {mm_inc_nr_pmds(mm);pud_populate(mm, pud, new);} else /* Another has populated it */pmd_free(mm, new); #elseif (!pgd_present(*pud)) {mm_inc_nr_pmds(mm);pgd_populate(mm, pud, new);} else /* Another has populated it */pmd_free(mm, new); #endif /* __ARCH_HAS_4LEVEL_HACK */spin_unlock(ptl);return 0; }static inline pmd_t *pmd_alloc_wz(struct mm_struct *mm, pud_t *pud, unsigned long address) {return (unlikely(pud_none(*pud)) && __pmd_alloc_wz(mm, pud, address))? NULL: pmd_offset(pud, address); }static int remap_pte_range(struct mm_struct *mm, pmd_t *pmd,unsigned long addr, unsigned long end,unsigned long pfn, pgprot_t prot) {pte_t *pte;spinlock_t *ptl;int err = 0;pte = pte_alloc_map_lock_wz(mm, pmd, addr, &ptl);if (!pte)return -ENOMEM;arch_enter_lazy_mmu_mode();do {BUG_ON(!pte_none(*pte));if (!pfn_modify_allowed(pfn, prot)) {err = -EACCES;break;}set_pte_at(mm, addr, pte, pte_mkspecial(pfn_pte(pfn, prot)));pfn++;} while (pte++, addr += PAGE_SIZE, addr != end);arch_leave_lazy_mmu_mode();pte_unmap_unlock(pte - 1, ptl);return err; }static inline int remap_pmd_range(struct mm_struct *mm, pud_t *pud,unsigned long addr, unsigned long end,unsigned long pfn, pgprot_t prot) {pmd_t *pmd;unsigned long next;int err;pfn -= addr >> PAGE_SHIFT;pmd = pmd_alloc_wz(mm, pud, addr);if (!pmd)return -ENOMEM;VM_BUG_ON(pmd_trans_huge(*pmd));do {next = pmd_addr_end(addr, end);err = remap_pte_range(mm, pmd, addr, next,pfn + (addr >> PAGE_SHIFT), prot);if (err)return err;} while (pmd++, addr = next, addr != end);return 0; }static inline int remap_pud_range(struct mm_struct *mm, p4d_t *p4d,unsigned long addr, unsigned long end,unsigned long pfn, pgprot_t prot) {pud_t *pud;unsigned long next;int err;pfn -= addr >> PAGE_SHIFT;pud = pud_alloc(mm, p4d, addr);if (!pud)return -ENOMEM;do {next = pud_addr_end(addr, end);err = remap_pmd_range(mm, pud, addr, next,pfn + (addr >> PAGE_SHIFT), prot);if (err)return err;} while (pud++, addr = next, addr != end);return 0; }static inline int remap_p4d_range(struct mm_struct *mm, pgd_t *pgd,unsigned long addr, unsigned long end,unsigned long pfn, pgprot_t prot) {p4d_t *p4d;unsigned long next;int err;pfn -= addr >> PAGE_SHIFT;p4d = p4d_alloc(mm, pgd, addr);if (!p4d)return -ENOMEM;do {next = p4d_addr_end(addr, end);err = remap_pud_range(mm, p4d, addr, next,pfn + (addr >> PAGE_SHIFT), prot);if (err)return err;} while (p4d++, addr = next, addr != end);return 0; }int wx_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,unsigned long pgoff, unsigned long size, pgprot_t prot) {pgd_t *pgd;unsigned long end = addr + PAGE_ALIGN(size);struct mm_struct *mm = vma->vm_mm;int err, i = 0;if (is_cow_mapping(vma->vm_flags)) {if (addr != vma->vm_start || end != vma->vm_end){return -EINVAL;}vma->vm_pgoff = page_to_pfn(wxcoder_dev->pages[pgoff]);}vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;BUG_ON(addr >= end);pgd = pgd_offset(mm, addr);flush_cache_range(vma, addr, end);for (i = 0; i < (PAGE_ALIGN(size) >> PAGE_SHIFT); i++){err = remap_p4d_range(mm, pgd, addr, addr + PAGE_SIZE, page_to_pfn(wxcoder_dev->pages[i+pgoff]), prot);if (err){printk("remap_p4d_range: %d\n", err);break;}addr += PAGE_SIZE;}return err; }static int wzalgo_mmap(struct file *filp, struct vm_area_struct *vma) {unsigned long size = PAGE_ALIGN(vma->vm_end - vma->vm_start);if (false == readable || false == loadok){return -EINVAL;}debug("wzalgo_mmap - start: 0x%lx, end: 0x%lx, size: %ld\n\tpgoff: %lu, flags: %lu\n", vma->vm_start, vma->vm_end, size, \vma->vm_pgoff, vma->vm_flags);if (!wxcoder_dev->vbase){return -EINVAL;}vma->vm_ops = &wzcoder_mapping_vmops;vma->vm_private_data = (void *)wxcoder_dev->pages;return wx_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot); }

? ? ? ? 函數(shù)wx_remap_pfn_range完成進(jìn)程虛擬地址區(qū)域vma到物理頁(yè)幀間的映射,即一一建立頁(yè)表項(xiàng),pgd_offset根據(jù)傳入的addr拿到當(dāng)前進(jìn)程的全局頁(yè)目錄,考慮到我們需要映射的物理頁(yè)幀是非連續(xù)的,因此分頁(yè)調(diào)用remap_p4d_range建立第5級(jí)頁(yè)表,remap_p4d_range函數(shù)根據(jù)待映射的地址范圍分段調(diào)用remap_pud_range建立第四級(jí)頁(yè)表,依次下去直至set_pte_at為每個(gè)虛擬內(nèi)存頁(yè)建立頁(yè)表項(xiàng)。需要注意的是較新的linux內(nèi)核采用了5級(jí)頁(yè)表的模式,實(shí)際使用的頁(yè)表級(jí)數(shù)依賴于cpu平臺(tái)的定義,不同cpu平臺(tái)下各級(jí)頁(yè)表的頁(yè)表處理宏的實(shí)現(xiàn)是不一樣的,內(nèi)核使用這種方式將頁(yè)表的建立過程統(tǒng)一到5級(jí)模式之下。

4. 借助內(nèi)核ptrace機(jī)制設(shè)計(jì)初步的反調(diào)試手段

? ? ? ? 以上工作完成后,設(shè)計(jì)的內(nèi)核驅(qū)動(dòng)程序就足以支撐用戶態(tài)進(jìn)程使用dlopen/dlsym等libdl庫(kù)中的函數(shù)導(dǎo)入動(dòng)態(tài)庫(kù)并解析其符號(hào)等。加密后的算法庫(kù)二進(jìn)制文件嵌入到外殼程序中,并配合內(nèi)核驅(qū)動(dòng)程序完成其內(nèi)存映射的方法大大提高了對(duì)算法庫(kù)文件的保護(hù),為進(jìn)一步提高安全性,防止用戶使用strace等調(diào)試工具追蹤其執(zhí)行過程,我們可以借助內(nèi)核的ptrace機(jī)制建立初步的反調(diào)試技術(shù),ptrace是linux內(nèi)核支持的一種進(jìn)程調(diào)試手段,值得慶幸的是即使是常用的gdb的實(shí)現(xiàn)也完全依賴于ptrace機(jī)制,為此可以采用在檢測(cè)到用戶使用ptrace追蹤外殼進(jìn)程時(shí)返回錯(cuò)誤碼等反制手段。

? ? ? ? 檢測(cè)外殼程序是否被ptrace跟蹤調(diào)試有2種簡(jiǎn)易方法,一種是在驅(qū)動(dòng)程序中添加獲取當(dāng)前進(jìn)程task_struct->ptrace值的功能,當(dāng)用戶態(tài)進(jìn)程被采用PTRACE_ATTACH調(diào)試時(shí),內(nèi)核會(huì)修改該值為一個(gè)非0值,指示當(dāng)前進(jìn)程的調(diào)試狀態(tài),外殼程序可以據(jù)此判定自己是否被跟蹤調(diào)試。另一種方法可以在用戶態(tài)監(jiān)測(cè)/proc/self/status,檢查其TracerPid項(xiàng)是否非0,如果非0值則表示當(dāng)前進(jìn)程被監(jiān)控跟蹤了,示例如下:

lyfan@MV:/home/lyfan/shtest$ cat /proc/3629/status Name: shtest State: t (tracing stop) Tgid: 3629 Pid: 3629 PPid: 3627 TracerPid: 3627 ...

? ? ? ? TracerPid為3627,可以看到進(jìn)程3629被其父進(jìn)程3627跟蹤調(diào)試了。

5. 不足和待研究的地方

? ? ? ? 采用這種外殼加固的方法雖然可以將原SO文件完全抹去,但外殼程序在動(dòng)態(tài)加載算法庫(kù)文件后,仍然會(huì)將解密后的部分動(dòng)態(tài)庫(kù)內(nèi)容暴露到進(jìn)程地址空間,需要進(jìn)一步配合反dump技術(shù)的使用,加強(qiáng)外殼在內(nèi)存安全強(qiáng)度方面的不足,prctl(PR_SET_DUMPABLE, 0)可以關(guān)閉進(jìn)程的coredump功能,但仍需結(jié)合其他方面的內(nèi)存安全技術(shù)來提升外殼程序的防御能力。另外針對(duì)ELF段分別加解密也是一種思路,前提是需要深入了解ELF文件的詳細(xì)組織方式,及其內(nèi)部符號(hào)的解析方法等。

? ? ? ? 至此,對(duì)算法庫(kù)二進(jìn)制文件的加密和導(dǎo)入的研究算是全部完成了,文中討論的相關(guān)技術(shù)的示例工程已上傳到gitee上,考慮到安全性,示例工程中的具體實(shí)現(xiàn)以及使用的密鑰口令等均做了較大調(diào)整。測(cè)試采用的內(nèi)核版本是Linux 4.19.0,cpu是arm64平臺(tái),gcc版本8.2.0。

附上項(xiàng)目地址:https://gitee.com/liangyuf/linux_so_encrypt

總結(jié)

以上是生活随笔為你收集整理的一种linux平台下算法库二进制文件加密方法探讨的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。