Code Monkey home page Code Monkey logo

wsxk.github.io's Introduction

Hi there 👋

I'm wsxk,a student in HUST.

I am a company's slaves

I am studying binary security and IOT security... as well as something interesting. Welcome to discuss these contents with me

email: [email protected]

wsxk's GitHub stats

wsxk.github.io's People

Contributors

wsxk avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

wsxk.github.io's Issues

linux内核基础 三 slub分配器详解

https://wsxk.github.io/linux_kernel_basic_three/

更新于2022-10-18
PS:请在学习完 linux内核基础一以及Linux内核基础二和相应的练习后进行三的学习

buddy system
slub分配器工作原理

  kmem_cache
  kmem_cache_cpu
  kmem_cache_node
  slub API
  slub各个数据结构间的关系
  slub内存分配原理/释放原理

references

buddy system
作为linux的最底层的内存管理器,详细请移步linux内核基础一,不过多赘述
slub分配器工作原理
buddy system + slub 构成了linux内核的内存分配系统,关于slub的叙述在linux内核基础一不是很详细,在重新学习了一下slub的原理后进行更详细的描述
其中要注意的一点是 slab、slob、slub 都是slab的一种(slab是最初设计的,机制复杂,效率较低;slob是针对嵌入式在slab的基础上编写的内存分配器,比较轻量;slub是在slab上简化代码逻辑,提高效率和安全性,已经成为现行linux内核的主流分配器)
slab会诞生的原因也很简单:buddy system按页提供内存,如果要申请15字节的内存,你一次给一个page过于浪费了,因此slab实现用于提供更细粒度的内存规划
因为先行主流是slub,所以直接学习slub,首先需要大家了解的是kmem_cache、kmem_cache_cpu、kmem_cache_node这三个重要结构体
kmem_cache
slub在向buddy system申请来若干页内存后,(该内存由slub管理器自由划分,被称为一个slab),将内存划分为若干个object,为此我们需要知道该object的大小啊,剩余数量啊,等等信息,kmem_cache就是用于记录这些信息的管理结构体。
其定义如下:
struct kmem_cache{
struct kmem_cache_cpu __percpu * cpu_slab;//percpu变量表示该变量始终位于cpu本地内存缓冲池中(分配内存时优先分配在cou内存缓冲池中以保证命中率)
slab_flags_t flags;//分配掩码,例如经常使用的SLAB_HWCACHE_ALIGN标志
unsigned long min_partial;//限制 kmem_cache_node中的partial链表的 slab最大数量
int size;//分配的object大小,用户填写的
int object_size;//实际的object大小,size+对齐+各种元数据(用于管理object)
int offset;//下一个object相对于当前object的偏移
#ifdef CONFIG_SLUB_CPU_PARTIAL
int cpu_partial;//限制 kmem_cache_cpu中的partial链表的所有slab 的free object的最大值
#endif
struct kmem_cache_order_objects oo;//低16位表示一个slab中的所有object数量,高16位表示page数量
struct kmem_cache_order_ojbects max;//等于oo
struct kmem_cache_order_ojbects min;//按照oo分配内存时出现内存不足后使用该定义分配
gfp_t allocflags;//从buddy system 分配内存的掩码
int refcount;
void (*ctor) (void *);
int inuse;//object size对齐后的大小
int align;//字节对齐大小
int reserved;
const char * name;//sysfs文件系统显示时使用
struct list_head list;//系统有一个slab_caches链表,所有的slab都会挂入此链表。
struct kmem_cache_node * node[MAX_NUMNODES];//slab节点 NUMA系统中每个内存控制器都有一个节点
};

kmem_cache_cpu
kmem_cache_cpu是对于cpu本地内存缓冲池的描述,每个cpu对应一个结构体
其定义如下:
struct kmem_cache_cpu{
void ** freelist;//空闲object的链表
unsigned long tid;//神奇的标志,用于cpu的同步
struct page *page;//当前cpu使用的slab
#ifdef CONFIG_SLUB_CPU_PARTIAL
struct page *partial;//本地 slab partial链表,部分使用object的slab组成
#endif
};

kmem_cache_node
kmem_cache_node用以描述一个 slab节点,由同一内存控制器下的所有cpu共享
其定于如下:
struct kmem_struct_node{
spinlock_t list_lock;//锁
unsigned long nr_partial;//当前 node中的partial链表中的slab数量
struct list_head partial; //slab节点的 slab partial链表
};

slub API
我们需要用到的API就4个,非常少,顾名思义也能看出怎么用。
struct kmem_cache *kmem_cache_create(const char *name,// name
size_t size,//object的size
size_t align,//对齐要求
unsigned long flags,//分配内存掩码
void (*ctor)(void *));//分配对象的构造回调函数
void kmem_cache_destroy(struct kmem_cache *);
void *kmem_cache_alloc(struct kmem_cache *cachep, int flags);
void kmem_cache_free(struct kmem_cache *cachep, void *objp);

slub各个数据结构间的关系
借图,侵删

slub内存分配原理/释放原理
http://www.wowotech.net/memory_management/426.html写的真的很好,还配有图示实例,非常有助于理解
强推!

references
per_cpu变量
slub详解
知乎

gdb 常用命令

https://wsxk.github.io/gdb%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/?query=gdb

这篇博客的主要目的是记录一下gdb调试的常用命令

gdb的可用命令实在是太多了,有很多重要的command,但是你不用就会忘记

所以需要记录一下(之后我好翻哈哈哈)

gdb多线程调试命令

  命令
  作用




  info threads
  查看线程ID


  thread ID(1,2…)
  根据info threads提供的ID号来切换线程


  thread apply ID1 ID2 command
  可以让相应线程执行相同的命令


  thread apply command
  让所以线程执行相同命令


  set scheduler-locking command
  设置调试线程时其他线程的状态(on是只有被调试线程会运行,off是所有线程都执行

pwndbg rebase功能
具体用法如下:
b *$rebase(offset)

非常方便!!在你运行开启了pie和aslr的程序时,不需要你自己计算偏移下断点。

在pwntools下可以这么用
gdb.attach(io,

pwn中常见leak方式和攻击手段

https://wsxk.github.io/pwn_%E5%B8%B8%E7%94%A8leak%E6%96%B9%E6%B3%95/

最近发现,做pwn题的时候,有时侯不是不理解思路,而是有些操作没学会,导致你在思考的时候没有思考全面,容易进入卡死的状态。(你所学到的限制了你的想象力)所以在这里记录一些常见的小tip,方便你泄露一些地址

leak方法

  libc中泄露stack
  mmap申请的chunk泄露libc
  stripped的libc中找main_arena位置

攻击思路

  heap攻击
  获得栈地址进而ROP
  获得shell后尝试拿到flag
    
      1.python命令import flag
      2. cpp flag -o > /dev/null
      3. ccl flag -o > /dev/null
      4. as
      5. ld

leak方法

libc中泄露stack

libc中有一个叫做 _environ 的全局变量,里面存放着当前进程的环境变量。

从这个图中我们可以看出, _environ存在在libc中,而其中所指向的值,是stack中的。

在IDA分析libc.so中,export符号里会有一个叫environ的符号,可用看它得到相对于libc的偏移

mmap申请的chunk泄露libc

mmap的chunk地址其实也在libc的那个段中,如果我们可用mmap一个chunk并泄露其堆地址,其实相当于泄露了libc地址。

stripped的libc中找main_arena位置

在ida拖入libc后,搜索函数malloc_trim

这个高亮即为main_arena的地址

其中main_arena有几个偏移比较重要,需要记住

在main_arena+112处,是bins数组的起始位置。因为一开始bins里什么都没有,所以指向main_arena+112(空bins都是这个,所以有很多),如果能泄露这个位置的值,就可以泄露libc

在mian_arena+96处,存放的是top_chunk的地址,据此可以推出heap的地址。

攻击思路
heap攻击
首先可以好好看看how2heap里的利用手法。

获得栈地址进而ROP

这个在最新的堆里面是很常见的(2.34)

获得shell后尝试拿到flag
一般情况下cat命令无法使用,重点在于利用其他命令执行sys_open来读取flag
1.python命令import flag
2. cpp flag -o > /dev/null
3. ccl flag -o > /dev/null
4. as
5. ld

搭建属于自己的blog

https://wsxk.github.io/build_my_blog/

好久以前我就有搭建博客记录自己所学的想法了。今天终于得偿所愿。
第一遍博客的内容当然是‘搭建自己的博客’啦。

1.找到模板
我找到的模板如下:
模板

十分感谢这位师傅的模板!!

fork一个分支到自己的github

2.修改模板
fork一个分支后,里面有很多东西都是别人的,你需要修改文件内容。

一、_config.yml
直接搜关键词lemon,找到的可以根据注释进行修改。

二、about.md
这个是原作者的推广内容,也是需要该的(它会出现在你的博客里)

三、_posts目录(注意目录本身不能删除,所有博客的文档原件必须放到里面)
里面有一篇原作者的博客,需要删除

四、googledb06e025310e016e.html
没什么作用可以直接删

五、README.md
里面的内容是关于原作者的需要删除。

修改完毕后,直接网址上输入
username.github.io
能访问到里的网页,里面什么都没有

3.gitalk
gitalk是评论区功能。开启步骤如下:
首先单击自己头像,点击settings

然后单击Developer settings

选择OAuth Apps,创建新的OAuth APP

创建完成后有Client ID
Client secrets 需要自己创建

然后复制两个到分支_config.yml中(搜索gitalk即可找到)修改对应信息即可。

linux 内核基础 八 modprobe提权

https://wsxk.github.io/linux_base_eight/

什么是modprobe
modprobe和 insmod/rmmod的区别
如何利用modprobe进行内核提权
modprobe提权的利用条件
references

什么是modprobe
modprobe是一个内置的linux系统命令,用于安装/卸载 LKM(loadable kernel module),它是一个内核全局变量,可以通过以下的命令进行查看:

modprobe和 insmod/rmmod的区别
众所周知,linux中还内置了另一组用于安装/卸载 LKM的命令 insmod/rmmod,modprobe和这组命令相比起来是有区别的。

modprobe可以解决 加载模块 的依赖关系,比如加载module a前必须加载module b,modprobe可以通过 /lib/modules/uname -r/modules.dep 查找依赖关系
modprobe 需要在/lib/modules/uname -r/ 下查找module,而insmod可以在当前目录下直接装载

如何利用modprobe进行内核提权
modprobe之所以可以进行内核提权,一个很重要的原因是, modprobe的路径(默认情况下是/sbin/modprobe)存储在内核本身的modprobe_path符号中,并且该页面是可写的
另一个原因是
在我们执行(exec)一个未知类型的文件时(magic number,魔数)(文件抬头没有被识别),系统会调用,会产生以下调用,最终调用到modprobe
entry_SYSCALL_64()
sys_execve()
do_execve()
do_execveat_common()
bprm_execve()
exec_binprm()
search_binary_handler()
__request_module() // wrapped as request_module
call_modprobe()

其中 call_modprobe的源码如下(5.14)kernel/kmod.c
static int call_modprobe(char *module_name, int wait)
{
//...

argv[0] = modprobe_path;
argv[1] = 

kernel pwn module

https://wsxk.github.io/kernel_pwn_module/

以目前浅薄的kernel pwn经验,总结了一套kernel pwn时会用到的基本操作,不定时更新~
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

size_t commit_creds=0;
size_t prepare_kernel_cred =0;

size_t user_cs;
size_t user_ss;
size_t user_sp;
size_t user_rflags;
void save_status(void){
asm(

python z3模块

https://wsxk.github.io/python_z3/?query=z3

z3介绍

Z3是由Microsoft Research开发的高性能定理证明器。(可以帮我们快速解方程)。Z3 在工业应用中实际上常见于软件验证、程序分析等。

由于Z3功能实在强大,也被用于很多其他领域:软件/硬件验证和测试,约束解决,混合系统分析,安全性,生物学(计算机模拟分析)和几何问题。

CTF 领域来说,能够用约束求解器搞定的问题常见于密码题、二进制逆向、符号执行、Fuzzing 模糊测试等。此外,著名的二进制分析框架 angr 也内置了一个修改版的 Z3。(当然了,我们关注的是就是它的自动解方程功能啦)

z3 安装

pip install z3

z3一般流程

from z3 import * # 1.导入库

enc = [BitVec(

canary 详解

https://wsxk.github.io/canary%E8%AF%A6%E8%A7%A3/

概述
canary是一种很流行的栈保护机制。

函数在开始时往往会在栈底插入一个值,在函数结束时,取出这个值和原值进行比对 判断栈溢出是否发生了

canary值通常在linux下取 fs:[0x28] 处的值

在发生错误时,程序会跳转到 __stack_chk_fail 函数处

canary的最低字节总是’\x00’,目的是为了截断字符串

虽然canary是非常简单高效的防护手段,仍然有许多手段可以绕过

这篇文章会写下我自己遇到的canary的绕过方法

  1. brute force

如果我们能直接覆盖到canary所在的位置,我们可以一次覆盖一个字节,byte to byte 得把canary爆破出来

这样爆破的次数能大大减少,爆破canary的可行性大大提高

例题可以看

网址

InCTF 2021 kqueue 复现(heap overflow)

https://wsxk.github.io/Inctf_2021_kqueue/

题目分析
原件可以在https://github.com/teambi0s/InCTFi/tree/master/2021/Pwn/Kqueue里下载。
这道题是给了源码的,可以通过查看源码来观察该ko文件到底做了些什么。
从源文件中可以看到,kqueue这道题目只提供了一个ioctl接口,根据其中输入的cmd,以及传入的request来创造queue
这道题目的每个函数看起来check很多,其实——都没有什么卵用,原因可以看代码:
static long err(char* msg){
printk(KERN_ALERT

IOT fuzz 面向漏洞检测的物联网设备辅助异常与崩溃捕获工具的研究

https://wsxk.github.io/IOT_FUZZ/

IOT fuzz 现状
解决方案

  1. Static Instrumentation
  2. Binary Rewriting
  3.Physical Re-Hosting
  4.Full Emulation
  5.Partial Emulation
  6.Hardware-Supported Instrumentation

题外话

IOT fuzz 现状
根据先前的 iot安全背景知识,我们总结过,嵌入式设备大概分为I型,II型,III三型,其中I型和II型比较接近现有的传统PC计算机,起码是有分为内核态和用户态的,换句话说,I型和II型嵌入式固件多少是有检测机制的(segment fault ,etc),这些设备其实可以用传统的fuzz技术来进行测试。
然而,对于III型嵌入式固件(monolithic firmware)而言,它是没有分成内核和用户态的,所有的程序都集成到了一个程序当中。这类程序通常没有相应的检测机制(节省空间),安装这种固件的嵌入式设备,通常资源都是有限的,对于fuzz来说,无疑是晴天霹雳。

资源有限导致fuzz效率很差
没有检测机制导致fuzz无法捕捉到相应的错误
  
    这是因为 fuzz依靠给出的crash来判断input可以触发漏洞,然而,III型firmware通常没有检测机制,其实触发了,重启/运行出错也不会立即产生效果。

针对上面提到的两个主要毛病,有许多有代价的解决方案。

解决方案

  1. Static Instrumentation
    prerequisites:源码和编译工具链
    在有源码和编译工具链的情况下,在源码里插入检测机制,重新编译.<br
    个人感觉这种办法没啥用,因为嵌入式设备厂商一般不开放源代码,如果是内部人士,或许可行

  2. Binary Rewriting
    prerequisites: 固件和设备
    在获取固件的情况下,对firmware的二进制文件插入相应的检测技术,其实难度还挺大的(😀。

3.Physical Re-Hosting
prerequisites: 源码, 编译链, 不同设备
将源代码重新编译到其他设备上,其他设备可能拥有更高算力。

4.Full Emulation
全模拟,把固件的所有行为都模拟一遍。
实际上模拟固件要花费的经力还挺大的。特别是外设模拟

5.Partial Emulation
退而求其次,只模拟固件,不模拟外设

6.Hardware-Supported Instrumentation
利用硬件支持的调试接口(例如,UART),可以利用高级接口获取更多的运行时信息,辅助fuzz发现crash。

题外话
在我读的What You Corrupt Is Not What You Crash: Challenges in Fuzzing Embedded Devices论文中,论文作者提出了6种启发式方法,其实对于这些个启发式方法的实现,这些个方法,是基于模拟器修改的,在模拟器的基础上增加了一个额外的监控器,包括Segment Tracking,Format Specifier Tracking,Heap Object Tracking,Call Stack Tracking,Call Frame Tracking,Stack Object Tracking
其实感觉吧,也算是Emulation的延申吧。

另外,在读Discovery and Identification of Memory Corruption Vulnerabilities on Bare-metal Embedded Devices时,作者通过binary rewriting方法实现了个插入检测机制,还算比较有趣。

linux内核基础 四 控制寄存器

https://wsxk.github.io/linux_kernel_basic_four/

更新于2022-10-19
PS:请学习完前三章后观看本章

cr0
cr1
cr2
cr3

  KPTI(Kernel Page Table Isolation)

cr4

  SMAP/SMEP

references

linux内核定义的控制寄存器(control register)共有5个,分别为cr0 cr1
cr2 cr3 cr4,这5个控制寄存器在内核中扮演了非常重要的角色,需要牢记

cr0

cr0寄存器中的 PE (page enable) 表示模式,1表示保护模式,0表示实模式。
cr0寄存器中的 PG 表示是否开启分页,PG=1 PE=1才开启分页模式,其他情况都不能开启

cr1
cr1寄存器被保留了,暂时没有作用

cr2
cr2寄存器用于存储触发缺页异常的线性地址(虚拟地址)

cr3
cr3寄存器用于保存页全局目录表PGD(page global directory)的地址
linux系统采用四级页表PGD->PUD->PMD->PTE
KPTI(Kernel Page Table Isolation)
KPTI是内核用于隔离kernel和user的PGD的机制。
显然,开启了该保护是由于修改了cr3寄存器的值来更换 不同的页表
但是为了提高切换页表的速度,kernel页表和user页表是很接近的
user_pgd_addr = kernel_pgd_addr + 0x1000
反应到cr3寄存器上即翻转cr3[12]

这里需要知道的是 kernel页表保存了用户态和内核态空间的完整映射。
而 用户态页表只保留了用户态空间的完整映射和部分内核态空间的映射(系统调用入口点、中断处理等等)
对于绕过kpti的保护,我们只需要执行内核中的swapgs_restore_regs_and_return_to_usermode函数来返回用户态即可(不使用swapgs、iretq)
这个函数的功能相当于
mov rdi, cr3
or rdi, 0x1000
mov cr3, rdi
pop rax
pop rdi
swapgs
iretq

在rop绕过时需要执行如下操作
↓ wapgs_restore_regs_and_return_to_usermode
0 // padding
0 // padding
user_shell_addr
user_cs
user_rflags
user_sp
user_ss

感谢arttnba3 blog
详细描述了kpti及其绕过手法

cr4
cr4寄存器结构如下

SMAP/SMEP
其中需要我们关注的是cr4[20]和cr4[21],标志位为1时表示开启SMAP/SMEP,标志位为0时表示关闭。
SMAP(Supervisor Mode Access Prevention)/SMEP(Supervisor Mode Execution Prevention) SMAP开启时,kernel无法访问user地址的内存,SMEP开启时,kernel无法执行user地址的代码。
但是显然,如果内核代码存在栈溢出问题可以让我们使用rop,或许我们可以利用rop修改cr4寄存器的 SMAP/SMEP标志位来使得kernel可以访问并执行 user的代码。

references
https://www.cnblogs.com/HcyRcer/p/16559321.html
arttnba3 blog
详细描述了kpti及其绕过手法

linux内核基础

https://wsxk.github.io/linux_kernel_basic/

什么是内核
内核架构

  宏内核(Monolithic Kernel)
  微内核(Micro Kernel)

linux系统启动过程

  1.加载BIOS(Basic I/O System)
  2.读取MBR(Master Boot Record,主引导记录)
  3.(GNU GRUB)引导
  4.加载kernel
  5.启动守护进程init

kernel部分原理

  1.分级保护域(hierarchical protection domains,简称rings)
    
      用户态和内核态
      用户空间和内核空间
      用户态->内核态
      内核态->用户态
    
  
  2.进程管理
    
      进程描述符(process descriptor)
      提权
    
  
  3.I/O
    
      进程文件系统(process file system,procfs)
      文件描述符(file descriptor,fd)
    
  
  4.Loadable Kernel Modules(LKMs)
  5.内核内存管理
    
      buddy system
        
          page
          pageblock
          page order
          迁移类型
          buddy system分配过程
        
      
      slab alloctor
        
          slab allocator的版本
          slub数据结构
          slub分配过程
          slub释放过程
        
      
      mmap、brk怎么申请内存
    
  
  6.kernel保护机制
    
      1.KASLR(kernel address space layout randomize,内核地址空间随机化)
      2.FGKASLR
      3.STACK PROTECTOR
      4.SMAP/SMEP(Supervisor Memory Access/Execution Prevention)
      5.KPTI(kernel page-table isolation,内核页表隔离)
      6.heap保护
        
          ①Hardened Usercopy
          ②Hardened freelist
          ③Random freelist

reference

什么是内核
操作系统(Operation System) 本质上也是一种软件,可以看作是普通应用程式与硬件之间的一层中间层,其主要作用便是调度系统资源、控制IO设备、操作网络与文件系统等,并为上层应用提供便捷、抽象的应用接口
而 内核(kernel) 是操作系统最重要的一部分。

通常来说,操作系统= 内核+文件系统(file system)

文件系统简单理解就是用来管理存放在硬盘上的文件和目录的。

kernel提供了除文件系统以外的其他功能:
1.控制并与硬件进行交互
2.提供应用程式运行环境
3.调度系统资源

  I/O,权限控制,系统调用,进程管理,内存管理等多项功能都可以归结到以上三点中

有一张图可以直观地看出内核的在计算机体系结构中的位置

内核架构
传统的内核架构有两种: 宏内核和微内核
宏内核(Monolithic Kernel)
宏内核顾名思义,它其实是一个完整的可执行的二进制程序,在内核态以监管者模式(Supervisor Mode)来运行。
宏内核将所有提供的功能都打包了起来,并向上层程序提供API接口。

微内核(Micro Kernel)
微内核,大部分的系统服务(如文件管理等)都被剥离于内核之外,内核仅仅提供最为基本的一些功能:底层的寻址空间管理、线程管理、进程间通信等。

虽然有宏内核和微内核各有优势,宏内核因为所有功能集中在一起,效率比较高(但是维护困难,不易扩展),微内核因为模块化,所以维护方便,设计难度大大降低(但是因为不同模块的通信问题导致效率降低)。当然了历史告诉我们,出现了两个极端的设计理念后,最理想的理念往往是取长补短,于是出现了混合内核(hybrid kernel)。

举个例子,linux的内核虽然整体架构是宏内核,但是为了提高可扩展性和可维护性,也允许用户自己编写模块并在内核中部署(可装载内核模块(Loadable Kernel Modules,简称LKMs)

微软虽然宣称自己是微内核,但是它里面也有一些重要的功能必须集成到内核上。

linux系统启动过程
根据我自己学习内核基础知识的过程,我觉得大家可能会对linux系统的引导过程产生异或,因此在真正讲述基础知识前,我会先解释一下linux的启动过程。

1.加载BIOS(Basic I/O System)
当你开启计算机电源,首先加载的是基本输入输出系统(Basic Input Output System ),就是BIOS

bios程序一般放在主板ROM(Read Only Memory)中,关机或掉电也不会消失。

BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机就知道应该去读取哪个硬件设备了

2.读取MBR(Master Boot Record,主引导记录)
读取硬盘上磁道的第一个扇区(MBR),它的大小是512字节.
前446字节存放的就是grub程序的一部分、后64字节是硬盘分区表,里面存放了预启动信息、分区表信息。
系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0×7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了

3.(GNU GRUB)引导
GRUB是boot loader的一种
GRUB是多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统

4.加载kernel
根据grub指定的内核映像的路径,来加载内核映像
系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。

5.启动守护进程init
init(sbin/init)是linux的第一个进程,这个进程读取相应的配置文件并启动一系列进程,这个进程的PID为1,所有的进程都由它衍生,都是它的子进程.
这个进程会读取/etc/inittab文件,/etc/inittab文件的作用是设定Linux的运行等级以及执行项(比如下文出现的rc.sysinit和rc*.d,tty)来启动对应的程序

运行等级如下。
•0:关机模式
•1:单用户模式
•2:无网络支持的多用户模式
•3:字符界面多用户模式
•4:保留,未使用模式
•5:图像界面多用户模式
•6:重新引导系统,重启模式

随后,init进程会fork出子进程执行/etc/rc.d/rc.sysinit(inittab文件告诉init要执行它)

这个脚本对系统进行一系列的初始化,包括时钟、键盘、磁盘、文件系统等初始化

然后init执行启动层级对应脚本(rc*.d),在/etc/inittab配置如下

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

rc执行完毕之后,系统环境已经设置完成,各种服务进程也已经启动。init开始启动终端程序。inittab文件中执行项通常如下

1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

kernel部分原理
1.分级保护域(hierarchical protection domains,简称rings)
分级保护域是一种将计算机不同的资源划分成不同权限的模型。
在一些硬件或者微代码级别上提供不同特权态模式的 CPU 架构上,保护环通常都是硬件强制的。Rings是从最高特权级(通常被叫作0级)到最低特权级(通常对应最大的数字)排列的
内层ring可以任意调用外层ring的资源,内层ring到外层ring可调用资源依次递减。
通常情况下,内核运行在r0级,用户程序运行在r3级(其实中间2级基本上没什么人用)
r3级其实没什么用,基本上啥都不能干(没权限),但是我们的用户程序也需要进行 访问文件,连接网络,与其他进程通信的操作,这时候应该怎么办?
聪明的内核设计者们提出了一个聪明的办法:当用户程序(r3)需要访问系统资源时,会向操作系统发送请求,操作系统(r0)会帮你完成你的请求,然后返回用户进程(r3)。这里涉及了r3-r0-r3的切换,接下来会具体讲解这个

用户态和内核态
用户态其实相当于r3,内核态相当于r0。

用户空间和内核空间
为了确保操作系统的安全稳定运行,操作系统启动后,将会开启保护模式:将内存分为内核空间(内核对应进程所在内存空间,存储内核代码)和用户空间(存储用户代码),进行内存隔离
处于用户态的程序只能访问用户空间,而处于内核态的程序可以访问用户空间和内核空间

用户态->内核态
用户态到内核态的切换,总的来说,有以下几种方式
1.系统调用(软件中断)

这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断

2.异常(出错(fault)和陷阱(trap))

当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态。异常分为出错和陷入两种。

出错(fault)保存的EIP指向触发异常的那条指令;而陷入(trap)保存的EIP指向触发异常的那条指令的下一条指令。因此,当从异常返回时,出错(fault)会重新执行那条指令;而陷入(trap)就不会重新执行。这一点实际上也是相当重要的,比如我们熟悉的缺页异常(page fault),由于是fault,所以当缺页异常处理完成之后,还会去尝试重新执行那条触发异常的指令(那时多半情况是不再缺页)。上文中提到的系统调用,其实属于trap的一种,int 3也是trap的一种,调试器原理之一就是int 3了。

3.外围设备的硬件中断(硬中断和软中断)

当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

回到具体的,当操作系统收到了用户态的请求时,会发生以下情况:

1.切换GS寄存器

通过 swapgs 切换 GS 段寄存器,将 GS 寄存器值和一个特定位置的值进行交换,目的是保存 GS 值,同时将该位置的值作为内核执行时的 GS 值使用

2.保存用户态栈信息。

将当前栈顶(用户空间栈顶)记录在 CPU 独占变量区域里,将 CPU 独占区域里记录的内核栈顶放入 rsp/esp

3.保存用户态寄存器信息.

通过 push 保存各寄存器值到栈上,以便后续“着陆”回用户态

4.控制权转交内核,执行系统调用

在这里用到一个全局函数表sys_call_table,其中保存着系统调用的函数指针

内核态->用户态
内核态到用户态,只需要再执行一次swapgs切换寄存器,然后使用sysretq或者iretq恢复到用户空间即可.

2.进程管理
进程描述符(process descriptor)
在内核中,使用 task_struct 结构体来对每个进程进行管理,其结构如图所示

这些大概知道一下内容就行,我们主要关注的也不是这个。
在一个 task_struct 结构体中,有一部分声明:
/* Process credentials: */

/* Tracer's credentials at attach: */
const struct cred __rcu *ptracer_cred;

/* Objective and real subjective task credentials (COW): */
const struct cred __rcu *real_cred;

/* Effective (overridable) subjective task credentials (COW): */
const struct cred __rcu *cred;

这是进程权限凭证相关的部分

ptracer_cred:使用ptrace系统调用跟踪该进程的上级进程的cred(gdb调试便是使用了这个系统调用,常见的反调试机制的原理便是提前占用了这个位置
real_cred: 客体凭证(objective cred) ,一个进程刚刚启动时的权限
cred: 主体凭证(subjective cred) ,该进程的有效cred,这才是真正标志进程权限的凭证。

现在仔细看看cred结构体的源码:
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;

其中

real UID:标识一个进程启动时的用户ID(你是root启动进程,它就是root,你是普通用户它就是普通用户
saved UID:标识一个进程最初的有效用户ID
effective UID: 进程的真正权限
UID for VFS ops:标识一个进程创建文件时进行标识的用户ID

real GID,saved GID,effective GID,GID for VFS ops与上面的类似。

提权
一个进程的权限是由位于内核空间的cred结构体进行管理的,只要改变一个进程的cred结构体,就能改变其执行权限
内核提供了修改进程cred权限的函数

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(

Pixel2xl刷机

https://wsxk.github.io/pixel2xl%E5%88%B7%E6%9C%BA/

大四菜鸡,闲着没事干决定整一波android的真机调试操作,于是就开始了折腾工作。

基础知识预备

  1.pixel 2xl的欧版和美版是啥
  2.bootloader是啥
  fastboot和recovery

step1:物理设备准备
step2:电脑环境准备

  1.官方刷机包
  2.USB驱动程序
  3.SDK工具

step3:pixel 2xl开启开发者模式
step4:刷机
step5:root
references

基础知识预备
其实想完成刷机的大可以从 step1:物理设备准备 开始看起
1.pixel 2xl的欧版和美版是啥
米娜桑去买手机的时候,会发现欧版卖得比美版更贵。主要原因在于,美版bootloader是锁定的,不允许你刷机,然而欧版的bootloader是解锁的,可以刷机。
当然,如果你是高人,美版其实也是可以解锁的,我是菜菜,图方便所以….
2.bootloader是啥
bootloader可以类比于电脑里的BIOS,是开机启动时运行的第一道程序
bootloader的工作大致可以分为2个阶段: 一、硬件初始化 二、加载内核
其中,在第二阶段,如果有特殊信号发现(以piexel 2xl为例,就是音量的下调键),那么会进入
fastboot模式
如果没有发现启动fastboot的信号,bootloader继续运行,然后发现了另一个特殊信号(也是手机按键,具体哪个忘记了),那么会进入recovery模式
fastboot和recovery
fastboot,它是bootloader后期进入的一个特殊阶段。可以通过数据线与电脑连接,然后在电脑上执行一些命令,如刷系统镜像到手机上。fastboot可以理解为实现了一个简单的通信协议,接收命令并更新镜像文件,其他什么的干不了。
须有一个PC机并且USB线要始终联着。所以这种方式称为线刷。
而 recovery 模式呢,它会加载recovery.img(包含一个kernel文件,recovery可执行程序以及一些初始化文件),相当于进入了一个微型的操作系统。
recovery恢复时,刷机包通常放在SD卡里,所以这里刷机一般称为卡刷
step1:物理设备准备

windows11 电脑一枚
pixel 2xl 欧版(android 11)
pixel 2xl 数据线一根
step2:电脑环境准备
1.官方刷机包
首先前往google的官方刷机包下载网站下载合适的镜像
https://developers.google.cn/android/images#taimen

按照本人的试验机(pixel 2xl),选最底下的那个(Dec 2020),单击Link下载压缩包。
2.USB驱动程序
https://developer.android.com/studio/run/win-usb

下载后,安装驱动程序(鼠标右键inf文件安装),安装后重启
3.SDK工具
https://developer.android.com/studio/releases/platform-tools
解压后,添加环境变量(windows terminal可以直接使用adb命令即算成功)
step3:pixel 2xl开启开发者模式
首先使用数据线将电脑和手机相连
手机里选择设置-》关于手机-》版本号,点击若干下,直到进入开发者模式
设置-》系统-》高级-》开发者选项里,首先进行oem解锁,然后开启usb调试接口
使用命令adb shell

能够进入后台即算成功
step4:刷机
重启pixel2 xl,重启时,按住音量键的下调键,进入fastboot界面
在官方刷机包的解压文件夹内,运行flash-all.bat,即开始自动刷机
step5:root
https://github.com/topjohnwu/Magisk
在上述网址里下载magisk的新版apk程序

使用adb install Magisk-v25.2.apk 在手机上安装magisk程序
大白话就是,在官方镜像包解压的文件夹里,还有一个压缩包

解压,把里面的boot.img通过adb push boot.img /storage/emulated/0/Download/boot.img放入android系统里,之所以要放入这个文件夹,因为/storage/emulated/0是android系统ui挂载的目录,使用magisk进行安装时,要想magisk能够访问到boot.img,需要push到这个界面里。
https://topjohnwu.github.io/Magisk/install.html,跟随这个网址的安装教程进行安装。
在magisk对boot.img进行修改后,同目录下会出现新img

把magisk_patched-25200_dNj2G.img 通过命令 adb pull /storage/emulated/0/Download/magisk_patched-25200_dNj2G.img ./magisk_patched-25200_dNj2G.img 传回电脑里
再进一次fastboot模式,使用fastboot flash boot /path/to/magisk_patched-25200_dNj2G.img 把镜像刷入。重启手机,尝试su时,即可以拿到root

references
https://www.cnblogs.com/codebai/p/16048498.html
https://blog.csdn.net/u011216417/article/details/75949409

java misc knowledge

https://wsxk.github.io/java_misc_konwledge/

记录一下java逆向时学到的一些东西。

jdk 源码
@OverRide
AWT和Swing

  控件绑定

reference

jdk 源码
记录一下jdk源码的位置
其实在你安装java环境时,会自动安装一份源码(以压缩包形式存放于你的java目录里)
在目录里搜索src.zip

解压后就可以查看响应的jdk源码(但是真的是又臭又长,解压开来有212MB,看都能给你看麻)

@OverRide
字面意思,表示该方法可以被重载

AWT和Swing
AWT和Swing都是java中的package

AWT(Abstract Window Toolkit):抽象窗口工具包,早期编写图形界面应用程序的包。
Swing :为解决 AWT 存在的问题而新开发的图形界面包。Swing是对AWT的改良和扩展

java的gui应用程序会使用如上两个包来进行开发
控件绑定
可以使用下面的demo感受一下控件的绑定和监听
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class Test {
public static void main(String[] args) {
new MyFrame();
}
}

class MyFrame extends JFrame implements ActionListener, MouseListener {
public MyFrame() {
this.setTitle(

Docker 学习

https://wsxk.github.io/docker/

Docker其重要性也不用我再强调,其流行也表明了其优越性。

Docker简介
Docker的基本概念
Docker安装过程
Docker命令

  1.启动命令
  2.帮助命令
  3.镜像命令
  4.容器命令

docker镜像构建

  1.dockerfile基本用法

Docker简介
docker的产生解决了程序移植和部署过程中产生的环境配置问题
相比于传统的虚拟机,docker更轻量,更高效,更便捷(虚拟机是基于硬件层面的虚拟化,而docker是基于内核层面的虚拟化,不像虚拟机那样每次虚拟就要占用大量的硬件资源)。
相比于过去,一个应用可以由多个docker组装而成,使得应用更加模块化,想要升级相应的部件,只需要更新部件对应的docker组件即可,十分方便

Docker的基本概念
镜像(image):可以类比c++中的类模板
容器(container):由类模板创建出的对象
仓库(repository):存放各种镜像的地方

Docker安装过程
关于Docker的安全,网上有许多有用的教程,这里不过多赘述

Docker命令
1.启动命令
docker的启动命令依赖于linux自带的systemctl,使用方式如下:

systemctl start docker # 开启docker服务
systemctl stop docker # 关闭docker服务
systemctl restart docker # 重启docker服务
systemctl status docker #  查询docker状态
systemctl enable docker # 使得docker能够开机自启动

2.帮助命令
当你忘记某些命令时,可以采取以下命令获得帮助

docker info #获得关于docker的基本信息
docker –help #获得可以使用的命令列表
docker cmd –help #获得某条特定命令的文档

3.镜像命令

docker images #显示当前存在的镜像
docker search xxx #查询镜像
docker pull xxx[:TAG] #从dockerhub拉取镜像
docker system df #df意思为disk free,查询docker占据的硬盘空间大小
docker rmi xxx #删除镜像、

4.容器命令

docker ps  #列出容器
docker run #创建并运行一个容器
docker start #启动一个容器
docker restart #重启
docker stop #停止容器(优雅
docker kill #停止容器(不优雅
docker rm  #删除一个容器
docker logs id #打印容器日志
docker top id #打印容器的运行进程信息
docker inspect id #打印容器配置信息
docker exec -it id /bin/bash #在正在运行的容器中启动一个新交互终端
docker attach id # 附加到在正在运行的容器中的终端(不会开启新终端)
docker cp # 容器和宿主机的文件传输
docker export id > xxx.tar #导出容器
docker import xxx.tar name:tag #导入镜像

如何从一个交互式脚本中退出,docker提供了2种方式,在shell中运行exit,退出容器后,容器也会停止,如果使用ctrl+p+q的话,退出交互shell,但是容器仍然运行。

docker镜像构建
可以使用dockerfile构建自己的镜像
构建时使用如下格式:
docker build -t image_name:tag . #使用当前目录下的dockerfile构建一个镜像image_name:tag

1.dockerfile基本用法
FROM ubuntu
RUN <运行命令> # 相当于在镜像中执行命令
<运行命令> # 相当于在本地执行命令

linux内核基础 五 seq_file结构体

https://wsxk.github.io/linux_kernel_basic_five/

seq_file
seq_file相关结构定义
seq_file相关API

  seq_read执行流程
  seq_write执行流程

seq_file编写实例

  (1)注册的实例代码
  (2)实现struct file_operations proc_ops的定义
  (3)实现seq_operations结构体

PS1:single_open
PS2:创建proc文件的三种方法
references

seq_file
seq_file最初是为了方便内核调试(传递信息到用户态便于查看)而创建的数据结构。
当时为了能够将调试信息导入到sysfs,debugfs,procfs,工作人员使用了许多不同的实现,但是都无法避免的产生一些漏洞。
因此,为了规范标准和便于维护,一些内核hacker依靠自己的经验和总结,实现了一套统一的接口,即seq_file
seq_file的使用过程如下(需要有一定的驱动编程基础,没有基础的同学可以自行百度)

seq_file相关结构定义
seq_file结构定义在include/linux/seq_file.h中,结构如下:
struct seq_file{
char * buf;//seq_file接口使用的缓存页指针
size_t size;//seq_file接口使用的缓存页大小
size_t from;//从seq_file中向用户态缓冲区拷贝时相对于buf的偏移地址
size_t count;//buf中可以拷贝到用户态的字符数
loff_t index;//start()、next()的处理的下标pos值
loff_t read_pos;//当前已拷贝到用户态的数据量大小
u64 version;
struct mutex lock;//互斥锁,同步相关
const struct seq_operations *op;//需要自己实现的操作底层函数
void * private;
}
struct seq_operations{//自定义的函数
void *(*start)(struct seq_file * m,loff_t *pos);//首先被调用的函数,设置访问起点,pos不是字节,而是自定义的单位
void (*stop)(struct seq_file *m,void *v);//结束,通常直接返回就行,如果需要做退出处理就在这里编写代码
void *(*next)(struct seq_file *m,void *v,loff_t *pos);//移动到下一个数据元素
int (*show)(struct seq_file *m,void *v);//将v指向的元素的数据拷贝到seq_file的内部缓存buf中
}

seq_file相关API
int seq_open(struct file*file,const struct seq_operations *op);//将seq_operations赋值给file结构体。
int seq_release(struct inode *inode,struct file *file);
ssize_t seq_read(struct file * file,char __user *buf,size_t size,loff_t *ppos);
int seq_write(struct seq_file *seq,const void * date,size_t len);
loff_t seq_lseek(struct file *file,loff_t offset,int whence);

seq_read执行流程
seq_read是最为重要的一个函数,它负责从内部buffer缓冲区中读取数据并调用copy_to_user 返回给应用层。阅读代码会发现该函数实现如下操作:

创建一个buffer(通常情况默认是 1个page的大小)
调用seq_operation中的 start函数
调用seq_operation中的show函数
调用seq_operation中的next函数
判断buffer大小是否足够,不够需要释放buffer,然后创建一个更大的buffer(通常情况是*2)
循环上述步骤直到不用写入且buffer没有溢出
将buffer内容调用copy_to_userf返回给用户态

seq_write执行流程
关于seq_write函数,其实它和seq_read并不对称。
seq_write只把内核的消息拷贝到seq_file的buffer当中,并不涉及用户态向内核态传送内容(seq_file系列本来就是只读的)
通常在show()函数中会调用seq_write函数来往buffer写入内容.

seq_file编写实例
(1)注册的实例代码
proc_test_entry = create_proc_entry(

D^3CTF 2019 - KNOTE 复现(userfaultfd + modprobe)

https://wsxk.github.io/zD3CTF2019_knote/

题目分析
首先看题目是没有溢问题的。
其次是审计代码时,发现get函数和edit函数没有加锁(会导致竞态条件的问题)
这时利用userfaultfd + modprobe 就可以获取flag了。

exp
直接借用了arttnba3

kernel.h
#include <sys/types.h>
#include <stdio.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>

void * kernel_base = 0xffffffff81000000;
size_t kernel_offset = 0;

static pthread_t monitor_thread;

void errExit(char * msg)
{
printf(

idapython常用api

https://wsxk.github.io/idapython/

之前看其他大佬用脚本自动化修改ida中的数据库,觉得十分牛逼,于是想着把idapython学一下,事实证明,学完后大受震撼,一下子就打开了新大陆。
PS: 所有的函数都基于新版idapython(从ida支持python3开始)
详情可以查官方文档https://hex-rays.com/products/ida/support/idapython_docs/

导入库
汇编操作
get&patch操作
段操作
函数操作
搜索操作
交叉引用

导入库
开局先导入
import idc
import idaapi
import idautils
import ida_funcs
import ida_kernwin
import ida_search
import ida_xref

汇编操作
ida可以支持写脚本批量处理汇编语句
idc.generate_disasm_line(addr,flags) # 获得addr位置的一条汇编语句
idc.print_operand(addr,index) # 获得addr位置的汇编语句的 第index个操作数
idc.print_insn_mnem(addr) # 获得addr位置的汇编语句的 操作符
idc.get_operand_type(addr,index) #获得addr位置的汇编语句的第index个操作数的 类型
idc.get_operand_value(addr,index) #获得addr位置的汇编语句的第index操作数的值
idaapi.get_imagebase() #获得程序基地址
idc.here() # 获得当前光标所在的位置
idc.next_head(addr) #addr位置的下一条汇编语句的位置
idc.prev_head(addr) #addr位置的上一条汇编语句的位置

get&patch操作
ida支持提取数据和修改数据
idc.get_db_byte(addr) # 获得从addr开始的一字节
idc.get_wide_word(addr) #获得从addr开始的2字节
idc.get_wide_dword(addr) # 4字节
idc.get_qword(addr) #8字节
idc.get_bytes(addr,size,use_dbg) #从addr获取size个字节后形成的bytes对象
idc.patch_byte(addr,value)#将addr处的一字节改成value
idc.patch_word(addr,value)# 2bytes
idc.patch_dword(addr,value)# 4bytes
idc.patch_qword(addr,value)#8bytes

段操作
idc.get_segm_name(addr) #获得addr所在segment的名称
idc.get_segm_start(addr) #获得addr所在segment的起始位置
idc.get_segm_end(addr) # 末尾位置
idc.get_first_seg() #获得程序的第一个段的起始地址
idc.get_next_seg(addr)#获得addr所在段的下一个段的起始地址
segments=idautils.Segments()#获得当前程序所有段的迭代器
for each in segments:
print(each) #打印每个段的起始地址

函数操作
Functions(startaddr,endaddr)#获得指定地址间的所有函数
idc.get_func_name(addr) #获得addr所在函数的函数名
idc.get_func_cmt(addr,repeatable)#获取函数的注释 repeatable通常为0
idc.set_func_cmt(addr,strings,repeatable) #设置函数注释
idc.choose_func(title) # 弹出窗口,由用户选择一个func
idc.get_func_off_str(addr)#将addr表现为 函数名称+偏移的形式
idc.find_func_end(addr)#找到addr所在函数的结尾
idc.set_func_name(addr,name,0)#设置addr所在函数的新名称
idc.get_prev_func(addr)#上一个函数的起始位置
idc.get_next_func(addr)#下一个函数的起始位置
ida_funcs.set_func_start(addr,newstart)#重新设置函数的起始位置
ida_funcs.set_func_end(addr,newend)#重新设置函数的末尾位置

搜索操作
idc.find_binary(addr,flags,search_str)#从addr开始,按照flags规定的方式,搜索search_str(比如

码农的 五险一金&收税 学习

https://wsxk.github.io/%E4%BA%94%E9%99%A9%E4%B8%80%E9%87%91/

五险一金对于打工人的作用也不用我多分析了,想必大家找工作时都听说过五险一金,公司收人时,也会把五险一金作为福利之一来吸引人才投递简历。
但是相信大部分人对于五险一金没有一个清晰的认识,但是了解五险一金是非常重要的,基本上是每个打工人的必备知识!!!(如果你对你的钱不感兴趣的话,你也可以不了解
普遍而言,五险指的是 养老保险、医疗保险、工伤保险 、工伤保险 、 失业保险 、一金指的是 公积金。(可能随政策的变化出现调整)。

五险

  养老保险
  医疗保险
  生育保险
  工伤保险
  失业保险

一金

  公积金

收税计算规律
补充

  假设你是某个公司的员工
  年终奖怎么算

五险
养老保险
养老保险顾名思义,就是你退休后每个月可以领到的钱
如果你在公司上班,养老保险默认抽取你的工资的8%,(其实公司需要交你工资的18%,视政策而定)其他其实可以不用管。(由不得你不交,哈哈)
要想退休后领钱,现在的政策是交满15年后,退休即可拿到。麻,但是普通人(非公务员老师)大概率是拿不到养老金的(别问我为什么知道),而且当前政策趋势,会逐步提高退休后能拿养老金的门槛(比如原本只要交满15年,现在要你交满20年)。
这种保险,其原理大概是当代年轻人的交钱给当代退休人员用,如果我们下一辈年轻人不干活了,你也就没有钱啦。

医疗保险
医疗保险,即你去医院看病的时可以报销的钱,
单位缴6%个人缴2%(视地区政策而定)。从参保缴费之日起,满6个月后才能享受职工基本医疗保险待遇。
这玩意还挺好用的,只要去医保定点医院,一般看病都能直接覆盖,刷你自己的医保卡就行。

生育保险
生育保险由用人单位缴纳。生育保险费率为0.3%(视地区政策而定),哈哈反正不是你交钱。
可能可以报销一部分产检费。

工伤保险
工伤保险和生育保险一样,都是由用人单位缴纳,不会从你工资里扣。
一般用不上。
失业保险
失业保险,如果你失业了,就可以领的钱(以备不时之需)
单位缴0.6%个人缴0.4%,失业保险必须在缴满一年后才能享受,一般为交一年领2个月,交2年领4个月,但享受的最高时限不能超过24个月(视政策而定)
不过有一点,如果你失业了,最好趁早把这个失业金给领了。如果时间托太长maybe就没有了…

一金
公积金
住房公积金存缴的比例是5%~12%(看公司给你交多少),从个人工资上扣。
公积金完全是你自己的钱,相当于另一个小金库!虽然说是买房用,其实不买房也是可以的
如果你在外地打工,离职后可以完完全全把公积金提取出来。

收税计算规律
先上公式:

税收 =(年工资-五险一金*12 - 起征点 * 12 -专项附加扣除 * 12) * 相应税率系数。

所以公积金越高越好!!(12%拉满最棒了!)
先看全年收入缴纳的税率。

税率还有一个重要的东西就是起征点和专项扣除项目
起征点 是指工资达到一定水平才收税(现在的情况是月工资超5000收税)
专项扣除项目扣除的是该月教育子女、赡养老人的支出(这些费用不会收税,但是每个月标准是固定的,如下图)

补充
其实关于五险一金有很多细节没有补充,然而这些细节是很重要的(因为细节往往才对应现实情况,了解了大体之后可以再看看工作当地的五险一金政策)。

假设你是某个公司的员工
你是一个大学毕业生,你在某个合法公司干活(刚刚入职),公司给你开的base是25k*15(一个月基本工资是25k,年终奖是3个月工资)
第一个月结束了,你成功获得了人生的第一桶金,但其实你并不是真的获得了25k,首先去除五险一金(公积金拉满的话,一般是月工资的22%)好,还剩下25k * 0.78=19.5k,那么你应该拿到19.5k吗,并不是,剩下的19.5k应该实行超额累进计税的机制。
首先假定你在租房(住房租金是1100/月),你刚刚毕业,你的爹妈还没60岁,也没孩子,没啥大病(暂时)

19.5k - 5000(起征点)- 1100(房租租金专项扣除) = 13400,对照税率表,此时你的年收入(暂定)在第一级里(小于36000),所以你应该要交的税是13400 * 0.03 = 402 元, 第一个月收到的工资实际为 19098元。

第二个月结束了,你成功获得了第二笔钱,同样是扣除五险一金后,剩下19.5k,那么怎么计算?

19.5k * 2 - 5000 * 2 - 1100 * 2 = 26800 同样小于36000,此时交的税是 26800 * 0.03 - 402 = 402 元,第二个月收到的工资是19098元。

到了第三个月情况发生了转变。

19.5k *3 - 5000 * 3 - 1100 * 3 = 40200, 有4200超过了第一级,所以税收是 36000 * 0.03 + 4200 * 0.1 - 402 * 2 = 696元,所以第三个月到账工资为 18804元。

年终奖怎么算
上例中25k15的工资,年终奖其实是25k * 3 = 75k。
这部分工资怎么算?
目前有2种计算方法: *计入年总收入计算 、单独计算

计入年总收入计算: 很好理解,就是总收入等于 基础工资加年终奖,直接进行计算.
单独计算:年终奖有另一套机制,采用下一套年终奖税率表和工资分开计税。

至于为什么要有2种方法(临时缓冲,单独计算估计没几年就要废弃了,以后都是计入总收入来算)。
现在有2种方法,你可以选择看看哪种方法交的税更少,选哪种交。

linux内核基础 一

https://wsxk.github.io/linux_kernel_basic_one/

更新于2022-10-12

什么是内核
内核架构

  宏内核(Monolithic Kernel)
  微内核(Micro Kernel)

linux系统启动过程

  1.加载BIOS(Basic I/O System)
  2.读取MBR(Master Boot Record,主引导记录)
  3.(GNU GRUB)引导
  4.加载kernel
  5.启动守护进程init

kernel部分原理

  1.分级保护域(hierarchical protection domains,简称rings)
    
      用户态和内核态
      用户空间和内核空间
      用户态->内核态
      内核态->用户态
    
  
  2.进程管理
    
      进程描述符(process descriptor)
      提权
    
  
  3.I/O
    
      进程文件系统(process file system,procfs)
      文件描述符(file descriptor,fd)
    
  
  4.Loadable Kernel Modules(LKMs)
  5.内核内存管理
    
      buddy system
        
          page
          pageblock
          page order
          迁移类型
          buddy system分配过程
        
      
      slab alloctor
        
          slab allocator的版本
          slub数据结构
          slub分配过程
          slub释放过程
        
      
      mmap、brk怎么申请内存
    
  
  6.kernel保护机制
    
      1.KASLR(kernel address space layout randomize,内核地址空间随机化)
      2.FGKASLR
      3.STACK PROTECTOR
      4.SMAP/SMEP(Supervisor Memory Access/Execution Prevention)
      5.KPTI(kernel page-table isolation,内核页表隔离)
      6.heap保护
        
          ①Hardened Usercopy
          ②Hardened freelist
          ③Random freelist

reference

什么是内核
操作系统(Operation System) 本质上也是一种软件,可以看作是普通应用程式与硬件之间的一层中间层,其主要作用便是调度系统资源、控制IO设备、操作网络与文件系统等,并为上层应用提供便捷、抽象的应用接口
而 内核(kernel) 是操作系统最重要的一部分。

通常来说,操作系统= 内核+文件系统(file system)

文件系统简单理解就是用来管理存放在硬盘上的文件和目录的。

kernel提供了除文件系统以外的其他功能:
1.控制并与硬件进行交互
2.提供应用程式运行环境
3.调度系统资源

  I/O,权限控制,系统调用,进程管理,内存管理等多项功能都可以归结到以上三点中

有一张图可以直观地看出内核的在计算机体系结构中的位置

内核架构
传统的内核架构有两种: 宏内核和微内核
宏内核(Monolithic Kernel)
宏内核顾名思义,它其实是一个完整的可执行的二进制程序,在内核态以监管者模式(Supervisor Mode)来运行。
宏内核将所有提供的功能都打包了起来,并向上层程序提供API接口。

微内核(Micro Kernel)
微内核,大部分的系统服务(如文件管理等)都被剥离于内核之外,内核仅仅提供最为基本的一些功能:底层的寻址空间管理、线程管理、进程间通信等。

虽然有宏内核和微内核各有优势,宏内核因为所有功能集中在一起,效率比较高(但是维护困难,不易扩展),微内核因为模块化,所以维护方便,设计难度大大降低(但是因为不同模块的通信问题导致效率降低)。当然了历史告诉我们,出现了两个极端的设计理念后,最理想的理念往往是取长补短,于是出现了混合内核(hybrid kernel)。

举个例子,linux的内核虽然整体架构是宏内核,但是为了提高可扩展性和可维护性,也允许用户自己编写模块并在内核中部署(可装载内核模块(Loadable Kernel Modules,简称LKMs)

微软虽然宣称自己是微内核,但是它里面也有一些重要的功能必须集成到内核上。

linux系统启动过程
根据我自己学习内核基础知识的过程,我觉得大家可能会对linux系统的引导过程产生异或,因此在真正讲述基础知识前,我会先解释一下linux的启动过程。

1.加载BIOS(Basic I/O System)
当你开启计算机电源,首先加载的是基本输入输出系统(Basic Input Output System ),就是BIOS

bios程序一般放在主板ROM(Read Only Memory)中,关机或掉电也不会消失。

BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。在此之后,计算机就知道应该去读取哪个硬件设备了

2.读取MBR(Master Boot Record,主引导记录)
读取硬盘上磁道的第一个扇区(MBR),它的大小是512字节.
前446字节存放的就是grub程序的一部分、后64字节是硬盘分区表,里面存放了预启动信息、分区表信息。
系统找到BIOS所指定的硬盘的MBR后,就会将其复制到0×7c00地址所在的物理内存中。其实被复制到物理内存的内容就是Boot Loader,而具体到你的电脑,那就是lilo或者grub了

3.(GNU GRUB)引导
GRUB是boot loader的一种
GRUB是多启动规范的实现,它允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统

4.加载kernel
根据grub指定的内核映像的路径,来加载内核映像
系统将解压后的内核放置在内存之中,并调用start_kernel()函数来启动一系列的初始化函数并初始化各种设备,完成Linux核心环境的建立。至此,Linux内核已经建立起来了,基于Linux的程序应该可以正常运行了。

5.启动守护进程init
init(sbin/init)是linux的第一个进程,这个进程读取相应的配置文件并启动一系列进程,这个进程的PID为1,所有的进程都由它衍生,都是它的子进程.
这个进程会读取/etc/inittab文件,/etc/inittab文件的作用是设定Linux的运行等级以及执行项(比如下文出现的rc.sysinit和rc*.d,tty)来启动对应的程序

运行等级如下。
•0:关机模式
•1:单用户模式
•2:无网络支持的多用户模式
•3:字符界面多用户模式
•4:保留,未使用模式
•5:图像界面多用户模式
•6:重新引导系统,重启模式

随后,init进程会fork出子进程执行/etc/rc.d/rc.sysinit(inittab文件告诉init要执行它)

这个脚本对系统进行一系列的初始化,包括时钟、键盘、磁盘、文件系统等初始化

然后init执行启动层级对应脚本(rc*.d),在/etc/inittab配置如下

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

rc执行完毕之后,系统环境已经设置完成,各种服务进程也已经启动。init开始启动终端程序。inittab文件中执行项通常如下

1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

kernel部分原理
1.分级保护域(hierarchical protection domains,简称rings)
分级保护域是一种将计算机不同的资源划分成不同权限的模型。
在一些硬件或者微代码级别上提供不同特权态模式的 CPU 架构上,保护环通常都是硬件强制的。Rings是从最高特权级(通常被叫作0级)到最低特权级(通常对应最大的数字)排列的
内层ring可以任意调用外层ring的资源,内层ring到外层ring可调用资源依次递减。
通常情况下,内核运行在r0级,用户程序运行在r3级(其实中间2级基本上没什么人用)
r3级其实没什么用,基本上啥都不能干(没权限),但是我们的用户程序也需要进行 访问文件,连接网络,与其他进程通信的操作,这时候应该怎么办?
聪明的内核设计者们提出了一个聪明的办法:当用户程序(r3)需要访问系统资源时,会向操作系统发送请求,操作系统(r0)会帮你完成你的请求,然后返回用户进程(r3)。这里涉及了r3-r0-r3的切换,接下来会具体讲解这个

用户态和内核态
用户态其实相当于r3,内核态相当于r0。

用户空间和内核空间
为了确保操作系统的安全稳定运行,操作系统启动后,将会开启保护模式:将内存分为内核空间(内核对应进程所在内存空间,存储内核代码)和用户空间(存储用户代码),进行内存隔离
处于用户态的程序只能访问用户空间,而处于内核态的程序可以访问用户空间和内核空间

用户态->内核态
用户态到内核态的切换,总的来说,有以下几种方式
1.系统调用(软件中断)

这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断

2.异常(出错(fault)和陷阱(trap))

当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态。异常分为出错和陷入两种。

出错(fault)保存的EIP指向触发异常的那条指令;而陷入(trap)保存的EIP指向触发异常的那条指令的下一条指令。因此,当从异常返回时,出错(fault)会重新执行那条指令;而陷入(trap)就不会重新执行。这一点实际上也是相当重要的,比如我们熟悉的缺页异常(page fault),由于是fault,所以当缺页异常处理完成之后,还会去尝试重新执行那条触发异常的指令(那时多半情况是不再缺页)。上文中提到的系统调用,其实属于trap的一种,int 3也是trap的一种,调试器原理之一就是int 3了。

3.外围设备的硬件中断(硬中断和软中断)

当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

回到具体的,当操作系统收到了用户态的请求时,会发生以下情况:

1.切换GS寄存器

通过 swapgs 切换 GS 段寄存器,将 GS 寄存器值和一个特定位置的值进行交换,目的是保存 GS 值,同时将该位置的值作为内核执行时的 GS 值使用

2.保存用户态栈信息。

将当前栈顶(用户空间栈顶)记录在 CPU 独占变量区域里,将 CPU 独占区域里记录的内核栈顶放入 rsp/esp

3.保存用户态寄存器信息.

通过 push 保存各寄存器值到栈上,以便后续“着陆”回用户态

4.控制权转交内核,执行系统调用

在这里用到一个全局函数表sys_call_table,其中保存着系统调用的函数指针

内核态->用户态
内核态到用户态,只需要再执行一次swapgs切换寄存器,然后使用sysretq或者iretq恢复到用户空间即可.

2.进程管理
进程描述符(process descriptor)
在内核中,使用 task_struct 结构体来对每个进程进行管理,其结构如图所示

这些大概知道一下内容就行,我们主要关注的也不是这个。
在一个 task_struct 结构体中,有一部分声明:
/* Process credentials: */

/* Tracer's credentials at attach: */
const struct cred __rcu *ptracer_cred;

/* Objective and real subjective task credentials (COW): */
const struct cred __rcu *real_cred;

/* Effective (overridable) subjective task credentials (COW): */
const struct cred __rcu *cred;

这是进程权限凭证相关的部分

ptracer_cred:使用ptrace系统调用跟踪该进程的上级进程的cred(gdb调试便是使用了这个系统调用,常见的反调试机制的原理便是提前占用了这个位置
real_cred: 客体凭证(objective cred) ,一个进程刚刚启动时的权限
cred: 主体凭证(subjective cred) ,该进程的有效cred,这才是真正标志进程权限的凭证。

现在仔细看看cred结构体的源码:
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;

其中

real UID:标识一个进程启动时的用户ID(你是root启动进程,它就是root,你是普通用户它就是普通用户
saved UID:标识一个进程最初的有效用户ID
effective UID: 进程的真正权限
UID for VFS ops:标识一个进程创建文件时进行标识的用户ID

real GID,saved GID,effective GID,GID for VFS ops与上面的类似。

提权
一个进程的权限是由位于内核空间的cred结构体进行管理的,只要改变一个进程的cred结构体,就能改变其执行权限
内核提供了修改进程cred权限的函数

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(

fmt 详解

https://wsxk.github.io/fmt%E8%AF%A6%E8%A7%A3/

概述
fmt(格式化字符串漏洞)这个漏洞一开始学的时候不觉得有什么,后来在做题时发现它能做到的事情挺多的,这里写下来做个记录

格式化字符串函数可以接受可变数量的参数,并将第一个参数作为格式化字符串,根据其来解析之后的参数

常见的函数有

输入
scanf

输出

printf 输出到 stdout

fprintf	    输出到指定 FILE 流

vprintf	    根据参数列表格式化输出到 stdout

vfprintf	    根据参数列表格式化输出到指定 FILE 流

sprintf	    输出到字符串

snprintf	    输出指定字节数到字符串

vsprintf	    根据参数列表格式化输出到字符串

vsnprintf	根据参数列表格式化输出指定字节到字符串

setproctitle	设置 argv

syslog	    输出日志

err, verr, warn, vwarn 等

fmt的功能有很多

常见的符号

%x %lx
%n(4) %hn(2) %hhn(1) %lln(8)
%s
%p
%d

1.使程序崩溃

一般情况下,用

%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s

不出意外都能让程序崩溃

  1. 泄露内存

可以通过%x-%x-%x-%x-%x-%x来获得栈的变量

为了快捷,也有 %number$x 的形式,值得一提的是,这里的number值得是除开格式化字符串的第几个参数

比如 printf(“abcdef”,4,6,7,8,9)

%4$x指向8,%3$x指向7

想获得内存中的变量 可以使用

addr%number$x

的形式来获得addr的值

  1. 覆盖内存
    可以用

addr%12c%number$n

的形式覆盖addr的值

%12c会打印出偏移的值,并且必然是12个字节

但addr可以放在任意的位置,你找得到偏移就行

ida 创建结构体

https://wsxk.github.io/ida%E7%BB%93%E6%9E%84%E4%BD%93%E5%88%9B%E5%BB%BA/?query=%E7%BB%93%E6%9E%84%E4%BD%93

玩二进制的应该都会遇到这问题(

有一个几百字节的数组 v1

v1[48]= 23

v1[60]= 44

你看得时候会不会麻?

我们需要给这个数组添加语义信息。

接下来演示如何用ida创建结构体并带入

1.查看代码,分析结构体

看到这个,大家应该知道

v1是个结构体指针

根据名字可以看出

v1[0]其实是指向name的指针

v1[1]是指向note指针

v1[2]是name的大小

v1[3]是note的大小

v1[4]是下一个chunk

2.构建结构体

用shift + F9 进入structure

然后按lns(shift + 小键盘0)

写入名称

然后按

按D键选择参数占的字节(1,2,4,8)

按N键改名

按U键取消定义

改完后像这样

应用

现在返回add函数

选择v1变量 然后按 Y,改成这个样子

reference

https://blog.csdn.net/qq_33163046/article/details/124290143

https://blog.csdn.net/weixin_45799954/article/details/115009760

GoldenDict导入词典

https://wsxk.github.io/GoldenDict%E5%AF%BC%E5%85%A5%E8%AF%8D%E5%85%B8/

GoldenDict是一款开源并且强大的词典查询ui工具。
其作用十分简单:导入词典格式的文件方便查询
推荐GoldenDict的原因也很简单,它支持最多的文件格式解析。
稍微探索了一下软件后,大概知道了如何使用并导入词典。

GoldenDict安装
https://github.com/goldendict/goldendict
为源码地址,但是我们肯定不用源码编译,太麻烦了,可以考虑源码编译后的压缩包,开袋即食:
https://github.com/goldendict/goldendict/wiki/Early-Access-Builds-for-Windows

打开后,界面是长这样的:

此时可以按 F3 出现如下界面:

可以通过添加按钮添加词典文件所在的文件夹,这样GoldenDict就会自动识别到词典文件并解析。

词典文件通常由几个部分组成:

mdx文件:该类型文件包含了词典的所有 文本信息
mdd文件:一个图文并茂的字典肯定不止只有文本内容,mdd包含如图片,语音,css文件,js脚本等
css文件:修饰页面,使界面更好看。

Android逆向学习 二

https://wsxk.github.io/android%E9%80%86%E5%90%91%E5%AD%A6%E4%B9%A0_2/

.class和.dex文件的区别

  class文件
  dex文件

反编译常见工具

  javap-java JDK自带
  jad
  CFR
  jd-jui
  jeb
  jadx

reference

.class和.dex文件的区别
class文件
class文件:能够被JVM(JavaVirtualMachine)识别,加载并执行的文件格式

class文件的作用:记录一个类文件的所有信息,记住是所有信息

dex文件
dex文件:能够被DVM或者Art虚拟机执行并且加载的文件格式。

dex文件的作用: 记录整个工程中所有类文件的信息,记住是整个工程

反编译常见工具
javap-java JDK自带
功能:class->java源码
较为轻量。推荐不用,可能少东西….
jad
功能: class->java源码
网址在这:
https://varaneckas.com/jad/
但是有点问题,jad长期不更新,对Java中的新功能支持很差,例如lambda表达式。反编译时,经常会报错误
CFR
功能:class->java源码
比前面两个好,最近更新是2021,还是比较新的,CFR可以编译 Java9,10,12 中的新功能,甚至可以将其他JVM语言的class文件反编译成Java文件
网址:
http://www.benf.org/other/cfr/
jd-jui
功能:class->java源码
图形化界面,可以直接拖入一整个jar包,图形化界面比较好看
网址:
http://java-decompiler.github.io/
jeb
功能:十分丰富,主要用于android逆向
需要自己找破解版(高级一点的)
jadx
功能:和jeb类同,但是是开源的,不如jeb好用,但是开源
网址:https://github.com/skylot/jadx

reference
class文件和dex文件
https://blog.csdn.net/LIZHONGPING00/article/details/103501619

linux内核基础 六 gs/fs寄存器 copy_from/to_user原理

https://wsxk.github.io/linux_kernel_basic_six/

gs/fs寄存器
copy_from_user/copy_to_user底层原理
references

gs/fs寄存器
众所周知,在内核态和用户态之间进行转换时,需要调用swapgs命令来切换gs段寄存器。
用户态还有个奇奇怪怪的fs段寄存器,这俩东西到底有啥用?

用户态使用fs寄存器引用线程的glibc TLS和线程在用户态的stack canary;用户态的glibc不使用gs寄存器;应用可以自行决定是否使用该寄存器(这里存在潜在的、充满想象力的优化空间)。

内核态使用gs寄存器引用percpu变量和进程在内核态的stack canary;内核态不使用fs寄存器。
简单来说 用户态使用fs寄存器来获得线程相关的结构体以及stack canary。内核态使用gs寄存器来获得percpu变量和stack canary。
那有大聪明可能会问了,那为啥还要swapgs呢,gs直接设定成内核态使用不就行了呗
其实还是不行,真设定成这样,这意味着用户态可以根据gs寄存器获得内核信息,这还挺要命的

copy_from_user/copy_to_user底层原理
这个问题的来源在于我知道了smap/smep保护之后,如果开启了这俩保护,copy_from/to_user该如何和用户态进行交互呢?毕竟都禁止了内核态对用户态的访问和执行了.
出于好奇翻阅了一下linux的源码(6.0.5) /linux/uaccess.h 和/include/linux/instrumented.h
copy_from_user(void *to, const void __user *from, unsigned long n)
{
if (check_copy_size(to, n, false))
n = _copy_from_user(to, from, n);
return n;
}
_copy_from_user(void *to, const void __user *from, unsigned long n)
{
unsigned long res = n;
might_fault();
if (!should_fail_usercopy() && likely(access_ok(from, n))) {
instrument_copy_from_user(to, from, n);
res = raw_copy_from_user(to, from, n);
}
if (unlikely(res))
memset(to + (n - res), 0, res);
return res;
}
instrument_copy_from_user(const void *to, const void __user *from, unsigned long n)
{
kasan_check_write(to, n);
kcsan_check_write(to, n);
}

我们已经很接近问题的答案了,问题应该是在raw_copy_from_user身上。
在/arch/alpha/include/asm/uaccess.h找到了该函数
static inline unsigned long
raw_copy_from_user(void *to, const void __user *from, unsigned long len)
{
return __copy_user(to, (__force const void *)from, len);
}

…..到这里还是没有找到。
但是还是从一些blog里得到了相关的信息
在调用底层函数时,copy发生前,通常会使用某些特殊指令关闭相应保护,在copy发生后再重置保护。
比如STAC(Set AC Flag)和CLAC(Clear AC Flag) 就是x64中用来关闭和开启SMAP的汇编指令
references
fs/gs寄存器的作用
https://blog.csdn.net/oqqYuJi12345678/article/details/103568087
https://blog.csdn.net/faxiang1230/article/details/105962793
https://bbs.pediy.com/thread-261744.htm

ubuntu16.04 xinetd安装以及使用方式

https://wsxk.github.io/xinetd%E5%AE%89%E8%A3%85/

xinetd是个非常不错的工具,常用来部署ctf pwn题目

xinetd简介可以看这里

链接

ubuntu16.04 xinetd安装步骤
在命令行中输入以下内容

apt get update
apt get upgrade

以上2条命令主要是用来更新apt

apt install xinetd

安装好了后,输入以下命令(用来开启自动启动xinetd

systemctl enable xinetd.service

用这条命令来检查是否 开机启动xinetd

systemctl is-enabled xinetd

xinetd使用步骤
xinetd主要用 /etc/xinetd.d目录下的配置文件进行配置

/etc/xinetd.d目录在你成功安装xinetd后会自动出现

举个例子,我要在本地启动一个python程序(python程序脚本你想些啥写啥,但是为了测试,可以写的简单点,确保你的python程序没有问题,这样排错方便)

首先我们需要在/etc/xinetd.d下创建一个文件

touch seed_brute_level0
chmod 777 seed_brute_level0

创建后,用vim 对其进行编辑,内容如下

创建完成后,重新启动 xinetd服务

systemctl restart xinetd.service

然后,用nc命令尝试连接

nc 0.0.0.0 10005

可以看到,测试成功

现在大家可以看出,/etc/xinetd.d目录下的文件其实是xinetd管理服务的配置文件。这个配置文件给出了服务的详细信息,方便xinetd管理

生活医学小知识

https://wsxk.github.io/%E6%97%A5%E5%B8%B8%E5%8C%BB%E8%8D%AF1/

湿疹
耳朵痒
脂溢性皮炎

湿疹
湿疹常用药: 炉甘石洗剂(皮肤溃烂时不要用)、糠酸莫米松乳膏

耳朵痒
先试试双氧水(利尔康3%过氧化氢消毒水)
如果还不行就去看看医生

脂溢性皮炎
头部: 采乐(酮康唑洗剂) + 消银颗粒 + 地氯雷他定片(芙必叮) +复合维生素b片(海王福药)

vim常见用法

https://wsxk.github.io/vim/

基于vimtutor编写(方便查)

vim mode
lesson 1 basic
lesson 2 delete and undo
lesson 3 p,replace,ce
lesson 4 jump,serach,substitute
lesson 5 execute external command
lesson 6 open ,copy,paste,R
lesson 7 change window,help

vim mode
vim一共有5种模式(先大概知道一下)

normal mode :  esc   #用于浏览和编辑文件
insert mode :  esc i #对文件插入内容
replace mode:  esc R #取代光标所指向的内容
visual mode:   esc v #复制黏贴用
command-line    esc : #文件保存,退出等操作。

lesson 1 basic

  1. The cursor is moved using either the arrow keys or the hjkl keys.

    h (left) j (down) k (up) l (right)

  2. To start Vim from the shell prompt type: vim FILENAME

  3. To exit Vim type: :q! to trash all changes.

    OR type: :wq to save the changes.

  4. To delete the character at the cursor type: x

  5. To insert or append text type:

    i type inserted text insert before the cursor

    A type appended text append after the line

lesson 2 delete and undo

  1. To delete from the cursor up to the next word type: dw

  2. To delete from the cursor to the end of a line type: d$

  3. To delete a whole line type: dd

  4. To repeat a motion prepend it with a number: 2w

  5. The format for a change command is:
    operator [number] motion
    where:
    operator - is what to do, such as d for delete
    [number] - is an optional count to repeat the motion
    motion - moves over the text to operate on, such as w (word),
    $ (to the end of line), etc.

  6. To move to the start of the line use a zero: 0

  7. To undo previous actions, type: u (lowercase u)
    To undo all the changes on a line, type: U (capital U)
    To undo the undo's, type: CTRL-R

lesson 3 p,replace,ce

  1. To put back text that has just been deleted, type p . This puts the
    deleted text AFTER the cursor (if a line was deleted it will go on the
    line below the cursor).

  2. To replace the character under the cursor, type r and then the
    character you want to have there.

  3. The change operator allows you to change from the cursor to where the
    motion takes you. eg. Type ce to change from the cursor to the end of
    the word, c$ to change to the end of a line.

  4. The format for change is:

    c   [number]   motion
    

lesson 4 jump,serach,substitute

  1. CTRL-G displays your location in the file and the file status.
    G moves to the end of the file.
    number G moves to that line number.
    gg moves to the first line.

  2. Typing / followed by a phrase searches FORWARD for the phrase.
    Typing ? followed by a phrase searches BACKWARD for the phrase.
    After a search type n to find the next occurrence in the same direction
    or N to search in the opposite direction.
    CTRL-O takes you back to older positions, CTRL-I to newer positions.

  3. Typing % while the cursor is on a (,),[,],{, or } goes to its match.

  4. To substitute new for the first old in a line type :s/old/new
    To substitute new for all 'old's on a line type :s/old/new/g
    To substitute phrases between two line #'s type :#,#s/old/new/g
    To substitute all occurrences in the file type :%s/old/new/g
    To ask for confirmation each time add 'c' :%s/old/new/gc

lesson 5 execute external command

  1. :!command executes an external command.

    Some useful examples are:
    (MS-DOS) (Unix)
    :!dir :!ls - shows a directory listing.
    :!del FILENAME :!rm FILENAME - removes file FILENAME.

  2. :w FILENAME writes the current Vim file to disk with name FILENAME.

  3. v motion :w FILENAME saves the Visually selected lines in file
    FILENAME.

  4. :r FILENAME retrieves disk file FILENAME and puts it below the
    cursor position.

  5. :r !dir reads the output of the dir command and puts it below the
    cursor position.

lesson 6 open ,copy,paste,R

  1. Type o to open a line BELOW the cursor and start Insert mode.
    Type O to open a line ABOVE the cursor.

  2. Type a to insert text AFTER the cursor.
    Type A to insert text after the end of the line.

  3. The e command moves to the end of a word.

  4. The y operator yanks (copies) text, p puts (pastes) it.

  5. Typing a capital R enters Replace mode until is pressed.

  6. Typing

linux内核基础 七 userfaultfd

https://wsxk.github.io/linux_base_seven/

userfaultfd简介
userfaultfd流程
userfaultfd样例

  step 1: 创建fd
  step 2:创建一个匿名映射
  step 3: 注册 userfaultfd功能
  step 4: 创建 userfault monitor线程监听
  完整demo

userfaultfd的应用场景

  pre-copy
  post-copy

番外 CRIU(checkpoint/restore in userspace)
userfault的缺陷

  弥补:sysctl_unprivileged_userfaultfd=0

references

userfaultfd简介
一切,都是为了效率
userfaultfd是linux内核为用户态提供的一种机制,其可以让用户态程序来处理原本只能由内核来处理的缺页异常问题。提高了用户编程的灵活性
其实允许用户态处理内核态任务以提高性能的想法越来越普及,比如DPDK(DATA plane development kit)它的注册绕过了linux原有的处理报文的繁琐流程(网卡收到报文发送信号给操作系统、操作系统获取报文进入内核协议栈处理、处理后将报文发送给相应的用户程序),简化为 用户通过设备映射来直接与网卡通信,通过轮询来处理报文(利用mmap和修改系统调用)
https://www.elecfans.com/news/1238266.html
有人可能会问,为什么内核不直接修改原有处理方式呢?就本人思考有以下2个主要原因:

内核已经十分复杂了,直接改太麻烦了
内核需要调度所有的任务,它没有权利给某个任务开小灶(绕过正常流程),这个权力是把握在用户手上的,应该由用户决定 当前任务是否能绕过内核

userfaultfd流程

该流程图的步骤如下:

某个线程(Faulting thread) 读取了一个 mmap 得到的内存,但是内存未初始化,触发pagefault问题,产生信号
该信号被内核转发给了userfaultfd
内核时出问题线程进入休眠,向monitior(实际处理缺页问题的线程)发送消息
monitor监听到了该消息,做出相应处理后,通知userfaultfd
内核唤醒出错线程。

userfaultfd样例
为了加深理解,写了下userfaultfd的demo来尝试。
step 1: 创建fd
long user_fault_fd = syscall(__NR_userfaultfd,O_CLOEXEC|O_NONBLOCK);
if(user_fault_fd<0){
printf(

书籍推荐

https://wsxk.github.io/%E4%B9%A6%E7%B1%8D%E6%8E%A8%E8%8D%90/

这篇文章用于记录我之前看过的书籍。

因为是比较早看的书,大部分内容都忘记了,只记得这本书到底值不值得看,对内容没有做记录,现在回顾一下里面的内容也忘得七七八八了。这样不好

从这个时候开始,我看过的每本书我都会做更细致的评价。

《C primer plus》
《c语言解惑》
《c陷阱与缺陷》
《数据结构与算法分析(c语言描述)》
《离散数学及其应用》
《汇编语言:王爽》
《深入了解计算机系统csapp》
《逆向工程核心原理》
《计算机网络:自顶向下方法》
《图解密码技术》
《ctf竞赛权威指南:pwn篇》
《从0到1:ctfer成长之路》
《ctf特训营》
《鸟哥的linux私房菜》《基础篇》
《社会工程-安全体系中的人性漏洞》
《逆向工程实战》
《Android恶意代码分析与渗透测试》
《物联网设备安全》
《物联网安全》 作者:李善仓,许立达
《物联网安全》 作者:布莱恩·罗素 德鲁·范·杜伦
《编译原理》
《数据库系统概念》
《操作系统导论》
《物联网 So Easy!》
《硬件安全攻防大揭秘》
《图解物联网》
《物联网渗透测试》
《暗网》
《硅谷思维》
《内网渗透体系建设》

《C primer plus》

很不错,c入门可以看

《c语言解惑》

emm感觉看完《C primer plus》后再看这个显得有点没意思。

《c陷阱与缺陷》

看完C primer后去看这个,虽然看懂了,但是理解还不是很深

《数据结构与算法分析(c语言描述)》

学数据结构的时候看了,对着抄了下代码,但是还有一部分没看,讲得太复杂了。

《离散数学及其应用》

一般吧,学着玩(图效率还是听老师讲课比较有效

《汇编语言:王爽》

国内为数不多的讲得比较不错的书籍,虽然讲的是16位汇编

《深入了解计算机系统csapp》

绝世好书,计算机人士必看(

《逆向工程核心原理》

re的很好的书籍,对着全看完了,学到了很多东西,学习逆向可以从这本书看起

《计算机网络:自顶向下方法》

看了一半,之后因为太细节了还有一些实现问题,就没再去看(因为没有地方可以实践)

《图解密码技术》

日本的书,讲得很好,能够对密码学有一个全面的了解和认识,推荐读一读。

《ctf竞赛权威指南:pwn篇》

不推荐一页页读下去,应该刷题的时候对着wp看看是什么知识点再去按目录翻页,哪里不好看哪里

《从0到1:ctfer成长之路》

同上,大概看看即可

《ctf特训营》

同上

《鸟哥的linux私房菜》《基础篇》

国内的不错的书,新手可以耐心读一读,但是我嫌内容太多了就只看了一部分,但是了解linux系统还是很有必要再看看

《社会工程-安全体系中的人性漏洞》

不错的书,看着很有意思,但是没啥实践的机会,就当学习一下防身吧

《逆向工程实战》

学re的不错的书籍,但是有点高深,前置知识有点多,需要慢慢看。

《Android恶意代码分析与渗透测试》

更像是介绍工具的书,介绍怎么用工具进行诊断,原理讲得不多。这种书一般都有时效性,现在已经有许多替代书中介绍工具的更优秀的工具,不推荐阅读。

《物联网设备安全》

不适合新手阅读,讲了很多案例,然而没有基础完全不懂。总之看看了解一下物联网设备就行

《物联网安全》 作者:李善仓,许立达

千万别买!买了就是亏了。看它给我感觉是看一篇水货论文。

《物联网安全》 作者:布莱恩·罗素 德鲁·范·杜伦

讲得不错,不过不适合一开始入门物联网安全的人来看,我认为作为物联网小白要想理解书中的内容或者有所收获,需要一段时间的物联网安全实战基础

《编译原理》

太厚了,没看完(原本想上编译课学习的,奈何教的内容不太一样,咕了),如果不从事这一行,没必要把这本书全弄懂

《数据库系统概念》

没看完(同上)

《操作系统导论》

写得很好,看完了,学到许多知识!!推荐

《物联网 So Easy!》

起初以为挺好的,教你入门玩设备,但是发现资源不全,教程也不全,书中所提到的开发板也没有,属于是被坑了

《硬件安全攻防大揭秘》

可能挺好的书,但是对于没有玩过设备的可能还是难以理解

《图解物联网》
太奇怪了以致于我不知道该说什么。。

《物联网渗透测试》

挺不错的,入门iot安全可以看看,起码把软件装好了(😀

《暗网》

挺不错的,看着可以了解一下论坛和引战方面的知识

《硅谷思维》

有关互联网产品的心理学的知识,说话通俗易懂,很有趣。可以看看

《内网渗透体系建设》

嗯,同样是本工具书,需要配合大量实践才能掌握里面的内容

hackbox pwn racecar wp

https://wsxk.github.io/hackbox-pwn-racecar/?query=race

1.运行
拿到文件,当然是先让他跑起来看看会发生什么

看起来是跑车小游戏

显然选项2才正常开始游戏

接下来让你选择车型,赛道

当我选完1车型 2赛道后,出现了奇怪的东西

打不开flag.txt

有点意思,这时候我们可以创建一个flag.txt然后往里面输点东西

之后出现的情况是下面这样的

2.分析程序

哦吼,看到了printf函数的格式化字符串漏洞

稍微调试一下

然后看下格式化字符串出现的东西

第11个就是我们输入的flag的4个字节啦

3.exp编写
编写exp有些值得质疑的点,比如收到的%x-%x…形式的字符串
需要去首尾空格,然后看看分片

用binascii库的 a2b_hex时,顺序的反的,需要倒过来

from pwn import *
from binascii import a2b_hex

#io = process(

MINI-LCTF2022 kgadget复现

https://wsxk.github.io/MINI-LCTF2022_kgadget/

PS:请观看完linux内核基础 二后练习该题

题目分析
遇到的坑

  1.新的获取vmlinux工具
  2.ROPgadget搜索不全
  3.gdb用法
  4. ret、retf、iretq、iret
  5.使用swapgs_restore_regs_and_return_to_usermode

reference

题目分析
只有kgadget_ioctl有用

但是它的代码其实不太对劲,需要我们自己看汇编

主要思路是,cmd=114514后,可以通过param(v3 rdx)传入的指针获得函数地址,随后调用
可以看到,它对pt_regs的其他值做了限制,导致你只能使用r8 r9两个寄存器
该题目利用了direct mapping of all physical memory的漏洞,通过mmap大量的用户态内存地址,写入同样的payload,在物理内存映射区里选择一个地址,有很大的概率会命中rop_chain,最终完成利用。
详细细节可以看arttnba3的blog

遇到的坑
1.新的获取vmlinux工具
github搜索 vmlinux-to-elf
可以安装该工具,能够成功提取出可供ida分析的内核文件
vmlinux-to-elf input_file output_file

2.ROPgadget搜索不全
github安装ropper
ropper --clear-cache
ropper -f vmlinux --nocolor > gadget.txt

3.gdb用法
gdb vmlinux
add-symbol-file xxx.ko addr -s data addr -s bss addr (addr内核root权限通过 cat /sys/module/xxx.ko/sections/.text .data .bss获得)
target remote :1234
b entry_SYSCALL_64 #可以在执行syscall后进行调试

  1. ret、retf、iretq、iret
    call 对于 ret(pop rip)
    call far 对于 retf(从栈顶弹出 EIP » CS » EFLAGS » ESP » SS)
    iret(4字节) iretq(8字节版本)
    5.使用swapgs_restore_regs_and_return_to_usermode
    因为开启了kpti保护

reference
arttnba3的blog
https://blog.csdn.net/qq_50332504/article/details/124145581

qwb2018 core 复现

https://wsxk.github.io/qwb2018_core/

题目分析

  start.sh文件分析
  init 文件分析
  core.ko分析

利用思路

  1.泄露kalsr
  2.编写payload
    
      如果没有vmlinux怎么办
    
  
  3.利用core_write写入payload
  4.返回用户态执行getshell

exp
待办
reference

这是我入门kernel pwn的第一道题目 哈哈

可以说是经典中的经典了。

题目分析

首先我们可以先解压题目压缩包

可以得到以下4个文件

bzImage,vmlinux,start.sh,core.cpio共4个文件

vmlinux 是静态编译,未经过压缩的 kernel 文件,相对应的 bzImage 可以理解为压缩后的文件

start.sh 文件是一个qemu的运行脚本,core.cpio其实就是一个文件系统。

start.sh文件分析

咱们先来看看 start.sh启动脚本的内容是什么

qemu-system-x86_64
-m 64M \
-kernel ./bzImage \
-initrd ./core.cpio \
-append

RSA原理复习

https://wsxk.github.io/RSA/

基础知识

  1.素数
  2.模运算
  3.互质
  4.欧拉函数
  5.欧拉定理
  6.模反元素

RSA

面试时被问到了RSA的原理和一些基本的攻击手段,之前接触过,但是太久没用的话,只记得大概,大部分都不记得了,所以现在重新回顾一下做个记录。

基础知识
密码学和数论之间存在强相关性,了解RSA之前需要对数论有一些基础的了解。
1.素数
如果一个自然数(大于1)的因数有且仅有 1和它本身,那么这个数被成为素数。
2.模运算
模运算也称为求余运算,即求 一个数除以另一个数的余数
如果两个整数a,b 除以 正整数m 得到的余数相同,则称a和b关于m同余,写作:

$a \equiv b \space mod \space m$

3.互质
若两个正整数a,b 除了1以外,没有其他共因素,则称a和b互质。
4.欧拉函数
给定一个正整数n,求小于等于n的正整数中,与n互质的个数,记为φ(n)

如果n为质数, φ(n) = n-1
如果n为质数p的k次方,φ(n)=p^k - p^(k-1)
如果n可以分解为素数的乘积(例如n=abc),则φ(n)=φ(a)φ(b)φ(c)

5.欧拉定理
如果a和n互质,那么存在以下公式成立:

aφ(n) $ \equiv 1 \space mod \space n$

6.模反元素
如果a和n互质,一定可以找到正整数b,使得以下公式成立:

ab $\equiv 1 \space mod \space n$

RSA
对于RSA加密,步骤如下:

首先你需要生成一个大数n,n=p*q且p,q均为质数。
计算φ(n),$ φ(n)= φ(p)φ(q)=(p-1)(q-1)$
随机选择一个数e,满足 1 < e < φ(n) 且 e与φ(n) 互质
输入明文m,得到密文c,公式:me $\equiv c \space mod \space n$
需要求得e关于φ(n)的模反元素d,即 $ed \equiv 1 \space mod \space φ(n)$
解密时,使如公式:$ c^d = (m^e)^d =$ med = mφ(n)+1 = m

因此,在进行传输时,将(n,e)作为公钥,(n,d)作为私钥。
可以看出,RSA的安全性基于n=p*q的分解难度。如果n=p * q被成功分解,在知道n,p,q,e的情况下,可以得到φ(n),进而可以求出d,使得RSA被攻破。

openssl生成证书并签名

https://wsxk.github.io/openssl%E7%94%9F%E6%88%90%E8%AF%81%E4%B9%A6/

https://www.jianshu.com/p/37ded4da1095 这位大佬的blog写的很好

前言
相关概念

  生成流程

开始之前
openssl生成自签名根证书

  1.生成ca根证书私钥
  2.生成csr
  3.自签名证书

使用ca证书签名 服务器/客户端 证书

  1.生成私钥
  2.生成csr
  3. 使用根证书签发csr得到crt

生成过期证书
reference

前言

之前编写vpn程序时遇到的一个问题是,如何用openssl生成自签名证书并授权(因为使用vpn的隧道需要进行加密通信,这就需要证书了)

这里简单记录一下如何使用openssl生成自签名证书以及服务器,客户端证书

相关概念

公钥加密大家应该不陌生,这里不过多赘述

pem、key:私钥文件,对数据进行加密解密

csr:证书签名请求文件,将其提交给证书颁发机构(ca、CA)对证书签名

crt:由证书颁发机构(ca、CA)签名后的证书或者自签名证书,该证书包含证书持有人的信息、持有人的公钥以及签署者的签名等信息

生成流程

首先需要生成私钥文件(key)

然后生成证书签名请求文件(包含公钥)

最后由证书颁发机构(ca)验证后签名

开始之前

openssl有一系列的配置文件,你为了生成新的证书及相关内容(又不影响到默认的配置文件),你需要进行一些操作

说白了,就是复制一份openssl的结构到你自己创建的目录下。

在你想创建证书的目录下执行以下命令。

mkdir -p ./demoCA/certs
mkdir -p ./demoCA/crl
mkdir ./demoCA/newcerts
mkdir -p ./demoCA/private
touch ./demoCA/index.txt
touch ./demoCA/serial
echo 01 > ./demoCA/serial

cp /usr/local/openssl/ssl/openssl.cnf openssl.cnf

至于为什么要打这些命令

可用通过 cat /usr/local/openssl/ssl/openssl.cnf 命令

查看,openssl.cnf里有注释。

openssl生成自签名根证书

1.生成ca根证书私钥

openssl genrsa -aes256 -out ca.key 2048

genrsa:使用RSA算法生成私钥
-aes256:使用256位密钥的AES算法对私钥进行加密
-out:输出文件路径
2048:私钥长度

输入完命令会让你输入两遍私钥文件的密码(一定要记得,忘了就remake吧哈哈)

2.生成csr

openssl req -new -key ca.key -out ca.csr

req:执行证书签发命令

-new:新的证书签发请求

-key:指定私钥文件的路径

-out:csr文件的输出路径

3.自签名证书

openssl x509 -req -days 365 -signkey ca.key -in ca.csr -out ca.crt

x509:用于自签名证书,生成x509格式的证书(x509告诉openssl使用自签名证书)

-req:请求签名

-days:证书有效期

-signkey:证书签发的私钥

-in:证书请求文件,有效的文件路径

-out:ca签名后的证书输出路径

使用ca证书签名 服务器/客户端 证书

1.生成私钥

openssl genrsa -aes256 -out server.key 2048

2.生成csr

openssl req -new -key server.key -out server.csr

  1. 使用根证书签发csr得到crt

openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config openssl.cnf

-in:输入证书签名请求文件

-out:签名后的证书输出路径

-cert:ca根证书

-keyfile:ca根证书私钥文件

-config:配置文件

生成过期证书

生成过期证书也很容易,我们可以生成一个有效期一天的证书,第二天它就过期了(哈哈

openssl genrsa -aes256 -out server.key 2048
openssl req -new -key server.key -out server.csr
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config openssl.cnf -days 1

reference

https://www.jianshu.com/p/37ded4da1095

http://t.zoukankan.com/qifei-liu-p-9155663.html

go 逆向初探

https://wsxk.github.io/go%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A2/

概述
1.汇编寄存器传参
2.栈帧机制

概述

这里主要讲一下go语言的汇编机制,不定时更新(

主要是因为go它不按常理出牌啊

底层机制弄得和传统不一样,要用起来就很麻烦

1.汇编寄存器传参

和普通的linux64位程序(rdi,rsi,rdx,rcx,r8,r9)不同

举个例子

感觉不仅用到了栈,还用到了寄存器

之后有空再来深究(

2.栈帧机制

go一开始会预留空间作为栈帧,rbp的用处不是很大(可有可无

一般都是去 rsp+xxh+var_xx来作为索引

可以看到 rsp+40h就是栈帧

ctf pwn题部署

https://wsxk.github.io/pwn%E9%A2%98%E7%9B%AE%E9%83%A8%E7%BD%B2/

socat一键部署
pwn_chroot_deploy(用户态题目部署)

  1.修改python3不适配的问题
  2.修改config.py基本配置

内核态pwn题部署
reference

socat一键部署
如果你当前操作的虚拟机or服务器就是可以被连接的目标,那么问题就简单了。

step1 首先在当前操作的机器部署后程序运行环境,并且使用exp能够成功打下。
step2 使用socat命令一键部署。

socat tcp-listen:10001, fork exec: ./pwn_qes, reuseaddr

tcp-listen:10001 开启10001监听端口

fork exec: ./pwn_qes 表明支持多用户访问

reuseaddr 表示如果有一个连接进程出现问题后,监听端口仍然能继续监听。

缺点:不安全,被拿shell了,如果这个人是坏蛋,它就可以任意操作你的机子。

pwn_chroot_deploy(用户态题目部署)
基于giantbranch大佬的开源项目,本人做了一些小修改.
修改后的代码地址如下:https://github.com/wsxk/pwn_deploy_chroot
具体修改如下:

1.修改python3不适配的问题
python2的语法,改成了python3,使得python3也能够成功运行代码文件。

2.修改config.py基本配置
关于原本的替换源,在国内环境可能会出现速度慢、无法访问的问题,这里修改了原本的镜像源为清华源。

PS:如果你换源后,发现仍然无法拉取更新,大概率是你的docker需要重启一下,使用systemctl restart docker重启服务即可(血的教训)

内核态pwn题部署
内核相关的pwn题目部署起来是相当麻烦的,虽然docker也可以完成这个工作,但是还是推荐在服务器上开个虚拟机直接上xinetd(docker毕竟是裁剪过的内核,不是完整的linux内核,出题时容易出现问题)。
还有一些题目需要用到qemu的,也是直接开个虚拟机比较好
目前没有比较好的一键配置脚本,待开发…..

reference
https://tina2114.github.io/2020/03/22/docker%E7%9A%84%E6%90%AD%E5%BB%BA/#more

frida hook

https://wsxk.github.io/frida_hook/

前置条件
安装frida
firda使用

前置条件
有root权限的android手机: https://wsxk.github.io/pixel2xl%E5%88%B7%E6%9C%BA/

安装frida
pip install frida
pip install frida-tools

安装完后,进入frida官网https://github.com/frida/frida/releases下载符合你frida版本的firda server

下载后,将server使用adb push 命令传送到手机上并添加root权限

firda使用
可以使用am start -n com.droidlearn.activity_travel/com.droidlearn.activity_travel.FlagActivity选择要运行的控件类
可以通过Manifest文件获取package名和name

运行脚本如下:
import frida

device = frida.get_usb_device()
session = device.attach(23992) #进程pid
with open(r

angr学习

https://wsxk.github.io/angr/

基础学习
angr 模板
angr存在的问题
capstone
keystone
z3

基础学习
关于angr的底层原理的基本学习,基本语法学习,可以转移至
https://bluesadi.github.io/0x401RevTrain-Tools/
(跟着做大概18道题,就能熟悉angr的基本使用了)
大概了解完之后,应该需要了解符号执行的常用术语

符号状态(symbolic state):{x->$x_{sym}$,y->$y_{sym}$,z->$2*y_{sym}$}
路径约束(path constraint):达到所需路径要满足的条件
遍历策略(通常是广度优先搜索BFS和深度优先搜索DFS)
约束求解(angr的约束求解器是z3的封装)
动态符号执行,也叫混合执行(主要是因为纯静态符号执行变量复杂,无法求解,实用化不高)动态符号执行通过维护实际状态和符号状态两个状态来解决这个问题
路径爆炸:指某种情况下符号执行的路径以指数级增长,这时候符号执行无法求解出正确的答案,解决办法目前有3个: 1. 手动添加约束 2. hook替换导致路径指数增长的位置 3. veritesting路径归并
angr会根据导入表自动识别动态库函数,但是对于静态库函数,angr是无法识别的,需要手动替换像printf之类的函数,否则无法跑出结果。
angr基于IR(Intermediate Representation)来运行,

https://docs.angr.io/?q=
(想做研究还是得看文档,看源码)

angr 模板
根据前面的学习,总结了angr的使用模板
import angr
import claripy

step 1:创建一个project

二进制程序路径

bin_path =

2018-0CTF-final baby 复现(race condition & double fetch)

https://wsxk.github.io/0CTF_FINAL_baby/

题目分析
攻击思路 double fetch
exp
references

题目分析
首先查看一下启动脚本

我们可以惊奇地发现,什么保护都没开(好耶!)
再看看那个驱动的保护:

发现也什么都没开。
接下来分析一下代码:

ioctl函数提供了2个接口:
一个是0x6666,它将会打印出flag的地址(内核)
另一个是0x1337,这个功能首先check用户的输入(input_addr)是否合法。
其中需要重点理解的有_chk_range_not_ok函数:

这个函数首先将第一个参数和第二个参数相加,判断是否发送进位(CF),再判断第一个参数和第二个参数相加是否大于第三个参数
2个条件必须同时满足才能通过检测
这里的第三个参数*(_QWORD *)(__readgsqword((unsigned int)&current_task) + 4952)看起来很复杂,仔细看,首先是__readgsqword((unsigned int)&current_task),从函数名称可以看出这是读取以gs寄存器为基址索引的8个字节,偏移为(unsigned int)&current_task,其实就是读取了current_task在内核中的地址,然后以8字节为单位找到偏移为4952的值
这里值得一提的是,我们还是不知道这个值是多少,需要调试去看一下。
注意,要想调试需要root权限,需要在init脚本里把 1000 改成 0,然后在启动脚本(start.sh)里添加-s选项
这里一般情况是没办法调试的

因为不知为何,地址变成了奇奇怪怪的东西(,这就说明 使用 add-symbol-file添加符号不可行。
因此需要用root权限来 lsmod

可以看到真正的驱动基地址。

顺道一提,我惊奇的发现,基地址刚刚好就是check函数的地址,哈哈,也不用算偏移了。
调试后发现,该值是 0x7ffffffff000

刚刚好是栈底(
从这个题目中,没有栈/堆的问题,似乎只是要我们猜flag

攻击思路 double fetch
我们可以利用 竞态条件(race condition)来绕过检测。
double fetch 就是其中的一种。
double fetch发送在以下条件:

用户往内核传送数据是以指针方式传送(意味着用户可以修改其所指向的内容)
内核需要多次使用该指针来取值。

double fetch直译就是取值2次
这次题目就满足了上述2个条件。
double fetch正常情况图:

attack后:

说白了,就是在一开始输入时,输入的参数是符合规范的,在越过检测后,修改参数为恶意地址
说的容易,其实我们没办法准确定位到第一次fetch和第二次fetch的时间,因此通常都采用爆破的手段。

exp
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>

struct request{
size_t addr;
size_t length;
};
pthread_t compete;
struct request input;
size_t flag_addr;
size_t competition_times = 0x100;
char flag[0x100];
int result_fd;
int success=0;

void * race_thread(void){
while(!success){
for(int i=0;i<competition_times;i++){
input.addr=flag_addr;
}
}
}

int main(){
int fd = open(

fuzz入门

https://wsxk.github.io/fuzz%E5%85%A5%E9%97%A8/

fuzz历史
AFL实践
发现

fuzz历史
fuzz的历史可以看这篇blog
https://riusksk.me/2020/01/22/Fuzzing%E6%8A%80%E6%9C%AF%E5%8F%91%E5%B1%95%E7%9A%84%E8%BF%9930%E5%B9%B4/

AFL实践
https://0xfocu5.github.io/posts/2ba75ee3/
这篇写的不错。

发现
发现无论是angr(符号执行)还是fuzz(模糊测试),他们的底层都是一样的,是静态程序分析。如果有空的话,可以好好学一学静态分析的基础知识。

android 逆向学习

https://wsxk.github.io/android%E9%80%86%E5%90%91%E5%AD%A6%E4%B9%A0/

android逆向是门很深的学问,之前在学习re时有接触过一些,但没有很系统的了解过,刚刚好老师上了门逆向课(它的android的课件有许多对我来说是知识盲区,可以好好了解以下)

APK文件

  APK文件构成
    
      AndroidManifest.xml
      res

android程序的运行机制-虚拟机

  Dalvik虚拟机
  ART(Android RunTime)虚拟机
  ART虚拟机(android 7.0后版本)

APK程序执行流程
smali语言
android运行的架构

  arm
  x86
  mips

android中so的加载流程
android程序加载过程
android动态调试下断点

  雷神模拟器下调试

APK文件

说起android逆向,大概情况就是给你一个APK文件,好,开始分析它吧。

要分析APK文件,我们首先得知道APK文件是什么。

APK(application package)其实是一个压缩包文件,android应用(一般是java写的,现在有转用Kotlin的倾向)在编译完成后,会和所有的资源文件(比如说图片)和数据统一打包成APK。

我们可以用解压缩程序打开他们,但是只能看到其中一部分的内容,另一部分是被加密后的(虽说是加密,其实是可逆的算法,只是一般的解压缩程序没有提供解密功能)

APK文件构成

一般情况下完整的APK是由如下组成的:

AndroidManifest.xml

其中AndroidManifest.xml是比较有用的东西

android应用程序中,用户感知的是一个个的应用界面(在程序中对应的是一个activity类,创建时执行onCreate函数,不可见时执行onStop函数),AndroidManifest.xml中的参数会配置入口的Activity界面

Activity中的application参数配置了程序的入口

在实例中可以看到,入口名称是cn.kwaiching.crackme.CrackMe

这里有对application更详细的描述

res

res目录比较重要的string.xml和public.xml(解密后才能找得到),其实是resources.asrc里的内容

string.xml 存放 实际的字符串和 在 程序中的变量名称 的对应关系

public.xml 存放 程序中的变量名称 和 程序中变量的ID 的对应关系

在实际的反编译代码中,往往是使用ID来代表字符串

android程序的运行机制-虚拟机

为了让android程序崩溃时不会影响到整个系统,使用了虚拟机机制来运行android程序

Dalvik虚拟机

Android4.4之前使用的虚拟机机制

使用JIT(just-in-time)机制,每次执行应用时,将程序的代码(dex码)翻译为机器语言(机器码)执行,程序运行同样的逻辑时,速度会更快

基于寄存器

缺陷:每次程序重新启动,就要重来一遍编译过程,浪费资源

ART(Android RunTime)虚拟机

采用AOT(Ahead-Of-Time)技术

在安装apk程序时启动dex2oat过程把dex预编译为ELF文件,每次运行程序不用重新编译

内存管理也有了较大的改进

缺陷:

应用安装和升级会比较耗时(需要重新编译ELF文件)

优化后的文件占用额外存储空间

ART虚拟机(android 7.0后版本)
Android 7.0后,又重新加入了JIT技术,采用AOT/JIT混合的策略

特点如下:

APK程序执行流程

smali语言

对Dalvik字节码的翻译,这里不详细讲它的语法规则了
smali也算是一种高级语言了。
话说其实现在逆向一般也看smali转换后的java源码(

android运行的架构

arm

arm指令集有一个子集,THUMB指令集

x86

android 1.6开始提供了x86的支持

mips

android 4.1提供了mips支持

android中so的加载流程

.init–>.init array–>JNI_Onload–>java_com_XXX

在脱壳的过程中有时候会在一些系统级的.so中下断点比如:fopen, fget, dvmdexfileopen,等等

而.init以及.init_array一般会作为壳的入口地方,称它为外壳级的.so文件

归纳为三类:

应用级别的:java_com_XXX;


外壳级别的:JNI_Onload, .init, .init_array;


系统级别的:fopen,fget,dvmdexfileopen;

android程序加载过程

首先是“init_array”,Android系统在加载App时,通过系统的 linker程序先加载这个函数,对App进行初始化

然后再调用“JNI_OnLoad”

android动态调试下断点

这个我实际只在夜神模拟器的x86上有试验过,经验还是太少了,毕竟android大多是还是arm程序,等有空实操后再书写如何下断点吧

雷神模拟器下调试

缺陷:只能调试x86的程序,arm经常出问题,碰到反调试一般用不了(

步骤

adb push debug_server /local/data/tmp/debug_server # debug_server指代IDA文件夹中的对应的执行文件
adb shell #进入模拟器shell
su
chmod 777 /local/data/tmp/debug_server #给予权限
./local/data/tmp/debug_server

#接下来再开一个cmd
adb forward tcp:23946 tcp:23946 #(端口转发,调试手 机上的某个进程要有协议支持通信)

#ida的debuger attach附加即可

rust逆向初探

https://wsxk.github.io/Rust%E9%80%86%E5%90%91%E5%88%9D%E6%8E%A2/

简述
之前做ctf的时候有碰到关于rust的逆向题目,但是看都看不懂。

rust被称作“最安全的编程语言”,名不虚传

这篇文章主要讲解的是怎么样来进行rust逆向入门

编写第一个rust程序
俗话说 知己知彼,百战不殆

要想逆向rust,你首先得会写一点点rust!

rust在windows上的安装比较简单,前提是你已经装好了VS(因为rust进行编译时会使用到VS编写c/c++程序的一些工具),换句话说,你装好了VS并能在VS上写一个简单的c/c++程序出来(hello world),你就能安装rust并使用了。

安装教程

安装好rust后,我们尝试编写一个简单的代码

fn main() {
println!(

Intermediate Representation

https://wsxk.github.io/%E9%9D%99%E6%80%81%E5%88%86%E6%9E%90IR/

1.compiler和static analysis之间的关系
2. AST和IR的差异
3. IR
4. Static Single Assignment(SSA)

  SSA的优势与劣势
  1. BB(basic block)

    生成BB的算法

  2. CFG(Control Flow Graph)

    生成CFG的算法

1.compiler和static analysis之间的关系
我直接用一张图表示!

这里可以看到,在源代码编译成机器可执行的程序之中,经历了很多的步骤,像Scanner(词法分析)、Parser(语法分析)、Type checker(语义分析),得到一个抽象语法树(AST),然后经过 Translator转换成汇编代码后,交给Code Generator生成二进制可执行程序

但是其实,如果Translator的任务不是生成汇编代码,而是生成IR,从这里就可以开始做静态分析的应用了。

  1. AST和IR的差异

AST就像图中左边的这种形式,IR就是右边下面的形如
1: i=i+1
2: t1 = a[i]
3: if t1 < v goto 1

的类似代码的东西。
其实这么一看差异还是比较明显的,接下来仔细说明AST和IR之间的关系。
AST:
接近语言本身的语法结构
依赖语言
比较适合做快速的类型检测
缺少控制流

IR:
比较低级,接近机器码的执行流程
语言无关
形式统一
保护控制信息

  1. IR
    IR(intermediate representation)是静态分析中很重要的概念。
    先前也稍微提到了IR的形式,它是静态分析的主要载体,大部分静态分析工具,都是在IR的基础上进行分析的。
    其实IR就是一种 3-Address Code(3AC) 三地址码
    它的特征也比较明显:
    a+b+3 转换成 IR后

t1 = a+b
t2 = t1+3

像这种有3个操作数的,就是三地址码了,其实2个操作数也是合理的。

  1. Static Single Assignment(SSA)
    SSA是不同于3AC的表现形式,具体体现在:

    对于每个定义,都必须有自己的名字
    每个变量只有一个定义

给个3AC和SSA的图示。

可以看到,每个步骤所得到的结果,都需要有一个变量标识,每个定义也需要有一个变量标识,且变量随后不能再被使用。这种形式当然是有问题的,比如:

像这种条件分支,因为会产生2个变量x0 和 x1,但是其实到达下一步后,程序无法得知到底是哪个值被用到了,如果就2个还可以通过遍历,但是如果分支多起来就很麻烦了。
于是SSA有了一种叫做special merge operator的操作符,用来merge变量的。

SSA的优势与劣势
优势:

flow-insensitive, 速度快,精度低
Define-and-Use pairs are explicit,就是定义使用对很明显很简介

劣势:

变量太多了
无法应对大型的copy操作
  1. BB(basic block)
    BB(基本块)是构成控制流图的基本单位,BB由 若干条IR组成,BB的具体定义就两条:
    入口唯一、出口唯一
    生成BB的算法

可以看到,其实只需要找, 一段IR中的 第一条指令、jump指令的目标、跳转指令后的那条指令 ,都是一个BB的入口,可以通过找入口来生成BBs
举个例子:

  1. CFG(Control Flow Graph)
    由一系列BBS构成的图,就叫做控制流图(IDA中的那个,想必大家用过都知道)

生成CFG的算法
三步走:

BB是CFG的节点,首先需要生成BBS
画边
  
    A的末尾和B的开头之间有跳转指令,连边

B直接跟在A后面且A不以无条件跳转指令结束

替换标签(比如跳转到第几条指令变为 跳转到块几)

ECDHE算法学习

https://wsxk.github.io/ECDHE/

离散对数
DH(Diffie Hellman)
DHE(Diffie Hellman Ephemeral)
ECDHE
reference

离散对数
如果对于一个整数b和质数p的一个原根a,可以找到一个唯一的指数i,使得:
(a^i mod(p) =b 成立)
那么 指数i 称为 b 的 以a为底 模p的 离散对数。
a 和 p都是公开的参数
当p是一个很大的质数,当你知道i的值时,b的值很容易就能算出来,但是如果仅知道b的值,你是很难算出i的值的(你需要从1开始爆破,直到上述式子成立)

DH(Diffie Hellman)
DH协议是一种密钥交换协议
它的目的是为了让双方在被窃听的信道内也能安全的交换密钥
举个例子,小明和小红使用DH算法来交换密钥

小明和小红确定 底数g 和 模数p,根据离散对数原理,g和p是公开的
小明和小红各自随便生成了一个整数作为私钥,分别为a和b
小明将A=$g^a mod (p)$发送给小红,小红将B=$g^bmod(p)$发送给小明
小明根据B和a计算$B^amod (p)$,小红根据A和b计算$A^bmod (p)$

值得注意的是$B^a mod (p) = (g^b mod(p))^a mod (p) = (g^a)^b mod (p) = A^b mod(p) $
因此,$K=B^a mod(p) = A^b mod(p)$就是共享的会话密钥了。
黑客只能知道 g p A B 想要计算a和b是困难的(基于离散对数)
DHE(Diffie Hellman Ephemeral)
DHE其实是DH的改进版本。
主要是每重新进行一次通信,就会重新生成服务端和客户端是公私钥。
这样即使你破解了这个通信,你之前的通信也要再爆破一次
这被称作 前向安全(即该次通信密钥被破解,不会影响到之前通信的通信)。
ECDHE
DHE运算性能不佳,需要进行大量乘法计算。此时诞生了效率更高的ECDHE算法

小明和小红确定使用的椭圆曲线并确定公开基点G
小明和小红各自随机生成一个私钥d1 和 d2
小明和小红各自计算自己的公钥$D1=d1G$ 和 $D2=d2G$
小明和小红将各自的公钥发送给对方
小明计算 $d1D2=d1d2G=d1Gd2=D1d2=d2D1$

这个过程中,双方的私钥都是随机、临时生成的,都是不公开的,即使根据公开的信息(椭圆曲线、公钥、基点 G)也是很难计算出椭圆曲线上的离散对数(私钥)。

reference
https://blog.csdn.net/m0_50180963/article/details/113061162

iot安全 一 背景知识

https://wsxk.github.io/iot%E5%85%A5%E9%97%A8%E4%B8%80/

作为一个爱好广泛的安全研究员,研究研究iot安全也是必须的

iot与传统pc软件的区别

  iot安全研究的困难

iot设备组成

  MMIO(Memory-Mapped Input/Output)
  PMIO(Port-Mapped Input/Output)
  IRQs(Interrupt Requests)
  DMA(Direct Memory Access)

iot固件类型分类
iot面临的风险攻击

  硬件层面
  系统层面
  应用软件层面
  网络服务层面

iot分析一般步骤

  设备固件获取
  固件自动化解析
  设备固件安全分析技术的研究
    
      静态分析
      动态分析

iot与传统pc软件的区别
iot,可以说是嵌入式别名
研究iot安全,通常研究的是运行在嵌入式设备上的软件(俗称固件)上的安全。
iot和传统pc软件的区别主要在于以下3点:

嵌入式系统固件通常情况下直接与硬件底层进行交互来执行任务(一般情况下,嵌入式设备的硬件资源实在有限,不足以支持一个操作系统的运行)
固件通常存储在嵌入式设备的ROM(read-only memory)或非易事内存芯片(比如flash内存和 EEPROM(Electrically Erasible Programmable Read-Only Memory)中
固件格式复杂多样,没有统一标准(因为过于底层,以致于程序运行的入口点在那个内存地址都是由嵌入式开发人员随意决定的),且通常一个固件就包含了保证设备运行的所有内容。

iot安全研究的困难
其实从这些区别就可以看到一些iot研究的困难了。

底层硬件平台复杂,结构不同(因为iot固件直接和硬件进行交互,不同的产商diy设计自己的硬件产品,直接导致固件设计产生巨大差异,没有统一标准)
专用性强,源码/文档不公开(懂得都懂,这要是公开了才怪了)
运行环境受限(一般情况下只有自家硬件才能运行得起来自家固件,然而一般厂商硬件资源有限,跑fuzz都嫌慢;所以学术界很推崇用模拟方式来模拟固件运行,用服务器的资源来跑fuzz,然而因为硬件不行,模拟起来也很费劲)
iot固件与通用pc程序不同,文件结构上有差异(包括不同固件入口点都随开发者开心,有用ida分析固件的人可以发现,有些固件,甚至ida都没办法辨别其型号和入口点,需要你自己选)

iot设备组成
直接上图:

IOT设备至少由一个cpu(执行固件)和一组外设组成。外设负责进行交互。
外设的种类丰富多样,大伙看一下有个印象就行。
至于片上外设和片外外设 大家可以想象一下自己的电脑,首先电脑有个耳机插口,这个就叫做片上外设,那么耳机就叫做片外外设.大概就是这么个关系。所以才有 CPU只能通过片上外设和片外外设进行交互的说法。
CPU与外设交互的方法种类多种多样:

MMIO(Memory-Mapped Input/Output)
外设的硬件寄存器直接映射到CPU可访问的内存空间中。
PMIO(Port-Mapped Input/Output)
用一些ISAs(Industrial Standard Architecture)引入特殊指令来与外设进行通信。
比如x86-64 的 in/out指令。
外设寄存器没有映射到主存上,通过使用指令查询端口进行访问
IRQs(Interrupt Requests)
外设发起中断信号,通知cpu发生事件。
其概念和OS接收到中断后保存上下文转去处理中断事件一致。
DMA(Direct Memory Access)
允许外设独立于CPU在其他外设和主存直接传输数据。
就是你CPU在干某件事情的同时,使用DMA通信的设备可以操作主存或和其他外设交互。并发!

iot固件类型分类
分成三种型号。

I型 基于通用目的操作系统的设备。通常带有linux内核,有轻量的用户空间环境(busybox、uClibc),与硬件交互通常经过驱动程序。
II型 基于嵌入式操作系统的设备(该操作系统比较低级,没有内存管理单元等高处理器特性,当然主要原因是硬件资源不足 直接上大的操作系统跑不起来),但是仍然存在用户态和内核态的隔离,比如VxWorks、ZephyrOS。常常用于单用途的电子设备,比如DVD播放器和LTE调整解调器。
III型 不具备操作系统抽象的设备,采用所谓”monolithic firmware”,一个固件直接负责和硬件交互,执行cpu运行逻辑。其操作通常采用一个控制循环+外设触发中断以处理外部事件 , 固件可以完全定制。

iot面临的风险攻击

硬件层面

调试接口攻击。 固件开发肯定是需要调试的,如果设备发布后,员工忘记封闭接口了,攻击人员可以直接通过接口来获得高权限root
flash芯片的攻击,flash芯片通常存了固件,如果芯片没有设置读写保护,可以直接读取固件并修改固件来绕过认证。
测信道分析攻击, 采用设备的硬件特征泄露来提取被处理数据的信息,并利用这些信息推断出设备上的敏感信息。
节点复制攻击 相当于中间人攻击

系统层面

固件修改攻击 顾名思义
固件恶意软件 也顾名思义
操作系统层面 指一般嵌入式的内核班长都比较低级,可以通过搜索cve 搜索已知exp实现快速进攻。

应用软件层面

后门 不用多说了
第三方代码库复用 懂得都懂
厂商定制代码 为了适配不同的架构,厂商需要对不同的架构做大量的开发工作(适配第三方库,厂商自主开发模块 etc) 开发人员安全意识不足,导致代码设计的安全缺陷

网络服务层面

远程管理接口不安全 弱口令,密码可以被爆破 etc
外设协议(针对协议比如 ZigBee 发起攻击)
通信协议方面 DDos攻击等等
暴力搜索攻击 端口扫描+爆破
phantom设备攻击 中间人攻击(不知道为什么要起奇奇怪怪的名字)

iot分析一般步骤

设备固件获取
一般可以通过 UART(Universal Asynchronous Receiver/Transmitter) 或 JTAG(Joint Test Action Group)接口读取固件,或者也可以去官网查找固件版本。还可以用中间人攻击截获更新固件的流程来获得固件。

固件自动化解析
当前,基本上只能模仿I型固件,II型固件和III型固件比较难用。

设备固件安全分析技术的研究
安全分析技术,就是在拥有固件且可以模拟运行的情况下,实行的用于自动化挖漏洞的方法。
静态分析
静态分析是当前非常流行(2022-11-15记录)的技术,其主要**是模拟人看代码的过程,分析程序运行的流程,形成一个结果(比如cfg 交叉引用 函数调用图 等等)
在此基础上,出现了符号执行(虚拟机模拟执行程序流程,用z3引擎求解到达路径的输入) 和 fuzz(通过一个种子生成随机输入,喂到程序中,fuzz会记录引发crash的输入) 等当下十分流行的技术。
值得一提的是,当前学术界内,关于 静态分析、符号执行、fuzz的 iot安全相关的论文 数量是非常多的(懂得都懂),但是实际上有没有用,我也不知道。

动态分析
动态分析一般都是人手动对设备进行调试。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.