福州公司建设网站,魔都网站建设,新网网站模板,怎样做医院网站目录 一 构建分析环境
二 栈的位置
三 栈开头8字节
四 寄存器环境
五 R4和R5
六 如何确定系统调用的具体函数 一 构建分析环境
为了分析方便#xff0c;做了如下测试环境#xff1a; 内核实现一个简单的创建字符设备的驱动 应用层实现一个c程序#xff0c;操作为打开内…目录 一 构建分析环境
二 栈的位置
三 栈开头8字节
四 寄存器环境
五 R4和R5
六 如何确定系统调用的具体函数 一 构建分析环境
为了分析方便做了如下测试环境 内核实现一个简单的创建字符设备的驱动 应用层实现一个c程序操作为打开内核创建的字符设备文件 内核在处理open设备文件的接口中将指针设置为空并在该空指针上赋值。这样就触发内核的空指针异常输出oops及相关堆栈。
为了说明方便将内核的栈信息单独提取出来
0dee0: beceeccc c00cd8f4 00000041 c004897c cf050790 cf5ab990 ea7ef579 00000006
0df00: cd826015 c0718100 00000000 cf401c38 ce2c3bc8 00000101 00000004 0000003e
0df20: 00000000 00000000 00000000 ffffff9c cd826000 00000ff0 c0776c30 000105f0
0df40: 00000000 00000000 ffffff9c cd826000 00000005 00000003 ffffff9c cd826000-1 -2 -3 -4 -5 -6
0df60: 00000005 c00bec3c c0714080 c071f780 cd0e2480 00000000 c0000000 00000024-7 -8 -9 r4 r5 r6 r7 r8
0df80: 00000100 00000001 cd3cc000 0001056c 00000000 00010354 00000005 c000e6a8r9 lr r4 r5 r0 0 r1 1 -18 2 3
0dfa0: cd3cc000 c000e4e0 0001056c 00000000 000105f0 00000000 00000001 000000004 5 6 7 8 9 10 11
0dfc0: 0001056c 00000000 00010354 00000005 00000000 00000000 b6f8b000 beceeccc12 13 sp 14 lr 15 pc 16 cpsr 17-old r0 rev rev
0dfe0: 00000000 beceecb4 000104fc b6df902c 60080010 000105f0 00000000 00000000
4[bf8f5074] (second_open [debug_for_syscall_statck]) from [c00c35fc] (chrdev_open0xd0/0x190)
4[c00c35fc] (chrdev_open) from [c00bd790] (do_dentry_open0x1d8/0x2f0)
4[c00bd790] (do_dentry_open) from [c00cbe44] (do_last0x6b0/0xc5c)
4[c00cbe44] (do_last) from [c00cc4a8] (path_openat0xb8/0x640)
4[c00cc4a8] (path_openat) from [c00cd8f4] (do_filp_open0x2c/0x88)
4[c00cd8f4] (do_filp_open) from [c00bec3c] (do_sys_open0x104/0x1c8)
4[c00bec3c] (do_sys_open) from [c000e4e0] (ret_fast_syscall0x0/0x38)
上面已对栈的信息做了标注。下面看这些标注如何得来。
二 栈的位置
参考之前对oops异常的分析。主要是内核栈占用两个页面共8KB一头上threadinfo一头是内核栈栈底。栈向下增长从高地址到低地址。
三 栈开头8字节
内核栈预留了8个字节 这是内核设计保留的具体原因参考内核的修改记录
https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id415395e19fd197ce4f248902dba54f4065af547c Always leave 8 bytes free at the top of the kernel stack. Thisprevents the stack becoming completely empty when do_exit() iscalled from an exiting nfsd() thread, and causing the wrongpointer to be returned from current_thread_info() 代码中也是如此定义栈开始位置的。
./arch/arm/include/asm/thread_info.h:#define THREAD_START_SP (THREAD_SIZE - 8)
上面标记为rev的两个位置
四 寄存器环境
接下来18个位置为寄存器环境保存用。占用大小根据pg_regs定义来72字节 具体入栈操作在entry-common.S中 .align 5ENTRY(vector_swi)#ifdef CONFIG_CPU_V7Mv7m_exception_entry#elsesub sp, sp, #S_FRAME_SIZEstmia sp, {r0 - r12} Calling r0 - r12ARM( add r8, sp, #S_PC )ARM( stmdb r8, {sp, lr}^ ) Calling sp, lr。。。。。。
上述文件在kernel目录的如下位置
./arch/arm/kernel/entry-common.S:ENTRY(vector_swi)
vector_swi定义了系统调用异常的入口。也就是上层c代码进入c库使用swi指令触发系统调用时会触发异常在异常向量表中执行上述汇编代码 在上述汇编代码中保存了栈的18个位置
其中的7保存了系统调用号
五 R4和R5
在调用系统调用接口之前保存了两个寄存器
local_restart:ldr r10, [tsk, #TI_FLAGS] check for syscall tracingstmdb sp!, {r4, r5} push fifth and sixth argstst r10, #_TIF_SYSCALL_WORK are we tracing syscalls?bne __sys_tracecmp scno, #NR_syscalls check upper syscall limitadr lr, BSYM(ret_fast_syscall) return addressldrcc pc, [tbl, scno, lsl #2] call sys_* routine
r4 和 r5之后使用r8保存系统调用表使用7中的中断号右移两位每个调用占用4字节修改pc寄存器直接跳转到系统调用中
跳转之前将返回地址写入lr寄存器中
六 如何确定系统调用的具体函数
sys_call_table确定开始位置
call.S确定具体函数名
/* 0 */ CALL(sys_restart_syscall)CALL(sys_exit)CALL(sys_fork)CALL(sys_read)CALL(sys_write)/* 5 */ CALL(sys_open)./arch/arm/include/asm/unistd.h:#define __NR_syscalls (388)
共有388个项目所以sys_call_table开始位置保留 388×4大小的空间 这些空间在vmlinux.o目标中是填充的零。此文件反汇编后虚拟地址的开始位置为0 需要查看vmlinux的反汇编。这个反汇编中虚拟地址开始位置调整为c0000000了且上述表的内容也有具体内容了。 c000e6a8 sys_call_table:c000e6a8: c002e0d8 ldrdgt lr, [r2], -r8c000e6ac: c0024c64 andgt r4, r2, r4, ror #24c000e6b0: c0021940 andgt r1, r2, r0, asr #18c000e6b4: c00cf1b8 ; UNDEFINED instruction: 0xc00cf1b8c000e6b8: c00cf254 andgt pc, ip, r4, asr r2 ; UNPREDICTABLEc000e6bc: c00cdf74 andgt sp, ip, r4, ror pcc000e6c0: c00cccd4 ldrdgt ip, [ip], -r4c000e6c4: c003aa08 andgt sl, r3, r8, lsl #20c000e6c8: c00cdf90 mulgt ip, r0, pc ; UNPREDICTABLE
这里5号调用第六个位置地址为c00cdf74 该地址的汇编代码为 c00cdf74 SyS_open:c00cdf74: e6ff3072 uxth r3, r2c00cdf78: e1a02001 mov r2, r1c00cdf7c: e1a01000 mov r1, r0c00cdf80: e3e00063 mvn r0, #99 ; 0x63c00cdf84: eaffff88 b c00cddac do_sys_open
所以系统调用最开始调用SyS_open接着调用do_sys_open 这就跟上述栈的回溯对应上了 c00cddac do_sys_open:c00cddac: e3a0c040 mov ip, #64 ; 0x40c00cddb0: e7dfc81c bfi ip, ip, #16, #16c00cddb4: e012c00c ands ip, r2, ipc00cddb8: 17eb3053 ubfxne r3, r3, #0, #12c00cddbc: e92d43f0 push {r4, r5, r6, r7, r8, r9, lr}c00cddc0: e24dd024 sub sp, sp, #36 ; 0x24
这个函数里入栈7个位置并预留9个位置这也跟上述栈标记及栈回溯对应上了 关于系统调用时如何通过代码里的宏定义等映射到SyS_open的后续再看。 基于此就可以进行栈分析了。 七 其他 关于汇编里的伪代码 标记 头文件引入 指令集选择 新旧ABI兼容处理 等等就不记录了可以结合最终汇编代码确定一些条件编译的情况