iOS逆向 07:Hash算法

iOS 底层原理 + 逆向 文章汇总

本文主要介绍Hash算法

Hash介绍

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数

简单来说,hash算法(即散列函数),是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出。哈希函数的这种单向特征和输出数据长度固定的特征使得它可以生成消息或者数据。

Hash的特点

  • 算法是公开的

  • 相同数据运算,得到的结果是一样的(同样的数据,得到的结果是一样的)

  • 不同数据运算,得到的结果是定长的,如MD5得到的结果默认是128位,32个字符(16进制标识)。

  • 无法逆运算

  • 是信息摘要、信息“指纹”,是用来做数据识别的、完整性检查的。

Hash用途

  • 1、用户密码的加密
  • 2、搜索引擎
  • 3、版权
  • 4、数字签名
  • ......

数据传输/存储原则网络传输数据 + 本地保存数据 + 服务端保存数据等 ,这些是隐私数据,绝对不能明文,一定是密文传输/存储。

常见的Hash算法

常见的hash算法主要有MD5、SHA-1、SHA-256、SHA-512等

MD5

MD4(RFC 1320)是 MIT 的Ronald L. Rivest在 1990 年设计的,MD 是 Message Digest(消息摘要) 的缩写。它适用在32位字长的处理器上用高速软件实现——它是基于 32位操作数的位操作来实现的。

MD5(RFC 1321)是 Rivest 于1991年对MD4的改进版本。它对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。MD5比MD4来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。

Message Digest Algorithm MD5(消息摘要算法5)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。是计算机广泛使用的杂凑算法之一,将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。

特点

  • 1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。

  • 2、容易计算:从原数据计算出MD5值很容易。

  • 3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。

  • 4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。

应用场景

  • 一致性验证

  • 数字签名

  • 安全访问认证

SHA-1、SHA-256、SHA-512

安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。

SHA1是由NIST NSA设计为同DSA一起使用的,它对长度小于264的输入,产生长度为160bit的散列值,因此抗穷举(brute-force)性更好。SHA-1 设计时基于和MD4相同原理,并且模仿了该算法。

SHA-2 SHA-224、SHA-256、SHA-384,和SHA-512并称为SHA-2。 新的哈希函数并没有接受像SHA-1一样的公众密码社区做详细的检验,所以它们的密码安全性还不被大家广泛的信任。 虽然至今尚未出现对SHA-2有效的攻击,它的算法跟SHA-1基本上仍然相似;因此有些人开始发展其他替代的哈希算法。

注:这里只做简单介绍,并不展开详细说明,有兴趣的可以参考以下链接

密码加密

这里我们分别来对比以下四种,对密码的加密方式

  • 1、直接使用MD5(信息摘要算法)
  • 2、MD5加盐
  • 3、HMAC加密方案
  • 4、添点东西

准备工作

在进行演示前,首先封装常用的Hash算法

<!--h文件-->
#import <Foundation/Foundation.h>

@interface NSString (Hash)
    
#pragma mark - 散列函数
    /**
     *  计算MD5散列结果
     *
     *  终端测试命令:
     *  @code
     *  md5 -s "string"
     *  @endcode
     *
     *  <p>提示:随着 MD5 碰撞生成器的出现,MD5 算法不应被用于任何软件完整性检查或代码签名的用途。<p>
     *
     *  @return 32个字符的MD5散列字符串
     */
- (NSString *)md5String;
    
    /**
     *  计算SHA1散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl sha1
     *  @endcode
     *
     *  @return 40个字符的SHA1散列字符串
     */
- (NSString *)sha1String;
    
    /**
     *  计算SHA256散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl sha -sha256
     *  @endcode
     *
     *  @return 64个字符的SHA256散列字符串
     */
- (NSString *)sha256String;
    
    /**
     *  计算SHA 512散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl sha -sha512
     *  @endcode
     *
     *  @return 128个字符的SHA 512散列字符串
     */
- (NSString *)sha512String;
    
#pragma mark - HMAC 散列函数
    /**
     *  计算HMAC MD5散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl dgst -md5 -hmac "key"
     *  @endcode
     *
     *  @return 32个字符的HMAC MD5散列字符串
     */
- (NSString *)hmacMD5StringWithKey:(NSString *)key;
    
    /**
     *  计算HMAC SHA1散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl sha -sha1 -hmac "key"
     *  @endcode
     *
     *  @return 40个字符的HMAC SHA1散列字符串
     */
- (NSString *)hmacSHA1StringWithKey:(NSString *)key;
    
    /**
     *  计算HMAC SHA256散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl sha -sha256 -hmac "key"
     *  @endcode
     *
     *  @return 64个字符的HMAC SHA256散列字符串
     */
- (NSString *)hmacSHA256StringWithKey:(NSString *)key;
    
    /**
     *  计算HMAC SHA512散列结果
     *
     *  终端测试命令:
     *  @code
     *  echo -n "string" | openssl sha -sha512 -hmac "key"
     *  @endcode
     *
     *  @return 128个字符的HMAC SHA512散列字符串
     */
- (NSString *)hmacSHA512StringWithKey:(NSString *)key;
    
#pragma mark - 文件散列函数
    
    /**
     *  计算文件的MD5散列结果
     *
     *  终端测试命令:
     *  @code
     *  md5 file.dat
     *  @endcode
     *
     *  @return 32个字符的MD5散列字符串
     */
- (NSString *)fileMD5Hash;
    
    /**
     *  计算文件的SHA1散列结果
     *
     *  终端测试命令:
     *  @code
     *  openssl sha -sha1 file.dat
     *  @endcode
     *
     *  @return 40个字符的SHA1散列字符串
     */
- (NSString *)fileSHA1Hash;
    
    /**
     *  计算文件的SHA256散列结果
     *
     *  终端测试命令:
     *  @code
     *  openssl sha -sha256 file.dat
     *  @endcode
     *
     *  @return 64个字符的SHA256散列字符串
     */
- (NSString *)fileSHA256Hash;
    
    /**
     *  计算文件的SHA512散列结果
     *
     *  终端测试命令:
     *  @code
     *  openssl sha -sha512 file.dat
     *  @endcode
     *
     *  @return 128个字符的SHA512散列字符串
     */
- (NSString *)fileSHA512Hash;
@end

<!--m文件-->
#import "NSString+Hash.h"
#import <CommonCrypto/CommonCrypto.h>

@implementation NSString (Hash)
    
#pragma mark - 散列函数
- (NSString *)md5String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    
    CC_MD5(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
    
- (NSString *)sha1String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
    
    CC_SHA1(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
}
    
- (NSString *)sha256String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
    
    CC_SHA256(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
}
    
- (NSString *)sha512String {
    const char *str = self.UTF8String;
    uint8_t buffer[CC_SHA512_DIGEST_LENGTH];
    
    CC_SHA512(str, (CC_LONG)strlen(str), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
}
    
#pragma mark - HMAC 散列函数
- (NSString *)hmacMD5StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    
    CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
    
- (NSString *)hmacSHA1StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
    
    CCHmac(kCCHmacAlgSHA1, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
}
    
- (NSString *)hmacSHA256StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
    
    CCHmac(kCCHmacAlgSHA256, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
}
    
- (NSString *)hmacSHA512StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    uint8_t buffer[CC_SHA512_DIGEST_LENGTH];
    
    CCHmac(kCCHmacAlgSHA512, keyData, strlen(keyData), strData, strlen(strData), buffer);
    
    return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
}
    
#pragma mark - 文件散列函数
    
#define FileHashDefaultChunkSizeForReadingData 4096
    
- (NSString *)fileMD5Hash {
    NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self];
    if (fp == nil) {
        return nil;
    }
    
    CC_MD5_CTX hashCtx;
    CC_MD5_Init(&hashCtx);
    
    while (YES) {
        @autoreleasepool {
            NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData];
            
            CC_MD5_Update(&hashCtx, data.bytes, (CC_LONG)data.length);
            
            if (data.length == 0) {
                break;
            }
        }
    }
    [fp closeFile];
    
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    CC_MD5_Final(buffer, &hashCtx);
    
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
}
    
- (NSString *)fileSHA1Hash {
    NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self];
    if (fp == nil) {
        return nil;
    }
    
    CC_SHA1_CTX hashCtx;
    CC_SHA1_Init(&hashCtx);
    
    while (YES) {
        @autoreleasepool {
            NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData];
            
            CC_SHA1_Update(&hashCtx, data.bytes, (CC_LONG)data.length);
            
            if (data.length == 0) {
                break;
            }
        }
    }
    [fp closeFile];
    
    uint8_t buffer[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1_Final(buffer, &hashCtx);
    
    return [self stringFromBytes:buffer length:CC_SHA1_DIGEST_LENGTH];
}
    
- (NSString *)fileSHA256Hash {
    NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self];
    if (fp == nil) {
        return nil;
    }
    
    CC_SHA256_CTX hashCtx;
    CC_SHA256_Init(&hashCtx);
    
    while (YES) {
        @autoreleasepool {
            NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData];
            
            CC_SHA256_Update(&hashCtx, data.bytes, (CC_LONG)data.length);
            
            if (data.length == 0) {
                break;
            }
        }
    }
    [fp closeFile];
    
    uint8_t buffer[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256_Final(buffer, &hashCtx);
    
    return [self stringFromBytes:buffer length:CC_SHA256_DIGEST_LENGTH];
}
    
- (NSString *)fileSHA512Hash {
    NSFileHandle *fp = [NSFileHandle fileHandleForReadingAtPath:self];
    if (fp == nil) {
        return nil;
    }
    
    CC_SHA512_CTX hashCtx;
    CC_SHA512_Init(&hashCtx);
    
    while (YES) {
        @autoreleasepool {
            NSData *data = [fp readDataOfLength:FileHashDefaultChunkSizeForReadingData];
            
            CC_SHA512_Update(&hashCtx, data.bytes, (CC_LONG)data.length);
            
            if (data.length == 0) {
                break;
            }
        }
    }
    [fp closeFile];
    
    uint8_t buffer[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512_Final(buffer, &hashCtx);
    
    return [self stringFromBytes:buffer length:CC_SHA512_DIGEST_LENGTH];
}
    
#pragma mark - 助手方法
    /**
     *  返回二进制 Bytes 流的字符串表示形式
     *
     *  @param bytes  二进制 Bytes 数组
     *  @param length 数组长度
     *
     *  @return 字符串表示形式
     */
- (NSString *)stringFromBytes:(uint8_t *)bytes length:(int)length {
    NSMutableString *strM = [NSMutableString string];
    
    for (int i = 0; i < length; i++) {
        [strM appendFormat:@"%02x", bytes[i]];
    }
    
    return [strM copy];
}
@end

方式一:直接使用MD5

终端演示

  • md5 -s "123456"

  • MD5 ("123456") ,结果为 e10adc3949ba59abbe56e057f20f883e

代码演示

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //传递数据
    pwd = pwd.md5String;
    NSLog(@"pwd - %@", pwd);
}

<!--运行结果-->
 pwd - e10adc3949ba59abbe56e057f20f883e

结论:直接使用MD5不安全,大部分MD5现在通过网站都可以还原

方式二:MD5加固定盐

在方式一的基础上,加上一个固定的字符串,代码演示如下

//加盐
static NSString *salt = @"LKSJDFLKJ";

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //传递数据
    pwd = [pwd stringByAppendingString:salt].md5String;
    NSLog(@"pwd - %@", pwd);
}

<!--运行结果-->
pwd - 2cac01acb03a3cc2b96cee3aae4d2276

结论:加固定盐,现在也不安全,因为如果盐泄漏了,也会有数据泄漏的风险

方式三:HMAC加密

HMAC加密,其原理就是判断是否有key,这个key是由服务器动态提供的,保存在手机侧。简单来说,就相当于现在的授权设备,HMAC主要就是检测你的设备是否有授权。其代码演示如下

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //传递数据
    //key是动态的,由服务器提供(即一个账号一个key)
    pwd = [pwd hmacMD5StringWithKey:@"CJL"];
    NSLog(@"pwd - %@", pwd);
}

<!--打印结果-->
pwd - b123975de0f19edef5a546bf212bd918

结论:现在这种加密方式,用户密码确实安全了。但是用于服务器登录验证是一个hash值,而不是密码。所以这个hash值也有被拦截的风险。也是不安全的

方式四:HMAC + 时间戳

基于方式三的基础上,我们载增加一些东西一起加密,这个增加的就是时间戳。对于密码来说,加密方式还是采用HMAC加密。只是验证方式发生了变化。原本只是验证hash值即可。现在是在hash值的基础上加上了一个时间期限。例如hash值的有效时间最多是2分钟,超过这个时间就失效了。类似于现在验证码的验证。

主要流程如下:

  • 1、客户端:对密码使用HMAC加密,得到一个hash值A,hash值带上服务器给的时间戳,求得另一个hash值B
  • 2、将客户端得到的Hash值AHash值B发送到服务器进行验证
  • 3、服务器:将传过来的Hash值A,带上服务器的时间戳,得到一个Hash值C,判断Hash值C是否等于传过来的Hash值B,如果不等于,则表示不通过,在时间范围内,再继续验证上一分钟

举例说明

假设现在用户的密码是:123456,

  • 1、客户端:
    • 使用HMAC加密得到的Hash值A = b123975de0f19edef5a546bf212bd918
    • HMAC哈希值 + 时间戳(假设是2021-4-10 17:15:10) 得到的hash值B = 430a60f4989e104e9e903dbdcb84c69a
  • 2、发送到服务器的Hash值
    • Hash值Ab123975de0f19edef5a546bf212bd918
    • hash值B430a60f4989e104e9e903dbdcb84c69a
  • 3、服务端:
    • HMAC哈希值 + 服务器时间戳(假设是2021-4-10 17:15:11) 得到的hash值C = 0cf1dceda0fd58aefd4c7a7b2b08e193
    • 与客户端传入的hash值C不等,继续求HMAC带上上一个时间(2分钟内)的 hash值D = 430a60f4989e104e9e903dbdcb84c69a.与hash值B相等,所以验证通过

例子中所用的演示代码如下

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSString *pwd = @"123456";
    //传递数据
    pwd = [pwd hmacMD5StringWithKey:@"CJL"];//key是动态的,由服务器提供
    NSLog(@"pwd - %@", pwd);
    pwd = [NSString stringWithFormat:@"%@%@",pwd, [self getOtherTimeStrWithString:@"2021-4-10 17:15:10"]].md5String;
    NSLog(@"pwd - %@", pwd);
}


- (NSString *)getOtherTimeStrWithString:(NSString *)formatTime{
    NSLog(@"formatTime - - - - - -%@",formatTime);
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterMediumStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; //(@"YYYY-MM-dd hh:mm:ss") ----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
    //设置时区选择北京时间
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
    [formatter setTimeZone:timeZone];
    NSDate* date = [formatter dateFromString:formatTime]; //------------将字符串按formatter转成nsdate
    //时间转时间戳的方法:
    NSInteger timeSp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue] * 1000;
    NSLog(@"将某个时间转化成 时间戳timeSp:%ld",(long)timeSp); //时间戳的值
    return [NSString stringWithFormat:@"%ld",(long)timeSp];
}

数字签名

目的:验证二进制数据是不是颁发机构颁发的

早期只是给一个数据,扔给我,我就用,这是不安全的

  • 1、数据报文,即原始数据


    早期

后来的数字签名,为了安全,不仅给数据,还给数据的Hash值,但是还是不安全,因为数据、hash值都有可能同时篡改

  • 1、数据报文,即原始数据

  • 2、原始数据的Hash值


    后来

现在的数字签名,除了给数据,同时还给原始数据Hash值进行RSA加密后得到的hash值,其中原始数据HASH值RSA加密后得到的值称为原始数据的 数字签名

  • 1、数据报文,即原始数据

  • 2、原始数据的Hash值

  • 3、数字签名:原始数据Hash值通过RSA加密后得到的结果


    现在

总结

  • HASH算法(散列函数)
    • 不可逆运算(因为无限的数据,存在有限的表示形式,所以会出现 多个不同的数据,有同样的hash值)

    • 相同的数据结果相同

    • 不同的数据长度相同

    • 一般用于做数据的识别,例如密码、版权、百度云数据识别

  • 密码加密
    • md5直接加密,可被查询,容易破解

    • 加固定盐(固定的盐有安全隐患)

    • HMAC:比较好的方案,因为破解的成本 大于 破解后的获益,所以相对安全

    • HASH + 时间戳:这样的方式,每次加密结果不一样,因为受时间的影响比较大

  • 数字签名
    • 算法:RSA + HASH

    • 目的:验证数据的完整性,不被篡改

    • 主要逻辑:

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

推荐阅读更多精彩内容