fishhook x MachOView源碼閱讀

1. fishhook源碼閱讀

1.1 fishhok原理

dyld通過(guò)更新Mach-O二進(jìn)制文件中特定__DATA段的指針來(lái)綁定惰性和非惰性符號(hào)既们。fishhook通過(guò)傳遞給rebind_symbols的符號(hào)名來(lái)確定需要更新的位置钞支,然后用相應(yīng)的替換項(xiàng)重新綁定這些符號(hào)。

對(duì)于給定的鏡像阅懦,__DATA段可以包含與動(dòng)態(tài)符號(hào)綁定相關(guān)的兩個(gè)部分:__nl_symbol_ptr__la_symbol_ptr

  • __nl_symbol_ptr是指向非延遲綁定數(shù)據(jù)的指針數(shù)組(這些指針在加載庫(kù)時(shí)綁定)理茎。

  • __la_symbol_ptr是指向?qū)牒瘮?shù)的指針數(shù)組熬苍,通常在第一次調(diào)用該符號(hào)時(shí)由名為dyld_stub_binder的例程填充(也可以在啟動(dòng)時(shí)告訴dyld綁定這些指針)。

為了找到對(duì)應(yīng)于這些部分中某個(gè)特定位置的符號(hào)的名稱地技,我們需要通過(guò)幾個(gè)間接層來(lái)進(jìn)行查看蜈七。

  • 對(duì)于兩個(gè)相關(guān)部分,section header<mach-o/loader.h>中聲明的struct section)提供一個(gè)偏移量(在reserved1字段中)到所謂的間接符號(hào)表中乓土。

  • 間接符號(hào)表位于二進(jìn)制文件的__LINKEDIT段中宪潮,它只是符號(hào)表(也在__LINKEDIT中)中的索引數(shù)組,其順序與非惰性和惰性符號(hào)部分中的指針順序相同趣苏。因此狡相,struct section nl_symbol_ptr,該部分中第一個(gè)地址的符號(hào)表中的對(duì)應(yīng)索引是indirect_symbol_table[nl_symbol_ptr->reserved1]食磕。

  • 符號(hào)表本身是一個(gè)struct nlist數(shù)組(請(qǐng)參見(jiàn)<mach-o/nlist.h>)尽棕,每個(gè)nlist都包含一個(gè)指向__LINKEDIT中字符串表的索引,其中存儲(chǔ)了實(shí)際的符號(hào)名彬伦。因此滔悉,對(duì)于每個(gè)指針__nl_symbol_ptr__la_symbol_ptr,我們都可以找到相應(yīng)的符號(hào)单绑,然后找到相應(yīng)的字符串與請(qǐng)求的符號(hào)名進(jìn)行比較回官,如果有匹配項(xiàng),我們用替換項(xiàng)替換節(jié)中的指針搂橙。

fishhook官方原理示意圖

1.2 測(cè)試代碼

//---------------------------------更改NSLog-----------
//函數(shù)指針
static void(*sys_nslog)(NSString * format,...);

//定義一個(gè)新的函數(shù)
void my_nslog(NSString * format,...){
    format = [format stringByAppendingString:@"你咋又來(lái)了 \n"];
    //調(diào)用原始的
    sys_nslog(format);
}

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"log來(lái)了歉提,老弟");
    
    struct rebinding nslog;
    nslog.name = "NSLog";
    nslog.replacement = my_nslog;
    nslog.replaced = (void *)&sys_nslog;
    struct rebinding rebs[1] = {nslog};
    rebind_symbols(rebs, 1);
    
    NSLog(@"log來(lái)了,老弟");
}

@end

運(yùn)行結(jié)果:

2020-03-16 09:47:38.526862+0800 Demo[28657:5210895] log來(lái)了区转,老弟
2020-03-16 09:47:38.536892+0800 Demo[28657:5210895] log來(lái)了苔巨,老弟你咋又來(lái)了

1.3 Mach-O附著

MachOView Attach

MachOView會(huì)彈出輸入框讓你輸入PID

PID

這個(gè)PID在Xcode的Show the Debug navigator菜單下废离,可以用? + 7快速切過(guò)來(lái)侄泽。這里我們可以看到進(jìn)程的PID,輸入到上面的框中蜻韭。

Xcode PID獲取

1.4 MachOView與源碼閱讀驗(yàn)證

頂部數(shù)據(jù)定義與初始化

struct rebindings_entry {
    struct rebinding *rebindings;
    size_t rebindings_nel;
    struct rebindings_entry *next;
};

static struct rebindings_entry *_rebindings_head;

// 給需要rebinding的方法結(jié)構(gòu)體開(kāi)辟出對(duì)應(yīng)的空間
// 生成對(duì)應(yīng)的鏈表結(jié)構(gòu)(rebindings_entry)
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
                              struct rebinding rebindings[],
                              size_t nel) {
    // 開(kāi)辟一個(gè)rebindings_entry大小的空間
    struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
    if (!new_entry) {
        return -1;
    }
    // 一共有nel個(gè)rebinding
    new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel);
    if (!new_entry->rebindings) {
        free(new_entry);
        return -1;
    }
    // 將rebinding賦值給new_entry->rebindings
    memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
    // 繼續(xù)賦值nel
    new_entry->rebindings_nel = nel;
    // 每次都將new_entry插入頭部
    new_entry->next = *rebindings_head;
    // rebindings_head重新指向頭部
    *rebindings_head = new_entry;
    return 0;
}

這里定義了rebindings_entry鏈表悼尾。每次進(jìn)行綁定的時(shí)候,會(huì)傳入struct rebinding rebindings[]數(shù)組湘捎,創(chuàng)建一個(gè)新的rebindings_entry結(jié)構(gòu)诀豁,然后把這個(gè)結(jié)構(gòu)插入鏈表頭部。

兩個(gè)公開(kāi)方法

static void _rebind_symbols_for_image(const struct mach_header *header, intptr_t slide) {
    // 找到對(duì)應(yīng)的符號(hào)窥妇,進(jìn)行重綁定
    rebind_symbols_for_image(_rebindings_head, header, slide);
}

// 在知道確定的MachO舷胜,可以使用該方法
int rebind_symbols_image(void *header,
                         intptr_t slide,
                         struct rebinding rebindings[],
                         size_t rebindings_nel) {
    struct rebindings_entry *rebindings_head = NULL;
    int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
    rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
    if (rebindings_head) {
        free(rebindings_head->rebindings);
    }
    free(rebindings_head);
    return retval;
}

int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
    int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
    if (retval < 0) {
        return retval;
    }
    // 如果這是第一次調(diào)用,請(qǐng)為image添加注冊(cè)回調(diào)(這也會(huì)為現(xiàn)有image調(diào)用,否則烹骨,只在現(xiàn)有image上運(yùn)行
    if (!_rebindings_head->next) {
        // 向每個(gè)image注冊(cè)_rebind_symbols_for_image函數(shù)翻伺,并且立即觸發(fā)一次
        _dyld_register_func_for_add_image(_rebind_symbols_for_image);
    } else {
        // _dyld_image_count() 獲取image數(shù)量
        uint32_t c = _dyld_image_count();
        for (uint32_t i = 0; i < c; i++) {
            // _dyld_get_image_header(i) 獲取第i個(gè)image的header指針
            // _dyld_get_image_vmaddr_slide(i) 獲取第i個(gè)image的基址
            _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
        }
    }
    return retval;
}

rebind_symbols_imagerebind_symbols是兩個(gè)公開(kāi)的方法,用于重新綁定符號(hào)沮焕。rebind_symbols_image用于指定鏡像的符號(hào)綁定吨岭,rebind_symbols對(duì)所有鏡像進(jìn)行處理。

不管是哪個(gè)方法峦树,最后都是調(diào)用rebind_symbols_for_image去獲取相關(guān)部分的地址辣辫。

相關(guān)部分的地址

static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
                                     const struct mach_header *header,
                                     intptr_t slide) {
    Dl_info info;
    // 判斷當(dāng)前macho是否在進(jìn)程里,如果不在則直接返回
    if (dladdr(header, &info) == 0) {
        return;
    }
    
    // 定義好幾個(gè)變量魁巩,后面去遍歷查找
    segment_command_t *cur_seg_cmd;
    // MachO中Load Commons中的linkedit
    segment_command_t *linkedit_segment = NULL;
    // MachO中LC_SYMTAB
    struct symtab_command* symtab_cmd = NULL;
    // MachO中LC_DYSYMTAB
    struct dysymtab_command* dysymtab_cmd = NULL;
    
    // header的首地址+mach_header的內(nèi)存大小
    // 得到跳過(guò)mach_header的地址,也就是直接到Load Commons的地址
    uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
    // 遍歷Load Commons 找到上面三個(gè)遍歷
    for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
        cur_seg_cmd = (segment_command_t *)cur;
        // 如果是LC_SEGMENT_64
        if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
            // 找到linkedit
            if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
                linkedit_segment = cur_seg_cmd;
            }
        }
        // 如果是LC_SYMTAB,就找到了symtab_cmd
        else if (cur_seg_cmd->cmd == LC_SYMTAB) {
            symtab_cmd = (struct symtab_command*)cur_seg_cmd;
        }
        // 如果是LC_DYSYMTAB,就找到了dysymtab_cmd
        else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
            dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
        }
    }
    // 下面其中任何一個(gè)值沒(méi)有都直接return
    // 因?yàn)閕mage不是需要找的image
    if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
        !dysymtab_cmd->nindirectsyms) {
        return;
    }
    
    // Find base symbol/string table addresses
    // 找到linkedit的頭地址
    uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
    // 獲取symbol_table的真實(shí)地址
    nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
    // 獲取string_table的真實(shí)地址
    char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
    
    // Get indirect symbol table (array of uint32_t indices into symbol table)
    // 獲取indirect_symtab的真實(shí)地址
    uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
    // 同樣的急灭,得到跳過(guò)mach_header的地址,得到Load Commons的地址
    cur = (uintptr_t)header + sizeof(mach_header_t);
    // 遍歷Load Commons,找到對(duì)應(yīng)符號(hào)進(jìn)行重新綁定
    for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
        cur_seg_cmd = (segment_command_t *)cur;
        if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
            // 如果不是__DATA段谷遂,也不是__DATA_CONST段葬馋,直接跳過(guò)
            if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
                strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
                continue;
            }
            // 遍歷所有的section
            for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
                section_t *sect = (section_t *)(cur + sizeof(segment_command_t)) + j;
                // 找懶加載表S_LAZY_SYMBOL_POINTERS
                if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
                    // 重綁定的真正函數(shù)
                    perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
                }
                // 找非懶加載表S_NON_LAZY_SYMBOL_POINTERS
                if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
                    // 重綁定的真正函數(shù)
                    perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
                }
            }
        }
    }
}

最上面,通過(guò)header指針和header大小獲取到加載指令的基址肾扰。然后遍歷獲取3個(gè)數(shù)據(jù)結(jié)構(gòu):

// MachO中Load Commons中的linkedit
segment_command_t *linkedit_segment = NULL;
// MachO中LC_SYMTAB
struct symtab_command* symtab_cmd = NULL;
// MachO中LC_DYSYMTAB
struct dysymtab_command* dysymtab_cmd = NULL;

下面是比較核心的代碼:

// 找到linkedit的頭地址
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
__LINKEDIT段

我們來(lái)看看linkedit_segment->vmaddr對(duì)應(yīng)4294995968畴嘶,linkedit_segment->fileoff對(duì)應(yīng)28672。這樣可能看不太出來(lái)這是基地址集晚,我們格式化一下:

(lldb) p/x 4294995968
(long) $0 = 0x0000000100007000
(lldb) p/x 28672
(int) $1 = 0x00007000
(lldb) p/x 4294995968 - 28672
(long) $2 = 0x0000000100000000

我們可以看出這個(gè)部分就是拿到了image對(duì)應(yīng)的內(nèi)存基址窗悯。

// 獲取symbol_table的真實(shí)地址
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
// 獲取string_table的真實(shí)地址
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
LC_SYMTAB

struct symtab_command結(jié)構(gòu)中獲取到符號(hào)表的字符表的偏移量,然后加載基址就是內(nèi)存中兩個(gè)表的地址了偷拔。

(lldb) p/x 0x0000000100000000 + 30200
(long) $3 = 0x00000001000075f8
(lldb) p/x 0x0000000100000000 + 33408
(long) $4 = 0x0000000100008280
符號(hào)表驗(yàn)證
字符表驗(yàn)證

通過(guò)MachOView我們也驗(yàn)證了這兩個(gè)地址是正確的蟀瞧。

// 獲取indirect_symtab的真實(shí)地址
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);

通過(guò)struct dysymtab_command獲取間接符號(hào)表。

間接符號(hào)表偏移量
(lldb) p/x 0x0000000100000000 + 33224
(long) $5 = 0x00000001000081c8
間接符號(hào)表驗(yàn)證

間接符號(hào)表的地址我們也獲得了条摸。

與動(dòng)態(tài)符號(hào)綁定相關(guān)的兩個(gè)部分

// 同樣的,得到跳過(guò)mach_header的地址,得到Load Commons的地址
cur = (uintptr_t)header + sizeof(mach_header_t);
// 遍歷Load Commons铸屉,找到對(duì)應(yīng)符號(hào)進(jìn)行重新綁定
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
    cur_seg_cmd = (segment_command_t *)cur;
    if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
        // 如果不是__DATA段钉蒲,也不是__DATA_CONST段,直接跳過(guò)
        if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
            strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
            continue;
        }
        // 遍歷所有的section
        for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
            section_t *sect = (section_t *)(cur + sizeof(segment_command_t)) + j;
            // 找懶加載表S_LAZY_SYMBOL_POINTERS
            if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
                // 重綁定的真正函數(shù)
                perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
            }
            // 找非懶加載表S_NON_LAZY_SYMBOL_POINTERS
            if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
                // 重綁定的真正函數(shù)
                perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
            }
        }
    }
}

對(duì)于給定的image彻坛,__DATA段包含與動(dòng)態(tài)符號(hào)綁定相關(guān)的兩個(gè)部分:__nl_symbol_ptr__la_symbol_ptr顷啼。遍歷找到這個(gè)兩個(gè)部分,然后進(jìn)行符號(hào)重新綁定昌屉。

符號(hào)重新綁定

static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
                                           section_t *section,
                                           intptr_t slide,
                                           nlist_t *symtab,
                                           char *strtab,
                                           uint32_t *indirect_symtab) {
    // reserved1對(duì)應(yīng)的的是indirect_symbol中的offset钙蒙,也就是indirect_symbol的真實(shí)地址
    // indirect_symtab+offset就是indirect_symbol_indices(indirect_symbol的數(shù)組)
    uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
    // 函數(shù)地址,addr就是section的偏移地址
    void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
    // 遍歷section中的每個(gè)符號(hào)
    for (uint i = 0; i < section->size / sizeof(void *); i++) {
        // 訪問(wèn)indirect_symbol间驮,symtab_index就是indirect_symbol中data的值
        uint32_t symtab_index = indirect_symbol_indices[i];
        if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
            symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
            continue;
        }
        // 訪問(wèn)symbol_table躬厌,根據(jù)symtab_index獲取到symbol_table中的偏移offset
        uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
        // 訪問(wèn)string_table,根據(jù)strtab_offset獲取symbol_name
        char *symbol_name = strtab + strtab_offset;
        // string_table中的所有函數(shù)名都是以"."開(kāi)始的竞帽,所以一個(gè)函數(shù)一定有兩個(gè)字符
        bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
        struct rebindings_entry *cur = rebindings;
        // 已經(jīng)存入的rebindings_entry
        while (cur) {
            // 循環(huán)每個(gè)entry中需要重綁定的函數(shù)
            for (uint j = 0; j < cur->rebindings_nel; j++) {
                // 判斷symbol_name是否是一個(gè)正確的函數(shù)名
                // 需要被重綁定的函數(shù)名是否與當(dāng)前symbol_name相等
                if (symbol_name_longer_than_1 &&
                    strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
                    // 判斷replaced是否存在
                    // 判斷replaced和老的函數(shù)是否是一樣的
                    if (cur->rebindings[j].replaced != NULL &&
                        indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {
                        // 將原函數(shù)的地址給新函數(shù)replaced
                        *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
                    }
                    // 將replacement賦值給剛剛找到的
                    indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
                    goto symbol_loop;
                }
            }
            // 繼續(xù)下一個(gè)需要綁定的函數(shù)
            cur = cur->next;
        }
    symbol_loop:;
    }
}

這個(gè)部分就像fishhook原理里面提到的:

  1. indirect_symbol_indices[nl_symbol_ptr->reserved1]拿到間接符號(hào)表的函數(shù)起始地址扛施。
  2. indirect_symbol_bindingsnl_symbol_ptr中對(duì)應(yīng)的函數(shù)指針數(shù)組鸿捧。
  3. 依次遍歷間接符號(hào)表拿到符號(hào)表索引值,并取出符號(hào)表中對(duì)應(yīng)索引值的結(jié)構(gòu)疙渣,拿到字符表中的偏移量匙奴。
  4. 通過(guò)字符表和偏移量獲取到函數(shù)名的字符數(shù)組首地址。
  5. 字符表中的函數(shù)名都是.開(kāi)頭的妄荔,所以至少有2個(gè)字符泼菌。symbol_name[1]是去掉開(kāi)頭.的字符串。
  6. 循環(huán)遍歷我們要綁定的鏈表啦租,對(duì)比函數(shù)名和symbol_name[1]是否相等哗伯,將原來(lái)的函數(shù)地址給replaced中的函數(shù)指針,再將原來(lái)函數(shù)的地址替換為我們要綁定的replacement函數(shù)地址刷钢。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末笋颤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子内地,更是在濱河造成了極大的恐慌伴澄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阱缓,死亡現(xiàn)場(chǎng)離奇詭異非凌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)荆针,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門敞嗡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人航背,你說(shuō)我怎么就攤上這事喉悴。” “怎么了玖媚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵箕肃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我今魔,道長(zhǎng)勺像,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任错森,我火速辦了婚禮吟宦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涩维。我一直安慰自己殃姓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著辰狡,像睡著了一般锋叨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宛篇,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天娃磺,我揣著相機(jī)與錄音,去河邊找鬼叫倍。 笑死偷卧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吆倦。 我是一名探鬼主播听诸,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚕泽!你這毒婦竟也來(lái)了晌梨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤须妻,失蹤者是張志新(化名)和其女友劉穎仔蝌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體荒吏,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敛惊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绰更。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞧挤。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖儡湾,靈堂內(nèi)的尸體忽然破棺而出特恬,到底是詐尸還是另有隱情,我是刑警寧澤徐钠,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布鸵鸥,位于F島的核電站,受9級(jí)特大地震影響丹皱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宋税,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一摊崭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧杰赛,春花似錦呢簸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瘦赫。三九已至,卻和暖如春蛤迎,著一層夾襖步出監(jiān)牢的瞬間确虱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工替裆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留校辩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓辆童,卻偏偏與公主長(zhǎng)得像宜咒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子把鉴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容