Fuzzilli 初识

PPT 资料

  • Motivation
    • 发现js引擎中的漏洞,比如JIT
  • Requirements
    • 提供的样本算法有效

语法正确性

  • 基于语法的fuzz很容易实现,比如domato
    • 基本思想:将js当作上下文无关文法语言,然后应用生成规则变异
    • 如下例子就是基于语法生成的fuzzer生成的样本
...;
var v4 = new Array(42, v3, “foobar”);
for (var v5 = 0; v5 < 1000; v5++) {    
    v4 = v5 * 7;    
    var v6 = v4.slice(v1, v1, v2);
    }
...;
  • 但是以上例子可能出现异常 Exception: TypeError: v4.slice is not a function.,导致后边的语句永远不会被执行。
    • 加入 try-catch? 但是会导致在JIT执行代码的时候和原有代码有很大出路

高度语义正确性

  • 相对语法正确来说更难
  • 多个解决方案
    • 生成代码阶段执行精确的类型检查
    • 一步一步生成,每一步都检测有效性
    • 使用变异方法,只保留语料库中语义有效的样本
  • 定义js代码的合理变异

变异程序

  • 变异可能是在不同的level上进行的:源代码、ast、bytecode
  • 观测 程序的数据流和控制流变化的情况

FuzzIL

  • 定义一种IR语言
  • 观测程序的控制流和数据流
  • 定义基于IL的变异
  • 将IL转换为Js

变异:

  • 操作变异
  • 插入变异(生成新的代码)
  • 输入变异
  • 拼接变异(插入已有代码)

最小化样本

  • 上述变异只会增大样本大小,在加入语料库前执行minim操作
    • 最简单的做法就是一句一句删除,看删除到那一句会对最终结果造成影响

guided fuzzing

  • feedback system
  • 基于边覆盖

Architecture

  • Fuzzer
    • Corpus
    • Opt module
    • Mutator
    • Lifter
    • Minimizer
    • FeedBack

​ 对于每一个target对应一个fuzzer实例,通过IPC或者network同步,程序可以从另一个实例导入,并与本地语料库进行比较

  • 同步
    • Master----(IPC、network、task queue)--- worker、worker

A fuzzer instance (implemented in Fuzzer.swift) is made up of the following central components:

  • FuzzerCore: produces new programs from existing ones by applying mutations. Afterwards executes the produced samples and evaluates them.
  • ScriptRunner: executes programs of the target language.
  • Corpus: stores interesting samples and supplies them to the core fuzzer.
  • Environment: has knowledge of the runtime environment, e.g. the available builtins, property names, and methods.
  • Minimizer: minimizes crashing and interesting programs.
  • Evaluator: evaluates whether a sample is interesting according to some metric, e.g. code coverage.
  • Lifter: translates a FuzzIL program to the target language (JavaScript).

Furthermore, a number of modules are optionally available:

​ The fuzzer is event-driven, with most of the interactions between different classes happening through events. Events are dispatched e.g. as a result of a crash or an interesting program being found, a new program being executed, a log message being generated and so on. See Events.swift for the full list of events. The event mechanism effectively decouples the various components of the fuzzer and makes it easy to implement additional modules.

​ A FuzzIL program can be built up using a ProgramBuilder instance. A ProgramBuilder provides methods to create and append new instructions, append instructions from another program, retrieve existing variables, query the execution context at the current position (e.g. whether it is inside a loop), and more.

Scalability

There is one fuzzer instance per target process. This enables synchronous execution of programs and thereby simplifies the implementation of various algorithms such as consecutive mutations and minimization. Moreover, it avoids the need to implement thread-safe access to internal state, e.g. the corpus. Each fuzzer instance has its own dedicated OperationQueue, conceptually corresponding to a single thread. Every interaction with a fuzzer instance must then happen on the instance’s queue. This guarantees thread-safety as the queue is serial. For more details see the docs.

To scale, fuzzer instances can become workers, in which case they report newly found interesting samples and crashes to a master instance. In turn, the master instances also synchronize their corpus with the workers. Communication between masters and workers can happen in different ways, each implemented as a module:

  • Inter-thread communication: synchronize instances in the same process by enqueuing tasks to the other fuzzer’s DispatchQueue.
  • Inter-process communication (TODO): synchronize instances over an IPC channel.
  • Inter-machine communication: synchronize instances over a simple TCP-based protocol.

This design allows the fuzzer to scale to many cores on a single machine as well as to many different machines. As one master instance can quickly become overloaded if too many workers send programs to it, it is also possible to configure multiple tiers of master instances, e.g. one master instance, 16 intermediate masters connected to the master, and 256 workers connected to the intermediate masters.

安装和运行

这里以V8为例,根据官方repo的readme依次执行即可,checkout到特定的版本,根据以下步骤

The basic steps to use this fuzzer are:

  1. Download the source code for one of the supported JavaScript engines (currently JavaScriptCore, Spidermonkey, and v8).
  2. Apply the corresponding patch from the Targets/ directory. Also see the README.md in that directory.
  3. Compile the engine with coverage instrumentation (requires clang >= 4.0) as described in the README.
  4. Compile the fuzzer: swift build [-c release].
  5. Run the fuzzer: swift run [-c release] FuzzilliCli --profile=<profile> [other cli options] /path/to/jsshell. See also swift run FuzzilliCli --help.

最后跑起来的效果就是:

nevv@ubuntu:~/Browser/fuzz/fuzzilli-master$ .build/x86_64-unknown-linux/release/FuzzilliCli --profile=v8 ../../pwn/34c3_v9/v8/v8/v8/out/fuzzbuild/d8
[Cli] No filesystem storage configured, found crashes will be discarded!
[Coverage] Initialized, 560233 edges
[JavaScriptEnvironment] initialized static JS environment model
[Fuzzer] Initialized
[Fuzzer] Recommended timeout: at least 120ms. Current timeout: 250ms
[Fuzzer] Startup tests finished successfully
[Fuzzer] Let's go!

Fuzzer Statistics
-----------------
Total Samples:                430
Interesting Samples Found:    142
Valid Samples Found:          318
Corpus Size:                  143
Success Rate:                 73.95%
Timeout Rate:                 0.47%
Crashes Found:                0
Timeouts Hit:                 2
Coverage:                     4.17%
Avg. program size:            73.97
Connected workers:            0
Execs / Second:               114.18
Total Execs:                  7343

源码概览

目录结构

nevv@ubuntu:~/Browser/fuzz$ tree fuzzilli-master
fuzzilli-master
├── CONTRIBUTING.md
├── Docs
│   └── ProcessingModel.md
├── LICENSE
├── Misc
│   ├── enum_properties.js
│   ├── Forkserver
│   │   ├── server.c
│   │   └── tester.c
│   └── REPRL
│       └── tester.c
├── Package.swift
├── README.md
├── Sources
│   ├── Fuzzilli
│   │   ├── Configuration.swift
│   │   ├── Core
│   │   │   ├── CodeGenerators.swift
│   │   │   ├── Component.swift
│   │   │   ├── Corpus.swift
│   │   │   ├── Environment.swift
│   │   │   ├── Events.swift
│   │   │   ├── FuzzerCore.swift
│   │   │   ├── JavaScriptEnvironment.swift
│   │   │   ├── Logging.swift
│   │   │   ├── ProgramBuilder.swift
│   │   │   └── Timers.swift
│   │   ├── Evaluation
│   │   │   ├── ProgramAspects.swift
│   │   │   ├── ProgramCoverageEvaluator.swift
│   │   │   └── ProgramEvaluator.swift
│   │   ├── Execution
│   │   │   ├── Execution.swift
│   │   │   ├── Forkserver.swift
│   │   │   ├── REPRL.swift
│   │   │   └── ScriptRunner.swift
│   │   ├── Fuzzer.swift
│   │   ├── FuzzIL
│   │   │   ├── AbstractInterpreter.swift
│   │   │   ├── Analyzer.swift
│   │   │   ├── Blocks.swift
│   │   │   ├── Instruction.swift
│   │   │   ├── Operations.swift
│   │   │   ├── Program.swift
│   │   │   ├── TypeSystem.swift
│   │   │   └── Variable.swift
│   │   ├── Lifting
│   │   │   ├── Expression.swift
│   │   │   ├── InliningPolicy.swift
│   │   │   ├── JavaScriptLifter.swift
│   │   │   ├── JSExpressions.swift
│   │   │   ├── Lifter.swift
│   │   │   └── ScriptWriter.swift
│   │   ├── Minimization
│   │   │   ├── BlockReducer.swift
│   │   │   ├── CallArgumentReducer.swift
│   │   │   ├── GenericInstructionReducer.swift
│   │   │   ├── InliningReducer.swift
│   │   │   ├── Minimizer.swift
│   │   │   ├── Reducer.swift
│   │   │   └── ReplaceReducer.swift
│   │   ├── Modules
│   │   │   ├── Module.swift
│   │   │   ├── NetworkSync.swift
│   │   │   ├── Statistics.swift
│   │   │   ├── Storage.swift
│   │   │   └── ThreadSync.swift
│   │   ├── Mutators
│   │   │   ├── BaseInstructionMutator.swift
│   │   │   ├── CombineMutator.swift
│   │   │   ├── ConcatMutator.swift
│   │   │   ├── GrowMutator.swift
│   │   │   ├── InputMutator.swift
│   │   │   ├── InsertionMutator.swift
│   │   │   ├── JITStressMutator.swift
│   │   │   ├── Mutator.swift
│   │   │   ├── OperationMutator.swift
│   │   │   └── SpliceMutator.swift
│   │   └── Util
│   │       ├── CInterop.swift
│   │       ├── Error.swift
│   │       ├── Misc.swift
│   │       ├── MovingAverage.swift
│   │       ├── OperationSource.swift
│   │       ├── Random.swift
│   │       ├── RingBuffer.swift
│   │       ├── VariableMap.swift
│   │       ├── VariableSet.swift
│   │       └── WeightedList.swift
│   ├── FuzzilliCli
│   │   ├── Arguments.swift
│   │   ├── main.swift
│   │   ├── Profiles
│   │   │   ├── JSCProfile.swift
│   │   │   ├── Profile.swift
│   │   │   ├── SpidermonkeyProfile.swift
│   │   │   └── V8Profile.swift
│   │   ├── Settings.swift
│   │   └── TerminalUI.swift
│   ├── libcoverage
│   │   ├── coverage.c
│   │   └── include
│   │       └── libcoverage.h
│   ├── libforkserver
│   │   ├── forkserver.c
│   │   └── include
│   │       └── libforkserver.h
│   ├── libreprl
│   │   ├── include
│   │   │   └── libreprl.h
│   │   └── libreprl.c
│   └── libsocket
│       ├── include
│       │   └── libsocket.h
│       └── socket.c
├── Targets
│   ├── JavaScriptCore
│   │   ├── fuzzbuild.sh
│   │   ├── README.md
│   │   └── webkit.patch
│   ├── Spidermonkey
│   │   ├── firefox.patch
│   │   ├── fuzzbuild.sh
│   │   └── README.md
│   └── V8
│       ├── fuzzbuild.sh
│       ├── README.md
│       └── v8.patch
└── Tests
    ├── FuzzilliTests
    │   ├── CodeGenerationTest.swift
    │   ├── InliningTest.swift
    │   ├── InterpreterTest.swift
    │   ├── MockFuzzer.swift
    │   ├── ProgramSerializationTest.swift
    │   ├── RingBufferTest.swift
    │   ├── TestUtils.swift
    │   ├── TypeSystemTest.swift
    │   ├── VariableMapTest.swift
    │   ├── VariableSetTest.swift
    │   └── XCTestManifests.swift
    └── LinuxMain.swift

31 directories, 111 files
FuzzilliCli
  • Fuzzilli的命令行工具,主要功能是:
    • 解析命令行参数并配置fuzz
    • 设置终端的UI (TerminalUI.swift)
    • 设置不同js引擎的profile(JSC、spidermonkey、V8)
│   ├── FuzzilliCli
│   │   ├── Arguments.swift
│   │   ├── main.swift
│   │   ├── Profiles
│   │   │   ├── JSCProfile.swift
│   │   │   ├── Profile.swift
│   │   │   ├── SpidermonkeyProfile.swift
│   │   │   └── V8Profile.swift
│   │   ├── Settings.swift
│   │   └── TerminalUI.swift
profiles

在profile文件中会定义 processArguments 和 codePrefix 、codeSuffix,比如v8:

let v8Profile = Profile(
    processArguments: ["--debug-code",
                       "--expose-gc",
                       "--single-threaded",
                       "--predictable",
                       "--allow-natives-syntax",
                       "--interrupt-budget=1024",
                       "--fuzzing",
                       "--reprl"],
    
    processEnv: [:],
    
    codePrefix: """
                function main() {
                """,
    
    codeSuffix: """
                }
                %NeverOptimizeFunction(main);
                main();
                """,
    
    crashTests: ["fuzzilli('FUZZILLI_CRASH', 0)", "fuzzilli('FUZZILLI_CRASH', 1)", "fuzzilli('FUZZILLI_CRASH', 2)"],
    
    additionalCodeGenerators: WeightedList<CodeGenerator>([
        (ForceV8TurbofanGenerator, 10),
    ]),
    
    additionalBuiltins: [
        "gc"                : .function([] => .undefined),
    ]
)
main
  • 初始化 REPRL
  • 初始化 Mutator,这里多插入了InsertionMutator,因为它更易产生无效样本
  • 初始化 FuzzerCore
  • 初始化 codeGenerators 负责代码生成 (defaultCodeGenerators + profile.additionalCodeGenerators,在V8中是ForceV8TurbofanGenerator)
  • 初始化 ProgramCoverageEvaluator 计算覆盖率
  • 初始化 JavaScriptEnvironment,能够加载可用的builtins函数
  • 初始化 JavaScriptLifter ,能够将FuzzIL转换成js代码
  • 初始化 Corpus,语料库,设置样本最大/最小值
  • 初始化 Minimizer ,minimize crashes and interesting programs.
  • 构造 Fuzzer 实例
  • 初始化 TerminalUI
Settings
  • defaultCodeGenerators 不同code生成器所占的权重
  • 权重计算? 优化?
    (IntegerLiteralGenerator,            2),
    (FloatLiteralGenerator,              1),
    (StringLiteralGenerator,             1),
    (BooleanLiteralGenerator,            1),
    (UndefinedValueGenerator,            1),
    (NullValueGenerator,                 1),
    (BuiltinGenerator,                   5),
    (BuiltinGenerator,                   5),
    (ObjectLiteralGenerator,             10),
    (ArrayLiteralGenerator,              10),
    (ObjectLiteralWithSpreadGenerator,   5),
    (ArrayLiteralWithSpreadGenerator,    5),
    (FunctionDefinitionGenerator,        15),
    (FunctionReturnGenerator,            3),
Fuzzilli
Fuzzer
  • 定义了Fuzzer的类,其中包括
    • Fuzzer的设置
    • 能够处理的event
    • FuzzerCore执行fuzz
    • 语料库corpus
    • 最小化执行用例的Minimizer等等
  • start
// runFor是函数标签 提高代码可读性。_ 参数前缀代表调用该函数的时候可以不加该参数的名字
public func start(runFor maxIterations: Int) {
        precondition(isInitialized)
        assert(OperationQueue.current == queue)
        
        self.maxIterations = maxIterations
        
        self.runStartupTests()
        // 测试是不是都正确的配置
        logger.info("Let's go!")
        
        // Start fuzzing
        queue.addOperation {
           self.fuzzOne()
        }
    }
  • fuzzOne
    /// Performs one round of fuzzing.
    private func fuzzOne() {
        guard maxIterations == -1 || iterations < maxIterations else {
            return
        }
        iterations += 1
        
        core.fuzzOne()
        
        // Fuzz another one.
        // We only want do actual fuzzing if there is nothing else to do. For that
        // reason we enqueue the corresponding operations with low priority.
        let op = BlockOperation(block: self.fuzzOne)
        op.queuePriority = .veryLow
        queue.addOperation(op)
    }
  • execute
    • 提升FuzzIL,并通过runner来执行
    /// Executes a program.
    ///
    /// This will first lift the given FuzzIL program to the target language, then use the configured script runner to execute it.
    ///
    /// - Parameters:
    ///   - program: The FuzzIL program to execute.
    ///   - timeout: The timeout after which to abort execution. If nil, the default timeout of this fuzzer will be used.
    /// - Returns: An Execution structure representing the execution outcome.
    public func execute(_ program: Program, withTimeout timeout: UInt32? = nil) -> Execution {
        assert(runner.isInitialized)
        assert(OperationQueue.current == queue)
        
        events.PreExecute.dispatch(with: program)
        
        let script: String
        if config.speedTestMode {
            script = lifter.lift(makeComplexProgram())
        } else {
            script = lifter.lift(program)
        }
        let execution = runner.run(script, withTimeout: timeout ?? config.timeout)
        
        events.PostExecute.dispatch(with: execution)
        
        return execution
    }

FuzzerCore

Generating and executing programs.

  • programPrefixGenerators 生成一些基本的变量类型

  • fuzzOne:变异后调用fuzzer的execute执行样本。

JavaScriptEnvironment
  • 定义了一些比较特殊的数字用来变异:比如smi的最大值、最大负数等
  • 定义了一些特殊的jsTypeNames以及字符串
  • 定义了js的不同数组类型比如jsTypedArrays、Uint16Array等
  • 定义了一些buildin比如Object、ArrayBuffer、Boolean、DataView
ProgramBuilder
  • 构造和附加程序中不同类型操作的随机实例。
  • 记录了属性名和当前程序中出现的整数值。
  • 用于当前程序的各种分析程序scopeAnalyzer和contextAnalyzer
Events
  • Events模版类,可以在fuzzer中调度的所有事件的列表。
    • observe 为当前event注册listener
    • dispatchAsync 异步运行所有注册的listener
    • dispatch 同步运行所有注册的listener
Evalueation
  • ProgramCoverageEvaluator
    • 计算当前边覆盖的百分比
    • 将共享内存地址设置到环境变量中,每次执行前clear共享内存
Execution
  • ExecutionOutcome 可能的结果
  • Execution 类保存执行一个程序后的结果
    • pid
    • outcome
    • termsig
    • output
    • execTime
Forkserver
  • init 初始化执行时的环境变量,执行的参数、outfile路径
  • run 获取执行后的结果

Fuzz 流程

  • fuzzer 的 start() 函数开始执行fuzz
    /// Starts the fuzzer and runs for the specified number of iterations.
    ///
    /// This must be called after initializing the fuzzer.
    /// Use -1 for maxIterations to run indefinitely.
    public func start(runFor maxIterations: Int) {
        precondition(isInitialized)
        assert(OperationQueue.current == queue)
        
        self.maxIterations = maxIterations
        
        self.runStartupTests()
        
        logger.info("Let's go!")
        
        // Start fuzzing
        queue.addOperation {
           self.fuzzOne()
        }
    }
  • 调用fuzzOne()
    /// Performs one round of fuzzing.
    private func fuzzOne() {
        guard maxIterations == -1 || iterations < maxIterations else {
            return
        }
        iterations += 1
        
        core.fuzzOne()
        
        // Fuzz another one.
        // We only want do actual fuzzing if there is nothing else to do. For that
        // reason we enqueue the corresponding operations with low priority.
        let op = BlockOperation(block: self.fuzzOne)
        op.queuePriority = .veryLow
        queue.addOperation(op)
    }

  • 实际调用的是 Fuzzercore 下的 fuzzOne()

    只要没有runtime异常,就会多次变异

    /// Perform one round of fuzzing.
    ///
    /// High-level fuzzing algorithm:
    ///
    ///     let parent = pickSampleFromCorpus()
    ///     repeat N times:
    ///         let current = mutate(parent)
    ///         execute(current)
    ///         if current produced crashed:
    ///             output current
    ///         elif current resulted in a runtime exception or a time out:
    ///             // do nothing
    ///         elif current produced new, interesting behaviour:
    ///             minimize and add to corpus
    ///         else
    ///             parent = current
    ///
    ///
    /// This ensures that samples will be mutated multiple times as long
    /// as the intermediate results do not cause a runtime exception.
  • 首先会根据numConsecutiveMutations(运行时指定,默认是5),对样本连续变异
  • 随后通知fuzzer program生成完毕
  • fuzzer 调用 execute 执行,根据返回结果判断成功、crash、超时或者失败

Mutate

  • 对于单一指令的类都继承自 BaseInstructionMutator,这个类中有两个mutate方法,一个是针对整个程序的,另外一个是变异单一statement
    public func mutate(_ program: Program, for fuzzer: Fuzzer) -> Program? {
        beginMutation(of: program)
        
        var candidates = [Int]()
        for instr in program {
            if canMutate(instr) {
                candidates.append(instr.index)
            }
        }
        
        guard candidates.count > 0 else {
            return nil
        }
        
        var toMutate = Set<Int>()
        for _ in 0..<Int.random(in: 1...maxSimultaneousMutations) {
            toMutate.insert(chooseUniform(from: candidates))
        }
        
        let b = fuzzer.makeBuilder()
        b.adopting(from: program) {
            for instr in program {
                if toMutate.contains(instr.index) {
                    mutate(instr, b)
                } else {
                    b.adopt(instr)
                }
            }
        }
        
        return b.finish()
    }
    

具体来说,主要有以下几类Mutator:

  • CombineMutator

    • A mutator that inserts a program in full into another one.
  • ConcatMutator

    • A mutator that concatenates two programs together.
  • GrowMutator

    • A mutator that inserts new instructions at the end of the program.
  • InputMutator

    • A mutator that changes the input variables of instructions in a program.
  • InsertionMutator

    • A mutator that inserts new code at random positions in a program.
  • JITStressMutator

    • A mutator designed to call already JITed functions with different arguments or environment. In a way, this is a workaround for the fact that we don't have coverage feedback from JIT code.由于不能直接从JIT代码中获得覆盖率的反馈信息,因此使用不同参数和环境强制调用一些JIT后的函数。
  • OperationMutator

    • A mutator that randomly mutates parameters of the Operations in the given program.
  • SpliceMutator

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

推荐阅读更多精彩内容