使用NDK方式调用jni将密码保存到so文件中

要使用NDK首先要了解NDK到底是什么?

(英语:native development kit,简称NDK)是一种基于原生程序接口的软件开发工具。通过此工具开发的程序直接以本地语言运行,而非虚拟机。因此只有java等基于虚拟机运行的语言的程序才会有原生开发工具包。[维基百科]

NDK是一系列工具的集合

NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的.

NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。

NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。

为什么要使用?

1、代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。能够阻挡一定的开发者进行逆向。

2、可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。

3、提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。

4、便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。

NDK和CMake 的下载和安装

打开android studio 找到Sdk管理器,进行下载安装即可。

下面开发正式开发:

1、创建jni文件夹用来存放调用C++相关的文件,有两个配置文件和头文件以及源文件

配置文件:Android.mk

作用:命令行cd到src/main/jni文件夹下,使用命令ndk-build生成.so文件,这里边定义了生成.so库的文件名、指定编译的c++源文件和头文件,用于向构建系统描述源文件和共享库,让ndk去按照指定的方式编译c++源文件和头文件。

编译目标目录声明

LOCAL_PATH:=$(call my-dir)   LOCAL_PATH需要编译源文件所在的目录,$(call my-dir)使用宏定义,my-dir是返回值对应Android.mk所在的目录。

重置全局变量

include $(CLEAR_VARS)          CLEAR_VARS变量指向特殊 GNU Makefile, 可为您清除许多LOCAL_XXX变量.不包括LOCAL_PATH因为系统在单一 GNU Make 执行环境(其中所有变量都是全局的)中解析所有构建控制文件. 在描述每个模块之前, 必须声明(重新声明)此变量

LOCAL_MODULE:=secretkey 指定模块的名称

LOCAL_MODULE, 指定模块的名称,唯一且不含空格, 之后会编译出librecorder-jni.so, 如果模块名已包含前缀lib, 则不会自动添加lib前缀.

LOCAL_SRC_FILES:=secretkey.cpp secretkey.h 指定编译c++源文件和头文件

待编译的源文件

LOCAL_SRC_FILES,指定源文件列表, 多个文件使用空格分割.可以使用相对文件路径(指向 LOCAL_PATH)和绝对文件路径

LOCAL_LDLIBS :=-llog 打印log

include $(BUILD_SHARED_LIBRARY)整合

BUILD_SHARED_LIBRARY变量指向GNU Makefile脚本, 用于收集您自最近 include 后在 LOCAL_XXX 变量中定义的所有信息.其实就是让上一次include到这里之间的内容生效

编译模块输出名称

LOCAL_MODULE_FILENAME, 真正的库输出文件名. 如果不喜欢系统自动生成的文件名, 可以指定这个值

LOCAL_MODULE := foo

LOCAL_MODULE_FILENAME := libnewfoo

Application.mk

此文件枚举并描述您的应用需要的模块。Android.mk有效的前提是依靠该文件的保证位于jni的目录下.包含下面几个方面的内容:

用于针对特定平台进行编译的 ABI。

工具链。

要包含的标准库(静态和动态 STLport 或默认系统)。

APP_STL := stlport_static 静态标准库

Android NDK 默认使用的是最小支持的C++运行库,如果你需要你的NDK程序中使用STL,则可以设置APP_STL := stlport_static,APP_STL有表二中的几种取值。

NameExplanation

system(default)系统默认的C++运行库

stlport_static以静态链接方式使用的sttport版本的STL

stlport_shared以动态链接方式使用的sttport版本的STL

gnustl_static以静态链接方式使用的gnustl版本的STL

gnustl_shared以动态链接方式使用的gnustl版本的STL

gabi++_static以静态链接方式使用的gabi++

gabi++_shared以动态链接方式使用的gabi++

c++_static以静态链接方式使用的LLVM libc++

c++_shared以动态链接方式使用的LLVM libc++

表二:NDK运行库

若APK中有多个SO文件用到STL,建议都使用动态方式链接STL,这样可以减小整个APK文件大小。

另外需要注意的是官方提供的NDK运行库除了默认的以外都支持RTTI和异常,然而默认是禁用的,将在下面的Android.mk中说明如何开启。

APP_OPTIM(编译模式)

“release”模式为默认的,生成的是优化后的二进制;也可以设置为“debug”模式,“debug”模式生成的是未优化二进制,提供很多BUG信息,便于调试和分析

APP_PLATFORM

指定当前程序支持android最低api水平,如APPP_PLATFORM:=16 最低支持到api16

编码实战

这里以我项目中使用ndk生成密匙一个例子进行讲解,实际项目中肯定会用到加密功能,有一些敏感的数据如用户的密码、账号等,需要将这些信息保存到本地一旦数据保存到磁盘上,如果不进行加密很有可能泄漏,造成不良的后果。

通过生成.so文件生成密匙,在一定程度上降低被盗风险,毕竟这里边设计到底层的东西比较多,安全性比硬编码之类的高了好几个档次。

1、所有文件存放的位置截图

jni存放位置

2、Android.mk配置详细信息

LOCAL_PATH:=$(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE:=secretkey

LOCAL_SRC_FILES:=secretkey.cpp secretkey.h

LOCAL_LDLIBS :=-llog

include $(BUILD_SHARED_LIBRARY)

3、Application.mk配置详细信息

APP_STL := stlport_static

APP_ABI := all

#app支持最小api level

APP_PLATFORM:= android-16

APP_OPTIM  := release

4、secretkey.h

#include

#defineUTF_8"UTF-8"

#ifdef__cplusplus

extern"C"{

//com.mine.cui.zxandroidlib.jni.com.mine.cui.zxandroidlib.security.SecretKeyHelper.createSecretKey

jstring

Java_com_test_lib_security_SecretKeyHelper_createSecretKey(JNIEnv*,jobject,jobject);

}

#endif

方法格式:jstring Java+全类名 (其中.用下划线分隔开)+java定义native方法的类名和native方法名

5、secretkey.app

#include

#include

#include"secretkey.h"

#include

#include

#defineLOG_TAG"robin_jni"// 自定义的LOG的标识

#defineLOGOPEN1//日志开关,1为开,其它为关

#defineLOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

#defineLOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

#defineLOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)

#defineLOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)

#defineLOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)

//char* to jstring

jstringtoString(JNIEnv* env,jbyteArraybyteArray) {

jclassstring_cls = env->FindClass("java/lang/String");

jmethodIDnew_string_mid = env->GetMethodID(string_cls,"",

"([BLjava/lang/String;)V");

return reinterpret_cast(env->NewObject(string_cls, new_string_mid,

byteArray, env->NewStringUTF(UTF_8)));

}

jbyteArraytoBytes(JNIEnv* env,const char* bytes) {

jclassstring_cls = env->FindClass("java/lang/String");

jmethodIDget_bytes_mid = env->GetMethodID(string_cls,"getBytes",

"(Ljava/lang/String;)[B");

return reinterpret_cast(env->CallObjectMethod(

env->NewStringUTF(bytes), get_bytes_mid, env->NewStringUTF(UTF_8)));

}

jbyteArraytoBytes(JNIEnv* env,jstringstring) {

jclassstring_cls = env->FindClass("java/lang/String");

jmethodIDget_bytes_mid = env->GetMethodID(string_cls,"getBytes",

"(Ljava/lang/String;)[B");

return reinterpret_cast(env->CallObjectMethod(string,

get_bytes_mid, env->NewStringUTF(UTF_8)));

}

jbyteArraygetDigestedBytes(JNIEnv* env,jbyteArraycomplex_bytes) {

staticjobjectsatic_message_digest_obj = __null;

jclassmessage_digest_cls = env->FindClass("java/security/MessageDigest");

jmethodIDget_instance_mid = env->GetStaticMethodID(message_digest_cls,

"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;");

if(satic_message_digest_obj == __null) {

jobjectlocal_message_digest_obj = env->CallStaticObjectMethod(

message_digest_cls, get_instance_mid, env->NewStringUTF("MD5"));

satic_message_digest_obj = env->NewGlobalRef(local_message_digest_obj);

env->DeleteLocalRef(local_message_digest_obj);

}

jmethodIDdigest_mid = env->GetMethodID(message_digest_cls,"digest",

"([B)[B");

env->DeleteLocalRef(message_digest_cls);

return reinterpret_cast(env->CallObjectMethod(

satic_message_digest_obj, digest_mid, complex_bytes));

}

jstringtoHex(JNIEnv* env,jbyteArraydigested_bytes) {

jclassbig_integer_cls = env->FindClass("java/math/BigInteger");

jmethodIDnew_big_integer_mid = env->GetMethodID(big_integer_cls,"",

"(I[B)V");

jobjectbig_integer_obj = env->NewObject(big_integer_cls,

new_big_integer_mid,1, digested_bytes);

env->DeleteLocalRef(digested_bytes);

jmethodIDto_String_mid = env->GetMethodID(big_integer_cls,"toString",

"(I)Ljava/lang/String;");

env->DeleteLocalRef(big_integer_cls);

return reinterpret_cast(env->CallObjectMethod(big_integer_obj,

to_String_mid,16));

}

jstringgetMD5(JNIEnv* env,jstringjInfo) {

jbyteArraydigested_bytes = getDigestedBytes(env, toBytes(env, jInfo));

returntoHex(env, digested_bytes);

}

jstringgetAppendedString(JNIEnv* env,jobjectthiz,jstrings1,jstrings2) {

const char*s1x = (env)->GetStringUTFChars(s1,NULL);

const char*s2x = (env)->GetStringUTFChars(s2,NULL);

char*sall =new char[strlen(s1x) + strlen(s2x) +1];

strcpy(sall, s1x);

strcat(sall, s2x);

jstringretval = (env)->NewStringUTF(sall);

(env)->ReleaseStringUTFChars(s1, s1x);

(env)->ReleaseStringUTFChars(s2, s2x);

free(sall);

returnretval;

}

jobjectgetInstance(JNIEnv* env,jclassobj_class) {

jmethodIDconstruction_id = env->GetMethodID(obj_class,"","()V");

jobjectobj = env->NewObject(obj_class, construction_id);

returnobj;

}

//获取deviceid

jstringgetDeviceID(JNIEnv*env,jobjectthiz,jobjectmContext) {

jclasscls_context = (env)->FindClass("android/content/Context");

if(cls_context ==0) {

return(env)->NewStringUTF("unknown");

}

jmethodIDgetSystemService = (env)->GetMethodID(cls_context,

"getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");

if(getSystemService ==0) {

return(env)->NewStringUTF("unknown");

}

jfieldIDTELEPHONY_SERVICE = (env)->GetStaticFieldID(cls_context,

"TELEPHONY_SERVICE","Ljava/lang/String;");

if(TELEPHONY_SERVICE ==0) {

return(env)->NewStringUTF("unknown");

}

jobjectstr = (env)->GetStaticObjectField(cls_context, TELEPHONY_SERVICE);

jobjecttelephonymanager = (env)->CallObjectMethod(mContext,

getSystemService, str);

if(telephonymanager ==0) {

return(env)->NewStringUTF("unknown");

}

jclasscls_tm = (env)->FindClass("android/telephony/TelephonyManager");

if(cls_tm ==0) {

return(env)->NewStringUTF("unknown");

}

jmethodIDgetDeviceId = (env)->GetMethodID(cls_tm,"getDeviceId",

"()Ljava/lang/String;");

if(getDeviceId ==0) {

return(env)->NewStringUTF("unknown");

}

jobjectdeviceid = (env)->CallObjectMethod(telephonymanager, getDeviceId);

return(jstring) deviceid;

}

//获取SerialNumber

jstringgetSerialNumber(JNIEnv*env,jobjectthiz,jobjectmContext) {

jclasscls_tm = (env)->FindClass("android/os/SystemProperties");

if(cls_tm ==0) {

return(env)->NewStringUTF("unknown");

}

jmethodIDgetDeviceId = (env)->GetStaticMethodID(cls_tm,"get",

"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");

if(getDeviceId ==0) {

return(env)->NewStringUTF("unknown");

}

jstringparam1 = (env)->NewStringUTF("ro.serialno");

jstringparam2 = (env)->NewStringUTF("unknown");

jobjectdeviceid = (env)->CallStaticObjectMethod(cls_tm, getDeviceId,

param1, param2);

return(jstring) deviceid;

}

jstringjint2jstring(JNIEnv*env,jintfirst) {

charbuf[64];// assumed large enough to cope with result

sprintf(buf,"%d", first);// error checking omitted

returnenv->NewStringUTF( buf);

}

//获取公钥

jstringgetPublicKey(JNIEnv* env,jobjectthiz,jobjectcontext) {

jclasscontext_cls = env->GetObjectClass(context);

jmethodIDget_package_manager_mid = env->GetMethodID(context_cls,

"getPackageManager","()Landroid/content/pm/PackageManager;");

jmethodIDget_package_name_mid = env->GetMethodID(context_cls,

"getPackageName","()Ljava/lang/String;");

env->DeleteLocalRef(context_cls);

jobjectpm_obj = env->CallObjectMethod(context, get_package_manager_mid);

jclasspm_cls = env->FindClass("android/content/pm/PackageManager");

jmethodIDget_package_info_mid = env->GetMethodID(pm_cls,"getPackageInfo",

"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

jstringpackage_name =reinterpret_cast(env->CallObjectMethod(

context, get_package_name_mid));

jfieldIDflag_fid = env->GetStaticFieldID(pm_cls,"GET_SIGNATURES","I");

jintflag = env->GetStaticIntField(pm_cls, flag_fid);

env->DeleteLocalRef(pm_cls);

jobjectpi_obj = env->CallObjectMethod(pm_obj, get_package_info_mid,

package_name, flag);

env->DeleteLocalRef(package_name);

jclasspi_cls = env->FindClass("android/content/pm/PackageInfo");

jfieldIDsignatures_fid = env->GetFieldID(pi_cls,"signatures",

"[Landroid/content/pm/Signature;");

env->DeleteLocalRef(pi_cls);

jobjectsig_obj = env->GetObjectField(pi_obj, signatures_fid);

env->DeleteLocalRef(pi_obj);

jobjectArraysigs =reinterpret_cast(sig_obj);

jclasssignature_cls = env->FindClass("android/content/pm/Signature");

jmethodIDto_byte_array_mid = env->GetMethodID(signature_cls,"toByteArray",

"()[B");

jbyteArraysig_bytes =reinterpret_cast(env->CallObjectMethod(

env->GetObjectArrayElement(sigs,0), to_byte_array_mid));

jclasscertificate_factory_cls = env->FindClass(

"java/security/cert/CertificateFactory");

jmethodIDget_certificate_instance_mid = env->GetStaticMethodID(

certificate_factory_cls,"getInstance",

"(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");

jobjectcertificate_factory_obj = env->CallStaticObjectMethod(

certificate_factory_cls, get_certificate_instance_mid,

env->NewStringUTF("X509"));

jmethodIDgenerate_certificate_mid = env->GetMethodID(

certificate_factory_cls,"generateCertificate",

"(Ljava/io/InputStream;)Ljava/security/cert/Certificate;");

env->DeleteLocalRef(certificate_factory_cls);

jclasscertificate_cls = env->FindClass("java/security/cert/Certificate");

jclassbyte_input_stream_cls = env->FindClass(

"java/io/ByteArrayInputStream");

jmethodIDnew_sig_bytes_is_mid = env->GetMethodID(byte_input_stream_cls,

"","([B)V");

jobjectsig_bytes_is = env->NewObject(byte_input_stream_cls,

new_sig_bytes_is_mid, sig_bytes);

env->DeleteLocalRef(sig_bytes);

env->DeleteLocalRef(byte_input_stream_cls);

jobjectcert = env->CallObjectMethod(certificate_factory_obj,

generate_certificate_mid, sig_bytes_is);

env->DeleteLocalRef(sig_bytes_is);

env->DeleteLocalRef(certificate_factory_obj);

jmethodIDget_pubic_key_mid = env->GetMethodID(certificate_cls,

"getPublicKey","()Ljava/security/PublicKey;");

env->DeleteLocalRef(certificate_cls);

jobjectpublicKey  = env->CallObjectMethod(cert, get_pubic_key_mid);

jclasspublicKey_cls = env->GetObjectClass(publicKey);

jmethodIDtoString_mid = env->GetMethodID(publicKey_cls,"toString","()Ljava/lang/String;");

jstringpublicKey_str =static_cast(env->CallObjectMethod(publicKey,toString_mid));

env->DeleteLocalRef(cert);

env->DeleteLocalRef(publicKey_cls);

env->DeleteLocalRef(publicKey);

jclassstring_cls = env->GetObjectClass(publicKey_str);

jmethodIDindexOf_mid = env->GetMethodID(string_cls,"indexOf","(Ljava/lang/String;)I");

jstringparam = env->NewStringUTF("modulus");

jintaa = env->CallIntMethod(publicKey_str,indexOf_mid,param);

jstringparam2 = env->NewStringUTF("publicExponent");

jintbb = env->CallIntMethod(publicKey_str,indexOf_mid,param2);

jmethodIDsubstring_mid = env->GetMethodID(string_cls,"substring","(II)Ljava/lang/String;");

jstringpublicKey2_str =static_cast(env->CallObjectMethod(publicKey_str,substring_mid,aa+8,bb-1));

returnpublicKey2_str;

}

//获取签名

jstringgetSignatures(JNIEnv* env,jobjectthizz,

jobjectthiz) {

jclassnative_clazz = env->GetObjectClass(thiz);

// 得到 getPackageManager 方法的 ID

jmethodIDmethodID_func = env->GetMethodID(native_clazz,

"getPackageManager","()Landroid/content/pm/PackageManager;");

// 获得应用包的管理器

jobjectpackage_manager = env->CallObjectMethod(thiz, methodID_func);

// 获得 PackageManager 类

jclasspm_clazz = env->GetObjectClass(package_manager);

// 得到 getPackageInfo 方法的 ID

jmethodIDmethodID_pm = env->GetMethodID(pm_clazz,"getPackageInfo",

"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

//获取包名

jmethodIDmethodID_packagename = env->GetMethodID(native_clazz,

"getPackageName","()Ljava/lang/String;");

jstringname_str =static_cast(env->CallObjectMethod(thiz,

methodID_packagename));

// 获得应用包的信息

jobjectpackage_info = env->CallObjectMethod(package_manager, methodID_pm,

name_str,64);//env->NewStringUTF("com.example.contasdf")

// 获得 PackageInfo 类

jclasspi_clazz = env->GetObjectClass(package_info);

// 获得签名数组属性的 ID

jfieldIDfieldID_signatures = env->GetFieldID(pi_clazz,"signatures",

"[Landroid/content/pm/Signature;");

// 得到签名数组,待修改

jobjectsignatur = env->GetObjectField(package_info, fieldID_signatures);

jobjectArraysignatures =reinterpret_cast(signatur);

// 得到签名

jobjectsignature = env->GetObjectArrayElement(signatures,0);

// 获得 Signature 类,待修改

jclasss_clazz = env->GetObjectClass(signature);

// 得到 hashCode 方法的 ID

jmethodIDmethodID_hc = env->GetMethodID(s_clazz,"hashCode","()I");

// 获得应用包的管理器,待修改

inthash_code = env->CallIntMethod(signature, methodID_hc);

charstr[100];

sprintf(str,"%u", hash_code);

jstringsign = env->NewStringUTF(str);

returnsign;

}

jstringgetPackageName(JNIEnv* env,jobjectthizz,jobjectthiz) {

jclassnative_clazz = env->GetObjectClass(thiz);

// 得到 getPackageManager 方法的 ID

jmethodIDmethodID_func = env->GetMethodID(native_clazz,

"getPackageManager","()Landroid/content/pm/PackageManager;");

// 获得应用包的管理器

jobjectpackage_manager = env->CallObjectMethod(thiz, methodID_func);

// 获得 PackageManager 类

jclasspm_clazz = env->GetObjectClass(package_manager);

// 得到 getPackageInfo 方法的 ID

jmethodIDmethodID_pm = env->GetMethodID(pm_clazz,"getPackageInfo",

"(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

//获取包名

jmethodIDmethodID_packagename = env->GetMethodID(native_clazz,

"getPackageName","()Ljava/lang/String;");

jstringname_str =static_cast(env->CallObjectMethod(thiz,

methodID_packagename));

returnname_str;

}

char* jstringTostring(JNIEnv* env,jstringjstr)

{

char* rtn =NULL;

jclassclsstring = env->FindClass("java/lang/String");

jstringstrencode = env->NewStringUTF("utf-8");

jmethodIDmid = env->GetMethodID(clsstring,"getBytes","(Ljava/lang/String;)[B");

jbyteArraybarr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);

jsizealen = env->GetArrayLength(barr);

jbyte* ba = env->GetByteArrayElements(barr,JNI_FALSE);

if(alen >0)

{

rtn = (char*)malloc((size_t) (alen +1));

memcpy(rtn, ba, (size_t) alen);

rtn[alen] =0;

}

env->ReleaseByteArrayElements(barr, ba,0);

returnrtn;

}

jstring Java_com_test_lib_security_SecretKeyHelper_createSecretKey(JNIEnv* env,jobjectthizz,

jobjectthiz) {

jstringimei = getAppendedString(env, thizz, getDeviceID(env, thizz, thiz),getSerialNumber(env, thizz, thiz));

if(LOGOPEN==1){

LOGD("imei = %s",jstringTostring(env,imei));

}

jstringsign = getPublicKey(env, thizz, thiz);

if(LOGOPEN==1){

LOGD("sign = %s",jstringTostring(env,sign));

}

jstringimei_sign = getAppendedString(env, thizz, imei, sign);

if(LOGOPEN==1){

LOGD("imei_sign = %s",jstringTostring(env,imei_sign));

}

jstringpackage = getPackageName(env, thizz, thiz);

if(LOGOPEN==1){

LOGD("package = %s",jstringTostring(env,package));

}

jstringimei_sign_package = getAppendedString(env, thizz, imei_sign,package);

if(LOGOPEN==1){

LOGD("imei_sign_package = %s",jstringTostring(env,imei_sign_package));

}

//请再加入自己的移位或替换 或其他加密算法,例如我又append了一次imei

imei_sign_package = getAppendedString(env, thizz, imei_sign_package, imei);

if(LOGOPEN==1){

LOGD("imei_sign_package2 = %s",jstringTostring(env,imei_sign_package));

}

imei_sign_package = getAppendedString(env, thizz, imei_sign_package, sign);

if(LOGOPEN==1){

LOGD("imei_sign_package3 = %s",jstringTostring(env,imei_sign_package));

}

jstringsecretKey = getMD5(env, imei_sign_package);

if(LOGOPEN==1){

LOGD("secretKey = %s",jstringTostring(env,secretKey));

}

returnsecretKey;

}

6、、

public class SecretKeyHelper {

static{

System.loadLibrary("secretkey");

}

private staticStringmKey;

public staticString getSecretKey(Context context) {

if(mKey==null) {

mKey=createSecretKey(context);

}

returnmKey;

}

public static nativeString createSecretKey(Context context);

}

8、

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

推荐阅读更多精彩内容