<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=454&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo中文社区 / linux源码解读（十二）：系统调用(strace命令0和中断&字节跳动HIDS简要分析]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=454</link>
		<description><![CDATA[linux源码解读（十二）：系统调用(strace命令0和中断&字节跳动HIDS简要分析 最近发表的帖子。]]></description>
		<lastBuildDate>Sun, 09 Oct 2022 03:42:21 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[linux源码解读（十二）：系统调用(strace命令0和中断&字节跳动HIDS简要分析]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=461#p461</link>
			<description><![CDATA[<p>中断是整个计算机体系最核心的功能之一，关于中断硬件原理可以参考文章末尾的链接1（https://www.cnblogs.com/theseventhson/p/13068709.html），这里不再赘述；中断常见的种类如下：</p><p>&#160; &#160; 硬件中断：键盘、鼠标、网卡等输入<br />&#160; &#160; 软件中断：int 3、int 0xe(page fault)<br />&#160; &#160; 自定义中断<br />&#160; &#160; 信号中断(kill -signum)，比如kill -9 pid杀死进程<br />&#160; &#160; 系统异常和错误-&gt;利于排错</p><p>&#160; &#160;1、（1）本人刚开始学习的时候，看到很多资料把中断、异常、陷阱放在一起介绍，很容易混淆这3个概念，这里详细列举一下各个概念之间的关系如下：<br /><span class="postimg"><img src="https://img2020.cnblogs.com/blog/2052730/202201/2052730-20220104204055421-229957432.png" alt="FluxBB bbcode 测试" /></span></p><p> 异步中断：都是由硬件产生的，比如键盘、鼠标、网卡等输入，导致硬件中断的事件是不可预测的（cpu也不可能知道用户啥时候敲键盘、移动鼠标，也不可能提前知道网卡什么时候接收到数据）；<br /> 同步中断：都是可预见的指令流产生的</p><p>&#160; &#160; &#160; fault：最常见的是page fault缺页异常；异常产生后可以重新执行产生异常的指令；逆向时用这个特性可以通过更改目标内存属性产生的page fault异常达到定位关键代码的目的；<br />&#160; &#160; &#160; &#160; &#160; &#160;trap：<br />&#160; &#160; &#160; &#160; &#160; 软中断：最常见的就是int 3了，常用于调试器单步调试；trap产生后会执行下一条指令，所以调试器调试时插入int 3才能达到单步调试的目的；<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;系统调用：3环app要调用内核的api，比如读写文件、通过wifi收发数据、在屏幕打印日志等，需要进入0环执行；linux早期采用int 0x80做系统调用（早期的windows比如xp系统是通过int 0x2e进入内核的）；后来x86架构的cpu硬件支持syscall、sysenter这种专门的系统调用（也就是从3环进入0环）指令；由于syscall/sysenter这种cpu原生支持的系统调用指令没有特权级别检查的处理，也没有压栈的操作，所以执行速度比 INT n/IRET 快了不少；如下：时间快了接近1倍！<br /><span class="postimg"><img src="https://img2020.cnblogs.com/blog/2052730/202201/2052730-20220104211946424-389878041.png" alt="FluxBB bbcode 测试" /></span></p><p>&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;abort：比如除0</p><p>&#160; &#160;（2）上述所有的操作，统称为中断！再说直白一点，中断的本质就是被打断！举个例子：cpu正在执行A进程的代码，突然用户敲了一下键盘，或者移动了鼠标，这时候就要马上接受用户的输入，然后采取相应的措施处理用户的输入；</p><p>&#160; &#160; &#160; 接受用户输入的功能已经在硬件上实现了，接下来操作系统需要做的就是实现中断响应的方法了，俗称handler！<br />&#160; &#160; &#160; &#160; &#160; &#160;中断的种类有很多（linux有256种中断），这么多中断种类，为了方便管理，各自都是有自己的编号的！每个编号自然也会有对应的响应handler（官方叫做中断处理routine）；这么多的handler，执行的时候怎么才能快速找到了？<br />&#160; &#160; &#160; &#160; &#160; &#160;cpu硬件层面有个IDTR寄存器，存放了IDT表的基址；IDT表本质上就是中断号和中断处理handler的映射；cpu硬件层面会根据中断号找到中断处理handler的入口地址，然后跳转到handler执行代码；有些病毒木马会hook键盘输入的handler，借此记录用户输入的所有字符来盗取账号！<br />&#160; &#160; &#160; &#160; &#160; 早期windows系统下部分杀毒软件为了确保内核或进程安全，也会通过驱动的方式hook一些系统调用SSDT来确保能及时发现恶意程序是否在作恶，比如hook openprocess来监控有没有恶意程序打开自己的进程注入代码（tp保护也是这个原理）；</p><p>&#160; 2、操作系统关于中断的开发，最核心的部分就是填充IDT了，本质就是先写好不同中断号的handler，再把handler函数的入口地址填写到正确的IDT表项（当然格式要符合中断描述符的要求）！接下来看看linux 4.9版本是怎样一步一步填充IDT和使用中断的！&#160; &#160; &#160; </p><p>&#160; &#160;（1）填充IDT，也就是中断初始化，在arch\x86\kernel\traps.c种的trap_init函数中：</p><div class="codebox"><pre class="vscroll"><code>void __init trap_init(void)
{
    int i;

#ifdef CONFIG_EISA
    void __iomem *p = early_ioremap(0x0FFFD9, 4);

    if (readl(p) == &#039;E&#039; + (&#039;I&#039;&lt;&lt;8) + (&#039;S&#039;&lt;&lt;16) + (&#039;A&#039;&lt;&lt;24))
        EISA_bus = 1;
    early_iounmap(p, 4);
#endif

    set_intr_gate(X86_TRAP_DE, divide_error);
    set_intr_gate_ist(X86_TRAP_NMI, &amp;nmi, NMI_STACK);
    /* int4 can be called from all */
    set_system_intr_gate(X86_TRAP_OF, &amp;overflow);
    set_intr_gate(X86_TRAP_BR, bounds);
    set_intr_gate(X86_TRAP_UD, invalid_op);
    set_intr_gate(X86_TRAP_NM, device_not_available);
#ifdef CONFIG_X86_32
    set_task_gate(X86_TRAP_DF, GDT_ENTRY_DOUBLEFAULT_TSS);
#else
    set_intr_gate_ist(X86_TRAP_DF, &amp;double_fault, DOUBLEFAULT_STACK);
#endif
    set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun);
    set_intr_gate(X86_TRAP_TS, invalid_TSS);
    set_intr_gate(X86_TRAP_NP, segment_not_present);
    set_intr_gate(X86_TRAP_SS, stack_segment);
    set_intr_gate(X86_TRAP_GP, general_protection);
    set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug);
    set_intr_gate(X86_TRAP_MF, coprocessor_error);
    set_intr_gate(X86_TRAP_AC, alignment_check);
#ifdef CONFIG_X86_MCE
    set_intr_gate_ist(X86_TRAP_MC, &amp;machine_check, MCE_STACK);
#endif
    set_intr_gate(X86_TRAP_XF, simd_coprocessor_error);

    /* Reserve all the builtin and the syscall vector:
    将前32个中断号都设置为已使用状态
     */
    for (i = 0; i &lt; FIRST_EXTERNAL_VECTOR; i++)
        set_bit(i, used_vectors);
    //设置0x80系统调用的系统中断门
#ifdef CONFIG_IA32_EMULATION
    set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_compat);
    set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif

#ifdef CONFIG_X86_32
    set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_32);
    set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif

    /*
     * Set the IDT descriptor to a fixed read-only location, so that the
     * &quot;sidt&quot; instruction will not leak the location of the kernel, and
     * to defend the IDT against arbitrary memory write vulnerabilities.
     * It will be reloaded in cpu_init() */
    __set_fixmap(FIX_RO_IDT, __pa_symbol(idt_table), PAGE_KERNEL_RO);
    idt_descr.address = fix_to_virt(FIX_RO_IDT);

    /*
     * Should be a barrier for any external CPU state:
     执行CPU的初始化，对于中断而言，在 cpu_init() 中主要是将 idt_descr 放入idtr寄存器中
     */
    cpu_init();

    /*
     * X86_TRAP_DB and X86_TRAP_BP have been set
     * in early_trap_init(). However, ITS works only after
     * cpu_init() loads TSS. See comments in early_trap_init().
     */
    set_intr_gate_ist(X86_TRAP_DB, &amp;debug, DEBUG_STACK);
    /* int3 can be called from all */
    set_system_intr_gate_ist(X86_TRAP_BP, &amp;int3, DEBUG_STACK);

    x86_init.irqs.trap_init();

#ifdef CONFIG_X86_64
    memcpy(&amp;debug_idt_table, &amp;idt_table, IDT_ENTRIES * 16);
    set_nmi_gate(X86_TRAP_DB, &amp;debug);
    set_nmi_gate(X86_TRAP_BP, &amp;int3);
#endif
}</code></pre></div><p>&#160; used_vectors变量是一个bitmap，它用于记录中断向量表中哪些中断已经被系统注册和使用，哪些未被注册使用；</p><p>&#160; （2）trap_init()已经完成了异常和陷阱的初始化。对于linux而言，中断号0~19是专门用于陷阱和故障使用的，20~31一般是intel用于保留的；而外部IRQ线使用的中断为32~255(代码中32号中断被用作汇编指令异常中断)。所以，在trap_init()代码中，专门对0~19号中断的门描述符进行了初始化，最后将新的中断向量表起始地址放入idtr寄存器中；相应的handler定义和实现在arch\x86\kernel\traps.c中，举个大家都熟悉的int 3为例，实现如下：</p><div class="codebox"><pre class="vscroll"><code>/* May run on IST stack. */
dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
{
#ifdef CONFIG_DYNAMIC_FTRACE
    /*
     * ftrace must be first, everything else may cause a recursive crash.
     * See note by declaration of modifying_ftrace_code in ftrace.c
     */
    if (unlikely(atomic_read(&amp;modifying_ftrace_code)) &amp;&amp;
        ftrace_int3_handler(regs))
        return;
#endif
    if (poke_int3_handler(regs))
        return;

    ist_enter(regs);
    RCU_LOCKDEP_WARN(!rcu_is_watching(), &quot;entry code didn&#039;t wake RCU&quot;);
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
    if (kgdb_ll_trap(DIE_INT3, &quot;int3&quot;, regs, error_code, X86_TRAP_BP,
                SIGTRAP) == NOTIFY_STOP)
        goto exit;
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */

#ifdef CONFIG_KPROBES
    if (kprobe_int3_handler(regs))
        goto exit;
#endif

    if (notify_die(DIE_INT3, &quot;int3&quot;, regs, error_code, X86_TRAP_BP,
            SIGTRAP) == NOTIFY_STOP)
        goto exit;

    /*
     * Let others (NMI) know that the debug stack is in use
     * as we may switch to the interrupt stack.
     */
    debug_stack_usage_inc();
    preempt_disable();
    cond_local_irq_enable(regs);
    do_trap(X86_TRAP_BP, SIGTRAP, &quot;int3&quot;, regs, error_code, NULL);//核心代码
    cond_local_irq_disable(regs);
    preempt_enable_no_resched();
    debug_stack_usage_dec();
exit:
    ist_exit(regs);
}</code></pre></div><p>&#160; （3）部分中断比如网卡接受到数据后，通过中断通知cpu来读取；如果数据量很大，cpu读取和处理数据的时候一直关闭中断，可能导致其他中断被延迟甚至忽略（大家肯定都遇到过电脑“卡死”的情况：敲击键盘、移动鼠标都没反应，很有可能是cpu还在处理旧中断，来不及响应新的中断）；为了在处理上一个中断的同时避免耽误下一个中断，linux把中断分成了上中断和下中断两部分（类似windows的DPC机制）。上部分代码优先级高，但是代码量较少，耗时不多；下半段执行优先级低但是耗时的代码；上半段执行时依然关闭中断，下半段就可以开中断了；此过程称之为softtirq，图示如下：&#160; <br /><span class="postimg"><img src="https://img2020.cnblogs.com/blog/2052730/202201/2052730-20220105173306025-1879624010.png" alt="FluxBB bbcode 测试" /></span></p><p>&#160; &#160;arch\x86\entry\entry_64.S中的调用代码：</p><div class="codebox"><pre><code>/* Call softirq on interrupt stack. Interrupts are off. */
ENTRY(do_softirq_own_stack)
    pushq    %rbp
    mov    %rsp, %rbp
    incl    PER_CPU_VAR(irq_count)
    cmove    PER_CPU_VAR(irq_stack_ptr), %rsp
    push    %rbp                /* frame pointer backlink */
    call    __do_softirq
    leaveq
    decl    PER_CPU_VAR(irq_count)
    ret
END(do_softirq_own_stack)</code></pre></div><p>&#160; &#160; &#160;实际使用时，linux提供了workqueue的机制和接口（create、destroy、insert等）供用户调用；</p><p>&#160; （4）站在上层3环业务应用的角度，各行业不同的业务都有自己的需求，可能需要自定义大量的中断处理程序，每个处理程序对应不同的中断号用于区分。但是管理中断的硬件设备的引脚是有限的；加上软件中断号也不超过256个中断，万一业务需要的中断号超过256个后该怎么办了？ 这个个hashtable实现的原理一摸一样了，以java的hashtable为了：用户刚开始创建hashtable时，会分配一个固定大小的数组作为索引，查询数组上元素的时间复杂度是O(1)；如果有多个key的hash结果都一样了，就用链表来存放(key,value)，由此解决hash冲突；其实在中断号的管理和hashtable完全一样，一旦中断号重复，同样可以通过链表的方式记录不同中断号、业务所对应的handler，图示如下：<br /><span class="postimg"><img src="https://img2020.cnblogs.com/blog/2052730/202201/2052730-20220108181331903-937138069.png" alt="FluxBB bbcode 测试" /></span></p><p>&#160; &#160; &#160;由此依赖，理论上用户使用的中断号数量就没有任何限制了（只要内存容量支持），所以也能自由地让用户自己注册中断服务了（让用户直接操作IDT也危险），妈妈再也不用担心中断号不够用?！调用的接口如上图所示：request_irq()，如下：</p><br /><p>int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)</p><br /><p>&#160; &#160; unsigned int&#160; irq:为要注册中断服务函数的中断号，比如外部中断0就是16,定义在mach/irqs.h<br />&#160; &#160; irq_handler_t&#160; handler:为要注册的中断服务函数,就是(irq_desc+ irq )-&gt;action-&gt;handler<br />&#160; &#160; unsigned long&#160; irqflags: 触发中断的参数,比如边沿触发, 定义在linux/interrupt.h。&#160; &#160; &#160; &#160; &#160;<br />&#160; &#160; const char&#160; *devname:中断程序的名字,使用cat /proc/interrupt 可以查看中断程序名字<br />&#160; &#160; void&#160; *dev_id:传入中断处理程序的参数,注册共享中断时不能为NULL,因为卸载时需要这个做参数,避免卸载其它中断服务函数</p><p>&#160; &#160; &#160;（5）有些时候异步中断产生的速度远超cpu处理中断的速度，导致中断无法被及时响应，这时外部的中断就只能被忽略不理会了？万一重要的中断被漏掉了怎么办了？还是举用户键盘输入的例子：当电脑变得卡顿的时候，只要没死机，用户还是可以在键盘输入的，只不过暂时在频幕上看不到输入；过一段时间后用户的输入就会出现在频幕上了，这是怎么做到的了？</p><p>&#160; 做服务器后台上层应用开发的时候，同样会遇到生产者和消费者速度不匹配的时候，最常见的解决办法就是加消息队列来缓冲了，目前市面上最流行的消息缓冲队列非kafka莫属了；底层中断场景遇到这种问题同样可以用类似的思路解决：加个消息队列缓冲，队列的名字就叫kfifo（猜测全称是kernel first in first out）！为了循环利用和节约空间，kfifo采用了环形队列！实际使用时，linux也提供了kfifo_alloc、kfifo_free、kfifo_in、kfifo_out等接口供用户直接调用！</p><p>&#160; 总结对比：为了不漏掉异步中断，linux在两方面做了改进（本质上是使用了两个队列记录信息）：</p><p>&#160; &#160; &#160; 执行中断handler时分成了上、下；两部分；上半部代码很少，可以关闭中断，一般执行很紧急的业务；下半部代码集较多、处理时间较长，可以开中断；用户实际使用时，可以调用work_queue相关接口把下半部任务加入队列<br />&#160; &#160; &#160; &#160; &#160; &#160;响应异步中断时，为了不漏掉中断请求，也可以增加kfifo队列！</p><p>&#160; 3、系统调用的核心意义：</p><p>&#160; &#160; &#160; 为什么要用系统调用了？<br />&#160; &#160; &#160; &#160; &#160; 每个3环app都需要底层的硬件交互，最常见的诸如在屏幕输出字符、读写磁盘的文件、通过网卡收发数据等；和硬件交互，肯定要调用硬件自身的驱动，但是硬件的种类非常多，如果每个app都单独调用硬件的驱动，会导致app开发的成本高昂！此时linux VFS的作用就凸显了: VFS统一对接种类繁多的硬件驱动，起到了类似各种“中台”的作用；上层app仅需调用linux提供的api，底层不同硬件的驱动由linux操作系统去适配（这里就是VFS啦），app不需要自己挨个调用每个硬件的驱动了，极大降低的开发的难度和成本！这里再发一次之前VFS的图示：</p><p><span class="postimg"><img src="https://img2020.cnblogs.com/blog/2052730/202201/2052730-20220105102207108-483037829.png" alt="FluxBB bbcode 测试" /></span></p><br /><p>&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160;同样的硬件只有1个，多个进程或线程都要使用硬件，比如多个进程/线程都要读写磁盘、都要从网卡收发数据，肯定有个先后顺序，这时也需要操作系统来协调； <br />&#160; &#160; &#160; &#160;linux提供的VFS解决了app适配不同硬件的老问题，但是新问题也来了：为了保护VFS和硬件的驱动不被app恶意篡改，VFS和驱动都在0环内核；但是app在3环啊，EIP直接从3环去取0环的指令是会出错的（如果cpu硬件层面不报错，内核代码就毫无安全性可言了，早期的DOS操作系统就是这样的，很容易被ap篡改代码或数据搞崩！），所以cpu硬件层面诞生了软中断：通过int+中断号的形式让EIP顺利进入内核执行代码；而且3环的app只需要执行“int+中断号”即可，完全看不见具体的VFS或驱动代码是怎么写的，极大的简化了app调用api的方法，也保护了VFS或驱动的代码安全(EIP从3环进入0环后，只能按照硬件厂家事先写好的驱动代码执行，没法干其他任何事情了)，一箭双雕！<br />&#160; &#160; &#160; &#160; &#160; 因为int要检查特权级别，还要出栈入栈保存上下文，比较耗时，cpu在硬件层面诞生了专门的系统调用指令：syscall/sysenter，但是核心功能和int是一样的！</p><p>&#160; 4、 系统调用在逆向/安全防护的应用：以字节跳动HIDS为例</p><p>&#160; 早期在windows下无论是杀毒软件，还是逆向破解的程序，都喜欢hook SSDT来监控3环的app在干啥，比如hook openProcess就知道3环有没有app在调试自己；在linux平台上原理类似，也可以通过hook 系统调用来做防护，拿字节跳动的HIDS举例（末尾参考5、6两个链接）：</p><p>&#160; （1）mprotect 函数挂钩：函数本是用来设置物理内存页的rwx属性的，利用这个功能可以用来调试和反调试</p><p>&#160; &#160; &#160; 调试: 先把关键的物理页设置为不可写，一旦有代码试图写该页就会产生page fault异常，由此可以定位关键的代码，这就是传说中的（硬件）内存读写断点，一般用来定来定位关键的加密字段生成代码，也可以用来注入自己的恶意代码；<br />&#160; &#160; &#160; &#160; &#160; &#160;反调试：把物理内存页面设置为不可写，调试的时候由于需要插入int 3，遇到这种不可写的内存是会报错；大家用ida调试时经常遇到各种signal弹窗告警有一部分就是内存属性不可写导致的！</p><p>&#160; &#160; （2）open函数挂钩：函数本来是用来打开文件、获取文件句柄的，利用这个可以用来：</p><p>&#160; &#160; &#160; 检测自己的so是否被第三方调用：loadlibrary函数底层最终会调用linux系统提供的open函数打开so，然后才能加载so到内存执行代码</p><p>&#160; &#160; （3）prctl函数挂钩：函数原本是用来设置进程属性的，利用这个可以用来：</p><p>&#160; &#160; &#160; 逆向调试&#160; <br />&#160; &#160; &#160; &#160; &#160;设置PR_SET_PTRACER属性用来把代码注入到目标进程，frida底层貌似用的就是ptrace注入代码；<br />&#160; &#160; &#160; &#160; &#160; &#160;改进程/线程的名字躲避安全防护的检测<br />&#160; &#160; &#160; &#160; &#160; &#160;安全防护<br />&#160; &#160; &#160; &#160; &#160; &#160;检测自己的进程/线程名字是否被更改；<br />&#160; &#160; &#160; &#160; &#160; &#160;检测自己的进程/线程是否被设置PR_SET_PTRACER或PR_SET_MM属性</p><p>&#160; &#160; &#160; （4）ptrace函数挂钩：这可能是逆向最有用的系统调用了，frida底层貌似就用了这个函数；HIDS hook这个函数记录了关键信息有：</p><p>&#160; &#160; &#160;POKETEXT/POKEDATA<br />&#160; &#160; &#160; &#160;进程ID<br />&#160; &#160; &#160; &#160;内存地址<br />&#160; &#160; &#160; &#160;拷贝的数据<br />&#160; &#160; &#160; &#160;执行程序<br />&#160; &#160; &#160; &#160;进程树</p><p>&#160; &#160; &#160; &#160; &#160;这样就很容易检测自己的进程是不是正在被调试了！&#160; &#160;</p><p>&#160; &#160; &#160; &#160; 还有很多重要的系统调用如execve、init_module等都被hook了，这里不再赘述！</p><p>&#160; 5、其实linux系统有现成的strace工具可以查看进程都触发了哪些系统调用，以打开文件为例，命令很简单：cat&#160; 1.txt；为了看清楚cat命令做了哪些系统调用，可以用strace来查看，完整的命令是：“strace cat 1.txt”，结果如下：</p><div class="codebox"><pre class="vscroll"><code>└─# strace cat 1.txt
execve(&quot;/usr/bin/cat&quot;, [&quot;cat&quot;, &quot;1.txt&quot;], 0x7ffd36672918 /* 51 vars */) = 0
brk(NULL)                               = 0x55cbf772d000
access(&quot;/etc/ld.so.preload&quot;, R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, &quot;/etc/ld.so.cache&quot;, O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=94692, ...}) = 0
mmap(NULL, 94692, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc9c4d5e000
close(3)                                = 0
openat(AT_FDCWD, &quot;/lib/x86_64-linux-gnu/libc.so.6&quot;, O_RDONLY|O_CLOEXEC) = 3
read(3, &quot;\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&gt;\0\1\0\0\0@n\2\0\0\0\0\0&quot;..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1839792, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc9c4d5c000
mmap(NULL, 1852680, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fc9c4b97000
mprotect(0x7fc9c4bbc000, 1662976, PROT_NONE) = 0
mmap(0x7fc9c4bbc000, 1355776, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7fc9c4bbc000
mmap(0x7fc9c4d07000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x170000) = 0x7fc9c4d07000
mmap(0x7fc9c4d52000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7fc9c4d52000
mmap(0x7fc9c4d58000, 13576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fc9c4d58000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fc9c4d5d580) = 0
mprotect(0x7fc9c4d52000, 12288, PROT_READ) = 0
mprotect(0x55cbf6e3e000, 4096, PROT_READ) = 0
mprotect(0x7fc9c4da0000, 4096, PROT_READ) = 0
munmap(0x7fc9c4d5e000, 94692)           = 0
brk(NULL)                               = 0x55cbf772d000
brk(0x55cbf774e000)                     = 0x55cbf774e000
openat(AT_FDCWD, &quot;/usr/lib/locale/locale-archive&quot;, O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=3041456, ...}) = 0
mmap(NULL, 3041456, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fc9c48b0000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(0x88, 0), ...}) = 0
openat(AT_FDCWD, &quot;1.txt&quot;, O_RDONLY)     = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=5, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc9c488e000
read(3, &quot;11111&quot;, 131072)                = 5
write(1, &quot;11111&quot;, 511111)                    = 5
read(3, &quot;&quot;, 131072)                     = 0                                                                        
munmap(0x7fc9c488e000, 139264)          = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++</code></pre></div><p>&#160; 从上面的系统调用来看，一个简单的打开文件，尽然使用了这么多的系统调用，常见的execve、mmap、open、read、write、cloes等都用上了！每次系统调用从3环进出0环时都要保存上下文，效率很低，建议少用系统调用！</p><p> </p><p> </p><p> </p><p>参考：</p><p>&#160; 1、https://www.cnblogs.com/theseventhson/p/13068709.html&#160; 实模式中断原理</p><p>&#160; 2、https://www.cnblogs.com/jiading/p/12606978.html&#160; linux中断和系统调用解析</p><p>&#160; &#160; &#160; &#160;3、https://www.cnblogs.com/LittleHann/p/4111692.html?utm_source=tuicool&amp;utm_medium=referral&#160; &#160; &#160;Linux Systemcall Int0x80方式、Sysenter/Sysexit Difference Comparation</p><p>&#160; &#160; &#160; &#160;4、https://bbs.pediy.com/thread-226254.htm&#160; &#160;syscall/sysenter具体过程</p><p>&#160; &#160; &#160; &#160;5、https://mp.weixin.qq.com/s/rm_hXHb_YBWQqmifgAqfaw&#160; &#160; 最后防线：字节跳动HIDS分析</p><p>&#160; &#160; &#160; &#160;6、https://github.com/EBWi11/AgentSmith-HIDS&#160; &#160; <a href="https://github.com/bytedance/Elkeid/blob/main/README-zh_CN.md" rel="nofollow">https://github.com/bytedance/Elkeid/blo … E-zh_CN.md</a>&#160; </p><p>&#160; &#160; &#160; &#160;7、https://blog.csdn.net/hunter___/article/details/83063131&#160; prctl()函数详解</p><p>&#160; &#160; &#160; &#160;8、https://www.jianshu.com/p/b1f9d6911c90&#160; ptrace使用介绍</p><p>&#160; &#160; &#160; &#160;9、https://www.cnblogs.com/tolimit/p/4415348.html&#160; linux中断源码分析-初始化</p><p>&#160; &#160; &#160; 10、https://www.cnblogs.com/vedic/p/11069249.html&#160; linux workqueue讲解</p><p>&#160; &#160; &#160; &#160;11、https://blog.csdn.net/MyArrow/article/details/8090504&#160; workqueue接口函数</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Sun, 09 Oct 2022 03:42:21 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=461#p461</guid>
		</item>
	</channel>
</rss>
