突然对 BIOS 起了兴趣

这篇文章想探究一下 CPU 上电后的 BIOS 执行过程。

CPU 有一个 RESET 引脚,电脑上电后该引脚给信号后(要等供电等稳定才能给信号),CPU 才开始运行,CPU 从固定内存地址读取指令并执行,而这个固定的内存地址是映射到 BIOS 芯片的,因此,RESET 后,CPU 是直接从 BIOS 的 ROM 中读取指令并执行的,一般 BIOS 程序最开始会先初始化内存控制器(然后才可以用内存),然后将 ROM 后面压缩的 BIOS 代码解压到内存中,再 JMP 到内存去运行解压后的 BIOS 程序,这么做的原因是直接从 ROM 读取指令太慢。

80386 及以后的 x86 处理器是从地址 0xfffffff0 开始运行的,这个地址一般叫 reset vector,相关资料可以查阅 Intel 的软件开发手册第三卷 9.1.4 节。

以前误以为上电后是由主板的某电路将 BIOS 的 ROM 内容先载入到内存,然后 CPU 再从内存开始读取指令并执行的。实际上并不是这样,即使是内存,它的内存控制器也是需要初始化才能用的,上电后要初始化内存控制器后才能用。初始化内存控制器是 BIOS 程序做的。

既然第一条指令是从 0xfffffff0 开始执行,那就去这个地方把内存 dump 下来分析,查看内存布局 cat /proc/iomem 发现,整个 0xff000000 - 0xffffffff 是一个整体,干脆把这个 16M 的内存块都 dump 下来了:
dd if=/dev/mem of=ffmem.dump bs=16M ibs=16M skip=255 count=1

dump 下来后用神器 radare2 分析,由于 CPU 启动时处于「真实地址模式」,指令操作数以及地址默认都是 16 位的(可以用指令前缀明确指示该指令用 32 位地址),所以 radare2 需要用参数 -b 16 运行,为了分析指令时更方便,还需要将基地址设定为 0xff000000:
r2 -b 16 -m0xff000000 ffmem.dump

[ff00000:0000]> s 0xfffffff0
[ffff000:fff0]> pD 16
        ╎   f000:fff0      90             nop
        ╎   f000:fff1      90             nop
        └─< f000:fff2      e923f9         jmp 0xf918

插一嘴 jmp 地址的计算方法:这是一条段内跳转地址,下一条指令段内地址 0xfff5 + 跳转指令操作数 0xf923 = 0x1f918,超过段内地址位数的部分抹去得到 0xf918,即段内(f000)0xf918 的地址。

由于我们指定了 16 位模式,所以段基地址被截断成了 f000,实际上我们应该在段 ffff000 内。

关于为什么 CPU 启动时是「真实地址模式」,但却从 0xfffffff0 开始执行,原因是这样的:

我们知道 CS 寄存器是 16 位的,其实那只是暴露出来的部分,CS 还有一个隐藏的部分,长 32 位,用来放段基地址的,什么?段基地址不是用 CS 左移 4 位计算出来的么?是的!段基地址是计算出来的,计算出来后就放到隐藏的那部分了,然后算地址的时候直接从隐藏部分取出地址来加上偏移就是目标地址了,所以其实寻址跟 CS 那 16 位是个间接的关系,与隐藏的 32 位的部分才是直接的关系。而 CPU 上电 RESET 后,CS 的初始值为0xf000,然而那 32 位也有初始值 0xffff0000,于是寻址时直接用这个段基地址去计算目的地址了,只要不去写 CS 寄存器,段基地址就一直会是 0xffff0000。IP 寄存器的初始值是 0xfff0,于是 CPU 的第一条指令地址就是 0xffff0000 + 0xfff0 = 0xfffffff0了。

这里还有一个问题,8086 CPU 寻址时,算出来的地址如果超过 20 位,默认 20 位以上会被 mask 掉,这样,就只能访问 1M 以下的内存了,有一个 A20 线可以控制这个行为,现在的 CPU 在「真实地址模式」下不会 mask,所以在这个模式下访问 1M 以上的内存才成为可能。

接着分析汇编代码,去 jmp 的地方看看:

[ffff000:fff0]> s 0xfffff918
[ffff000:f918]> pd 64
       ╎    f000:f918      dbe3           fninit // 初始化浮点单元,应该是用来判断 CPU 的,不支持这个指令的就是老 CPU
       ╎    f000:f91a      0f6ec0         movd mm0, eax
       ╎    f000:f91d      fa             cli
       ╎    f000:f91e      b800f0         mov ax, 0xf000
       ╎    f000:f921      8ed8           mov ds, ax
       ╎    f000:f923      bef0ff         mov si, 0xfff0
       ╎    f000:f926      803cea         cmp byte [si], 0xea  // 检查 0xffff0 处值是否为 0xea
       ╎┌─< f000:f929      7505           jne 0xfffff930
       └──< f000:f92b      ea5be000f0     ljmp 0xf000:0xe05b // 检查通过跳转到 legacy BIOS 区
        └─> f000:f930      66bbc4faffff   mov ebx, 0xfffffac4  // 不为 ea 会跳到这里,怀疑这个 ea 标至是用来区别 new BIOS 和 legacy BIOS 的
            f000:f936      662e0f0117     lgdt cs:[bx]
            f000:f93b      0f20c0         mov eax, cr0
            f000:f93e      0c01           or al, 1
            f000:f940      0f22c0         mov cr0, eax
            f000:f943      fc             cld
            f000:f944      b80800         mov ax, 8
            f000:f947      8ed8           mov ds, ax
            f000:f949      8ec0           mov es, ax
            f000:f94b      8ed0           mov ss, ax
            f000:f94d      8ee0           mov fs, ax
            f000:f94f      8ee8           mov gs, ax
        ┌─< f000:f951      66ea59f9ffff.  ljmp 0x10:0xfffff959

0xffff0 是哪里呢?就是 BIOS 程序,BIOS 的 128K 程序会被映射到内存地址空间 0xe0000 - 0xfffff,注意,这 128K 不会载入到内存,此时,内存控制器甚至还未初始化,内存根本不能访问,这 128K 的地址是直接电路到 BIOS 的 ROM,CPU 此时取数或者取指令都是直接从 ROM 取的,所以会比较慢。

把 BIOS 程序 dump 下来:
dd if=/dev/mem of=bios.rom bs=64k ibs=64k skip=14 count=2

然后看下 0xffff0 的值:r2 -b 16 bios.rom

[f000:fff0]> px 1
- offset -  0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
f000:fff0  ea                                       .

果然是 0xea!

好,检查通过后会执行 ljmp 0xf000:0xe05b,这里要注意了,这是个长跳转指令,会载入 CS 寄存器,因此,段基地址会变成 0xf000,所以这条指令跳转到了 BIOS 程序中,地址为 0xfe05b,接着分析 bios.rom:

[f000:fff0]> s 0xfe05b
[f000:e05b]> pd 1
        └─< f000:e05b      e9e259         jmp 0x3a40

开头就是一个 jmp

[f000:e05b]> s 0xf3a40
[f000:3a40]> pd 100
            f000:3a40      fa             cli
            f000:3a41      fc             cld
            f000:3a42      0f01e0         smsw ax // 取 cr0 低 16 位
            f000:3a45      a801           test al, 1 // 测试是否开启保护模式
        ┌─< f000:3a47      7504           jne 0xf3a4d // 开启则跳转
        │   f000:3a49      8cc8           mov ax, cs
        │   f000:3a4b      8ed0           mov ss, ax // 栈跟代码用一个段
       ┌└─> f000:3a4d      e90000         jmp 0xf3a50
       └┌─< f000:3a50      e99201         jmp 0xf3be5 // 从这里跳走
        │   f000:3a53      b08f           mov al, 0x8f // 关 NMI
        │   f000:3a55      e670           out 0x70, al
       ┌──< f000:3a57      e300           jcxz 0xf3a59
      ┌└──> f000:3a59      e300           jcxz 0xf3a5b
      └┌──< f000:3a5b      e300           jcxz 0xf3a5d
      ┌└──> f000:3a5d      e300           jcxz 0xf3a5f
      └───> f000:3a5f      e471           in al, 0x71 // 读 CMOS Shutdown Status,这个值应该是上一次关机时设置的,相当于一个 checkpoint
        │   f000:3a61      b400           mov ah, 0
        │   f000:3a63      8bf0           mov si, ax
        │   f000:3a65      b08f           mov al, 0x8f
       ┌──< f000:3a67      e300           jcxz 0xf3a69
      ┌└──> f000:3a69      e300           jcxz 0xf3a6b
      └───> f000:3a6b      e670           out 0x70, al
        │   f000:3a6d      b000           mov al, 0
       ┌──< f000:3a6f      e300           jcxz 0xf3a71
      ┌└──> f000:3a71      e300           jcxz 0xf3a73
      └┌──< f000:3a73      e300           jcxz 0xf3a75
      ┌└──> f000:3a75      e300           jcxz 0xf3a77
      └───> f000:3a77      e671           out 0x71, al // 写 CMOS Shutdown Status,写了个0(Power on or soft reset)进去
        │   f000:3a79      8cc8           mov ax, cs
        │   f000:3a7b      8ed0           mov ss, ax
        │   f000:3a7d      8bc6           mov ax, si
        │   f000:3a7f      3c04           cmp al, 4                    ; 4
       ┌──< f000:3a81      740a           je 0xf3a8d
       ││   f000:3a83      3c05           cmp al, 5                    ; 5
      ┌───< f000:3a85      7406           je 0xf3a8d
      │││   f000:3a87      3c0a           cmp al, 0xa                  ; 10
     ┌────< f000:3a89      760d           jbe 0xf3a98
    ┌─────< f000:3a8b      eb43           jmp 0xf3ad0
    ││└└──> f000:3a8d      bb7008         mov bx, 0x870
    ││  │   f000:3a90      bc963a         mov sp, 0x3a96 // 内存不可用,模拟call指令,该地址的值是 0x3a98
    ││ ┌──< f000:3a93      e99a00         jmp 0xf3b30 // 跳转到硬件初始化,随后跳到3a9b,si=4|5
        ┌─< f000:3a98      e95d01         jmp 0xf3bf8 // 跳转到 3a9b,si<=a && si != 4 && si !=5
        │   f000:3a9b      b84000         mov ax, 0x40
        │   f000:3a9e      8ed8           mov ds, ax // ds = 0x40
        │   f000:3aa0      33c0           xor ax, ax
        │   f000:3aa2      8ec0           mov es, ax // es = 0
        │   f000:3aa4      b030           mov al, 0x30
        │   f000:3aa6      8ed0           mov ss, ax // ss = 0x30
        │   f000:3aa8      bc0001         mov sp, 0x100
        │   f000:3aab      d1e6           shl si, 1
        │   f000:3aad      2effa4b23a     jmp word cs:[si + 0x3ab2]

[f000:3ab2]> pxh
f000:3ab2  0x3ad0 0x3e7a 0xeffb 0x3ac8 0xf001 0xf009 0x3aca 0x3bf2  .:z>...:.....:.;
f000:3ac2  0x3bf5 0x3eaf 0xf011
// 这是从 si = 0 到 si = a 的调用地址
si = 0, jmp 0x3ad0 // Power on or soft reset,没走通
si = 1, jmp 0x3e7a // Memory size pass,有可能是这个
si = 2, jmp 0xeffb // Memory test pass
si = 3, jmp 0x3ac8 // Memory test fail
si = 4, jmp 0xf001 // POST complete; boot system,没走通
si = 5, jmp 0xf009 // JMP double word pointer with EOI
si = 6, jmp 0x3aca // Protected mode tests pass
si = 7, jmp 0x3bf2 // protected mode tests fail
si = 8, jmp 0x3bf5 // Memory size fail
si = 9, jmp 0x3eaf // Int 15h block move
si = a, jmp 0xf011 // JMP double word pointer without EOI
si = b, jmp 0x3ad0 // Used by 80386,没走通

      ┌───< f000:3ac8      eb06           jmp 0xf3ad0
     ┌────< f000:3aca      eb04           jmp 0xf3ad0
    ┌─────< f000:3acc      eb02           jmp 0xf3ad0
   ┌──────< f000:3ace      eb00           jmp 0xf3ad0
   └└└└───> f000:3ad0      b83000         mov ax, 0x30 // si=0,跳到这里
       ││   f000:3ad3      8ed0           mov ss, ax
       ││   f000:3ad5      bc0001         mov sp, 0x100
       ││   f000:3ad8      b002           mov al, 2
       ││   f000:3ada      e680           out 0x80, al // 写了个 2 到 debug
       ││   f000:3adc      e87407         call 0xf4253
       ───> f000:3adf      ebfe           jmp 0xf3adf // 死循环,所以上面函数调用后不应返回

            f000:3b30      b011           mov al, 0x11                 ; 17
            f000:3b32      e6a0           out 0xa0, al
            f000:3b34      50             push ax
            f000:3b35      e461           in al, 0x61
            f000:3b37      58             pop ax
            f000:3b38      50             push ax
            f000:3b39      e461           in al, 0x61
            f000:3b3b      58             pop ax
            f000:3b3c      e620           out 0x20, al
            f000:3b3e      50             push ax
            f000:3b3f      e461           in al, 0x61
            f000:3b41      58             pop ax
            f000:3b42      50             push ax
            f000:3b43      e461           in al, 0x61
            f000:3b45      58             pop ax
            f000:3b46      8ac3           mov al, bl
            f000:3b48      e6a1           out 0xa1, al
            f000:3b4a      50             push ax
            f000:3b4b      e461           in al, 0x61
            f000:3b4d      58             pop ax
            f000:3b4e      50             push ax
            f000:3b4f      e461           in al, 0x61
            f000:3b51      58             pop ax
            f000:3b52      8ac7           mov al, bh
            f000:3b54      e621           out 0x21, al
            f000:3b56      50             push ax
            f000:3b57      e461           in al, 0x61
            f000:3b59      58             pop ax
            f000:3b5a      50             push ax
            f000:3b5b      e461           in al, 0x61
            f000:3b5d      58             pop ax
            f000:3b5e      b002           mov al, 2
            f000:3b60      e6a1           out 0xa1, al
            f000:3b62      50             push ax
            f000:3b63      e461           in al, 0x61
            f000:3b65      58             pop ax
            f000:3b66      50             push ax
            f000:3b67      e461           in al, 0x61
            f000:3b69      58             pop ax
            f000:3b6a      b004           mov al, 4
            f000:3b6c      e621           out 0x21, al
            f000:3b6e      50             push ax
            f000:3b6f      e461           in al, 0x61
            f000:3b71      58             pop ax
            f000:3b72      50             push ax
            f000:3b73      e461           in al, 0x61
            f000:3b75      58             pop ax
            f000:3b76      b001           mov al, 1
            f000:3b78      e6a1           out 0xa1, al
            f000:3b7a      50             push ax
            f000:3b7b      e461           in al, 0x61
            f000:3b7d      58             pop ax
            f000:3b7e      50             push ax
            f000:3b7f      e461           in al, 0x61
            f000:3b81      58             pop ax
            f000:3b82      e621           out 0x21, al
            f000:3b84      50             push ax
            f000:3b85      e461           in al, 0x61
            f000:3b87      58             pop ax
            f000:3b88      50             push ax
            f000:3b89      e461           in al, 0x61
            f000:3b8b      58             pop ax
            f000:3b8c      b0ff           mov al, 0xff
            f000:3b8e      e6a1           out 0xa1, al
            f000:3b90      50             push ax
            f000:3b91      e461           in al, 0x61
            f000:3b93      58             pop ax
            f000:3b94      50             push ax
            f000:3b95      e461           in al, 0x61
            f000:3b97      58             pop ax
            f000:3b98      e621           out 0x21, al
            f000:3b9a      c3             ret

     ╎╎╎╎   f000:3be5      0f01e0         smsw ax
     ╎╎╎╎   f000:3be8      a801           test al, 1                   ; 1
    ┌─────< f000:3bea      7403           je 0xf3bef
   ┌──────< f000:3bec      e96406         jmp 0xf4253
   │└───└─< f000:3bef      e961fe         jmp 0xf3a53
   │ ╎└───< f000:3bf2      e9d7fe         jmp 0xf3acc
   │ └────< f000:3bf5      e9d6fe         jmp 0xf3ace
   │   └──< f000:3bf8      e9a0fe         jmp 0xf3a9b

[f000:3ac6]> s 0xf4253
[f000:4253]> pd100
            f000:4253      6a01           push 1                       ; 1
            f000:4255      e8cdc1         call 0xf0425
            f000:4258      baf90c         mov dx, 0xcf9                ; 3321
            f000:425b      ec             in al, dx
            f000:425c      0c02           or al, 2
            f000:425e      ee             out dx, al
            f000:425f      50             push ax
            f000:4260      e461           in al, 0x61
            f000:4262      58             pop ax
            f000:4263      0c04           or al, 4
            f000:4265      ee             out dx, al
        ┌─> f000:4266      f4             hlt
        └─< f000:4267      ebfd           jmp 0xf4266 // 这里死循环停机,所以,0xf0425 如果返回的话,肯定不对

            f000:0425      55             push bp
            f000:0426      8bec           mov bp, sp
            f000:0428      50             push ax
            f000:0429      56             push si
            f000:042a      1e             push ds
            f000:042b      9c             pushf
            f000:042c      be1000         mov si, 0x10                 ; 16
            f000:042f      2e837c4200     cmp word cs:[si + 0x42], 0 // 0xf0052 处为 e000
        ┌─< f000:0434      7424           je 0xf045a
        │   f000:0436      2eff7442       push word cs:[si + 0x42]
        │   f000:043a      1f             pop ds // ds 设为 e000
        │   f000:043b      837ef600       cmp word [bp - 0xa], 0 // 第一个局部变量,貌似并没有用
       ┌──< f000:043f      7419           je 0xf045a
       ││   f000:0441      2eff7444       push word cs:[si + 0x44] // 0xf0054 处为 0x8008
       ││   f000:0445      5e             pop si // si 设为 0x8008
       ││   f000:0446      8b44fe         mov ax, word [si - 2] // 0xe8006 处为0xe82d
       ││   f000:0449      874604         xchg word [bp + 4], ax // bp+4为入参1
      ┌───> f000:044c      833cff         cmp word [si], 0xffff // 最终会在0xe800c处找到0xffff
     ┌────< f000:044f      7409           je 0xf045a
     │╎││   f000:0451      3904           cmp word [si], ax // 此时 ax 为 1
    ┌─────< f000:0453      740d           je 0xf0462 // 找到 1 就跳
   ┌──────> f000:0455      83c604         add si, 4
   ╎││└───< f000:0458      ebf2           jmp 0xf044c
   ╎│└─└└─> f000:045a      9d             popf
   ╎│       f000:045b      1f             pop ds
   ╎│       f000:045c      5e             pop si
   ╎│       f000:045d      58             pop ax
   ╎│       f000:045e      5d             pop bp
   ╎│       f000:045f      c20200         ret 2
   ╎└─────> f000:0462      1e             push ds
   ╎        f000:0463      06             push es
   ╎        f000:0464      0fa0           push fs
   ╎        f000:0466      0fa8           push gs
   ╎        f000:0468      6660           pushal
   ╎        f000:046a      0e             push cs
   ╎        f000:046b      687504         push 0x475                   ; 1141
   ╎        f000:046e      ff7604         push word [bp + 4]
   ╎        f000:0471      ff7402         push word [si + 2]
   ╎        f000:0474      cb             retf
   ╎        f000:0475      6661           popal
   ╎        f000:0477      0fa9           pop gs
   ╎        f000:0479      0fa1           pop fs
   ╎        f000:047b      07             pop es
   ╎        f000:047c      1f             pop ds
   └──────< f000:047d      ebd6           jmp 0xf0455

[e000:8006]> pxh
e000:8006  0xe82d 0x0007 0x0000 0xffff 0x0000 0xf859 0xf000 0xfc80  -.........Y.....

[f000:3ab2]> s 0xf3af4
[f000:3af4]> pd 100
            f000:3af4      66c1e902       shr ecx, 2 // ecx=0
            f000:3af8      6633c0         xor eax, eax // eax=0
            f000:3afb      f36766ab       rep stosd dword es:[edi], eax // 写入 ecx=0 次
            f000:3aff      e85106         call 0xf4153
            f000:3b02      6633c0         xor eax, eax
            f000:3b05      6633db         xor ebx, ebx
            f000:3b08      6633c9         xor ecx, ecx
            f000:3b0b      6633d2         xor edx, edx
            f000:3b0e      6633f6         xor esi, esi
            f000:3b11      6633ff         xor edi, edi
            f000:3b14      6633ed         xor ebp, ebp
            f000:3b17      6681e4ffff00.  and esp, 0xffff
            f000:3b1e      cb             retf

[f000:3af4]> s 0xf4153
[f000:4153]> pd 100
            f000:4153      6660           pushal
            f000:4155      fa             cli
            f000:4156      9c             pushf
            f000:4157      ba1000         mov dx, 0x10                 ; 16
            f000:415a      e80d00         call 0xf416a // 根据下面的推测,这个函数也不应该返回
            f000:415d      9d             popf
        ┌─< f000:415e      7503           jne 0xf4163 // 上一个 je 指令为假,所以这一个指令会跳转,但跳转的话函数就返回了,也不对
        │   f000:4160      e8affa         call 0xf3c12
        └─> f000:4163      6661           popal
            f000:4165      c3             ret
            f000:4166      e8eaff         call 0xf4153
            f000:4169      cb             retf

其他资料

SeaBIOS

开源的 16bit x86 BIOS 实现,实现了 x86 平台标准 BIOS 调用接口,qemu 的默认 BIOS。
https://www.seabios.org/SeaBIOS

coreboot

开源的 boot firmware,支持多种硬件,CPU 的第一条指令从 coreboot 开始运行,随后控制权被 coreboot 交给 payload,payload 可以是上面提到的 SeaBIOS。
https://www.coreboot.org/

TianoCore

UEFI 的开源实现。
https://www.tianocore.org/

参考资料:
https://jin-yang.github.io/reference/linux/kernel/CPU_Reset.pdf
http://ece-research.unm.edu/jimp/310/slides/8086_memory2.html
http://bochs.sourceforge.net/techspec/CMOS-reference.txt
https://stackoverflow.com/questions/42593957/bios-reads-twice-from-different-port-to-the-same-register-in-a-row
http://www.bioscentral.com/misc/cmosmap.htm
http://kernelx.weebly.com/cmos.html
http://bochs.sourceforge.net/techspec/PORTS.LST
https://sites.google.com/site/pinczakko/pinczakko-s-guide-to-award-bios-reverse-engineering#Bootblock
http://stanislavs.org/helppc/bios_data_area.html
https://www.matrix-bios.nl/system/cmos.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容