童梦(Kevin Luo) 下午11:10
指针可以想象成一根连接线
童梦(Kevin Luo) 下午11:44
加锁和释放锁是执行流的约定行为规范 如果不遵守 有可能出问题
————— 2020-7-9 —————
童梦(Kevin Luo) 上午12:42
调试原理是调试器和被调试进程建立了联系后 因为被调试进程被标记了 内核就会对他特别处理 当调试事件发生时 让被调试进程停下 通知调试器处理 Windows 与Linux 都是这么干的 int3断下后 可以再TF标志位设置去单步
TCP协议可提供面向连接的可靠的字节流传输服务,是根据发出去的数据包 然后收到ACK包 再根据ACK包的内容 去指导下一个发送行为 发太快不好 发太慢也不好 总有一个刚刚好的值 这里面有连接状态管理 流量控制 拥塞控制 但是有队头阻塞的问题 HTTP3+QUIC+UDP要革掉他的命
缺页异常是cpu的行为 pte的p位为0或权限位只读但却要写都会引发异常 发生之后会根据地址找到vma来决定下一步怎么做
进程是不能直接操作设备的 内核可以 但是内核只能完成基本动作 进程才知道具体要做的事 这样进程必须借助内核去操作 文件系统就是进程与内核的中间人 内核让进程将诉求告诉文件系统 文件系统传达给内核 这样沟通链就有了 因为文件系统是进程与内核都可以访问的 因此一切皆文件就这么来的 内核想办法把很多事情都挂在文件系统上 这样太方便进程与内核通信了 所以一堆文件系统都出来了 procfs sysfs debugfs 连cgroupfs也来了
buddy子系统是Linux 内核管理物理内存分配的系统 slab是在buddy子系统上建立的小块内存和对象池的分配子系统。比较形象的比喻是buddy子系统是土豪的拥有一个城中村的房子 Slab系统是二房东 包下一幢一幢房子 隔成很多大大小小的户型出租
内存按node zone migrate_type maxorderblock 划分然后 用free_eara和free_list 来管理 分配时 有现成的直接分配 没有就把大的拆小再分 归还时能合成大的就合成大的
关中断关抢占是针对某一cpu的 主要是用在中断上下文或临界区 目的只是一个 尽快执行完 对中断而言 任务是紧迫的 当然要快 对临界区而言 进去是持有锁的 也当然要快
Slab通用内存分配可以按2的n次方字节去分 kmem_cache_node相当于仓库 kmem_cache_cpu相当于营业厅
Linux 信号机制可以认为是进程的一种中断机制 是异步的 不确定的 有signal mask 和signaction 去维护 signal mask是per thread的 signaction 则是属于进程的 所有线程共享 与Windows下apc机制相似 执行过程都是返回用户空间前改eip寄存器去用户空间执行 之后再进内核 恢复以前的eip去回到进程的代码
变量本质是存储空间 函数的本质是一段顺序排放的二进制指令硬编码 这一切都是编译器链接器帮我们做的 我们是否察觉到 他就在那里 不懂汇编语言的确无法成为c语言的高手
中断总的流程是收到中断信号 就查idt表 找到入口 执行 返回 中断信号从哪来 这很关键 一种是cpu在进程中执行了某些int x指令 这是故意的 如系统调用 一种是执行指令过程中产生异常 如除零缺页等 这些都是来自cpu内部 还有一种来自外部的中断 是可以抢占前者 也可以被设置屏蔽 为减小复杂度 外部硬件中断 在处理中断上半部时都关中断关抢占 这样可以避免中断嵌套
ELF文件本身运行时能够用的是数据和代码 但是要和其他代码和数据一起使用 需要提供信息给链接器 因此 每个elf都包含描述自身信息的各种表
内存与硬盘能保存信息 然后描述自身也是需要信息的 可以保存在自身之上 描述信息是有格式 存储的对象也是有描述信息与对象本书身信息的 如文件存在硬盘上 格式是一种约定 理解起来很绕
所谓驱动程序就是把进程访问设备的路打通 两步即可 首先创建设备文件 进程是可以访问文件系统的 然后是实现该设备文件fop函数 函数里可以访问硬件 这样路就通了
在内存中的信息就是一堆0和1 如果没有元数据 我们不可能明白里面数据的意义 所以元数据就像是地图像模板 让我们可以解析数据 元数据可以是结构体 可以是格式规范 如elf文件 我们可以定义结构体 去操作数据 编译器会帮助我们去做很多工作 让我们面向对象 而不是面向数据
汇编指令操作寄存器与内存 所以内存地址是核心 编写汇编程序时 地址是可以通过宏和标号来给出 通常定义数据时顺带给一标号 标号代表地址 可以做加减运算去定位更多地址 编译链接后 这些信息都用不着了 只生成赤裸裸的指令流与数据段空间
运行时环境很重要 所以系统内核程序和用户空间程序是不一样的 各有各的依赖环境 裸机程序也是不一样 所以不同的操作系统 就算相同的硬件平台 同样的代码 不一定都能运行 代码注入 两个问题要解决 一是能发起一个执行流去执行代码 一个是找到进程空间现成的代码去调用 使其所注入的代码站在巨人的肩膀上 这样可以做很多事 不然只能通过二进制接口去调用系统调用 做什么都难度大了 注入代码也不能定义全局变量或字符串常量 只能在栈和堆定义保存数据
Linux 调试原理是ptrace的调用 通过int3异常处理 被调试进程收到信号 在返回用户态前 信号处理中将自己挂起 唤醒调试进程
中断 软中断 异常 信号处理这些代码在什么时候执行 这些对理解操作系统有非常重要的作用 其中软中断与信号都是软件模拟的 非常相似
驱动最终要访问硬件去实现open read write函数功能 如果是真实物理硬件 那就没问题 但是如果是虚拟化的硬件 那就要捕捉陷入来模拟了 但是如果驱动预先知道设备是虚拟的 采用一种请求的方式向vmm要数据 那就是半虚拟化技术了 如virtio 效率高多了
程序是数据加指令 都存在内存里 指令是对数据的加工 寄存器是指令执行的核心场地 相当于 内存放着原材料 原材经过生产线加工后再放回内存 寄存器是加工现场
能操作内存写入数据和代码 能起一条线程去执行或者hook现有线程上的代码去执行 代码如果有依赖现有代码 那就要注入后重定位 一般用dll 注入 如果没任何依赖的shellcode 那放在哪里都可运行 但是要实现功能要自己去找函数地址 fs是指向当前线程teb 延着这线索可以找到getprocaddress函数 然后可以得到一切
想让自己的代码得到执行,首先要有代码,然后要有执行流 可以hook,可以覆盖返回地址等方法
所有的代码是为数据服务的