Skip to content

Instantly share code, notes, and snippets.

@r0nn
Forked from ChiChou/Android.mk
Created July 17, 2017 05:32
Show Gist options
  • Save r0nn/7be3cb60f53de04e48dba92151a37bd3 to your computer and use it in GitHub Desktop.
Save r0nn/7be3cb60f53de04e48dba92151a37bd3 to your computer and use it in GitHub Desktop.

Revisions

  1. @ChiChou ChiChou revised this gist Jun 21, 2017. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion loader.c
    Original file line number Diff line number Diff line change
    @@ -39,7 +39,7 @@ void init_jvm(JavaVM **vm, JNIEnv **env)
    jint (*register_natives)(JNIEnv * env);
    jint (*register_natives_legacy)(JNIEnv * env, jclass clazz);
    jint result;
    JavaVMOption options[6];
    JavaVMOption options[7];
    JavaVMInitArgs args;

    char so_name[PROP_VALUE_MAX];
    @@ -162,6 +162,8 @@ void device_id(char *id, size_t len) {
    (*env)->DeleteLocalRef(env, hashmap);
    (*env)->DeleteLocalRef(env, key);
    (*env)->DeleteLocalRef(env, val);

    vm->DestroyJavaVM();
    }

    // does not work on certain devices
  2. @ChiChou ChiChou revised this gist May 23, 2017. 1 changed file with 11 additions and 0 deletions.
    11 changes: 11 additions & 0 deletions Android.mk
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)


    LOCAL_MODULE := loader
    LOCAL_MODULE_TAGS := optional
    LOCAL_SRC_FILES := loader.c
    LOCAL_CPPFLAGS := -std=gnu++0x -Wall
    LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -pie -fPIE
    LOCAL_LDFLAGS += -Wl,--export-dynamic
  3. @ChiChou ChiChou created this gist May 23, 2017.
    375 changes: 375 additions & 0 deletions loader.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,375 @@
    #include <dlfcn.h>
    #include <fcntl.h>
    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>

    #include <jni.h>

    #include <sys/system_properties.h>

    #include "loader.h"

    void my_assert(int condition, char *msg)
    {
    if (!condition) {
    printf("%s", msg);
    exit(-1);
    }
    }

    static uint get_system_api_level(void)
    {
    char sdk_version[PROP_VALUE_MAX];
    sdk_version[0] = '\0';
    __system_property_get("ro.build.version.sdk", sdk_version);
    return atoi(sdk_version);
    }

    void init_jvm(JavaVM **vm, JNIEnv **env)
    {
    if (!getenv("ANDROID_DATA"))
    putenv("ANDROID_DATA=/data");
    if (!getenv("ANDROID_ROOT"))
    putenv("ANDROID_ROOT=/system");

    void *vm_module, *runtime_module;
    jint (*create_java_vm)(JavaVM ** vm, JNIEnv ** env, void * vm_args);
    jint (*register_natives)(JNIEnv * env);
    jint (*register_natives_legacy)(JNIEnv * env, jclass clazz);
    jint result;
    JavaVMOption options[6];
    JavaVMInitArgs args;

    char so_name[PROP_VALUE_MAX];
    if ((__system_property_get("persist.sys.dalvik.vm.lib.2", so_name) <= 0) &&
    (__system_property_get("persist.sys.dalvik.vm.lib", so_name) <= 0)) {
    strcpy(so_name, "libdvm.so"); // fallback
    }

    vm_module = dlopen(so_name, RTLD_LAZY | RTLD_GLOBAL);
    my_assert(vm_module != NULL, "dvm module is null");
    runtime_module = dlopen ("libandroid_runtime.so", RTLD_LAZY | RTLD_GLOBAL);
    my_assert(runtime_module != NULL, "runtime module is null");
    create_java_vm = dlsym (vm_module, "JNI_CreateJavaVM");
    my_assert(create_java_vm != NULL, "unable to resolve JNI_CreateJavaVM");

    FILE *pipe = popen("pm path com.tencent.mm", "r");
    if (!pipe) {
    puts("have you installed wechat?");
    abort();
    }

    char wechat_path[MAX_PATH];
    fgets(wechat_path, MAX_PATH, pipe);
    if (strstr(wechat_path, "package:") != wechat_path) {
    puts("unable to retrive package info");
    abort();
    }
    char class_path[MAX_PATH];
    snprintf(class_path, MAX_PATH, "-Djava.class.path=%s", wechat_path + strlen("package:"));

    int i = 0;
    /* for debugging */
    options[i++].optionString = "-verbose:jni";
    options[i++].optionString = "-verbose:gc";
    options[i++].optionString = "-verbose:class";
    options[i++].optionString = "-Xdebug";
    /* for debugging */

    options[i++].optionString = "-Xcheck:jni";
    options[i++].optionString = class_path;

    args.version = JNI_VERSION_1_6;
    args.nOptions = i;
    args.options = options;
    args.ignoreUnrecognized = JNI_TRUE;
    result = create_java_vm(vm, env, &args);
    my_assert(result == JNI_OK, "unable to create jvm");

    register_natives = dlsym (runtime_module, "registerFrameworkNatives");
    if (register_natives != NULL)
    {
    result = register_natives(*env);
    my_assert(result == JNI_OK, "unable to register natives");
    }
    else
    {
    register_natives_legacy = dlsym(runtime_module,
    "Java_com_android_internal_util_WithFramework_registerNatives");
    my_assert(register_natives_legacy != NULL,
    "unable to resolve android.internal.util.WithFramework.registerNatives");

    result = register_natives_legacy(*env, NULL);
    my_assert(result == JNI_OK, "unable to register natives using legacy function");
    }
    }

    JNIEXPORT void InitializeSignalChain() {

    }

    JNIEXPORT void ClaimSignalChain() {

    }

    JNIEXPORT void UnclaimSignalChain() {

    }

    JNIEXPORT void InvokeUserSignalHandler() {

    }

    JNIEXPORT void EnsureFrontOfChain() {

    }

    void device_id(char *id, size_t len) {
    JavaVM *vm;
    JNIEnv *env;

    init_jvm(&vm, &env);

    jstring filename = (*env)->NewStringUTF(env, DATA_PATH"MicroMsg/CompatibleInfo.cfg");
    jclass clsFileInputStream = (*env)->FindClass(env, "java/io/FileInputStream");
    jclass clsObjectInputStream = (*env)->FindClass(env, "java/io/ObjectInputStream");
    jclass clsHashMap = (*env)->FindClass(env, "java/util/HashMap");

    jmethodID constructor = (*env)->GetMethodID(env, clsFileInputStream, "<init>", "(Ljava/lang/String;)V");
    jobject fileInputStream = (*env)->NewObject(env, clsFileInputStream, constructor, filename);

    constructor = (*env)->GetMethodID(env, clsObjectInputStream, "<init>", "(Ljava/io/InputStream;)V");
    jobject objInputStream = (*env)->NewObject(env, clsObjectInputStream, constructor, fileInputStream);
    jmethodID readObject = (*env)->GetMethodID(env, clsObjectInputStream, "readObject", "()Ljava/lang/Object;");
    jobject hashmap = (*env)->CallObjectMethod(env, objInputStream, readObject);

    // cast to hash map
    jmethodID get = (*env)->GetMethodID(env, clsHashMap, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
    jmethodID toString = (*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Object"), "toString", "()Ljava/lang/String;");

    jclass clsInteger = (*env)->FindClass(env, "java/lang/Integer");
    jmethodID valueOf = (*env)->GetStaticMethodID(env, clsInteger, "valueOf", "(I)Ljava/lang/Integer;");
    jobject key = (*env)->CallStaticObjectMethod(env, clsInteger, valueOf, 258);
    jstring val = (*env)->CallObjectMethod(env, hashmap, get, key);

    strncpy(id, (*env)->GetStringUTFChars(env, val, 0), len);

    (*env)->DeleteLocalRef(env, filename);
    (*env)->DeleteLocalRef(env, fileInputStream);
    (*env)->DeleteLocalRef(env, objInputStream);
    (*env)->DeleteLocalRef(env, hashmap);
    (*env)->DeleteLocalRef(env, key);
    (*env)->DeleteLocalRef(env, val);
    }

    // does not work on certain devices
    // int get_imei(char *imei)
    // {
    // FILE* pipe;
    // char line[MAX_PATH];
    // char stripped[MAX_PATH];
    // char *left, *right, *p;
    // size_t len, pos = 0;
    // pipe = popen("service call iphonesubinfo 1", "r");
    // if (!pipe) {
    // return UNABLE_TO_CALL_SERVICE;
    // }
    // while(!feof(pipe)) {
    // if (fgets(line, MAX_PATH, pipe)) {
    // left = strstr(line, "'");
    // if (!left) continue;
    // left++;
    // right = strstr(left, "'");
    // if (!right) continue;
    // for (p = left; p < right && pos < IMEI_LEN - 1; p++)
    // if (*p != '.')
    // imei[pos++] = *p;
    // }
    // }
    // imei[IMEI_LEN - 1] = '\0';

    // pclose(pipe);
    // return OK;
    // }

    int get_uin(char *uin, size_t len)
    {
    FILE *fp = fopen(DATA_PATH"shared_prefs/system_config_prefs.xml", "r");
    char line[MAX_PATH];
    char *p;
    size_t pos = 0;

    // todo: parse xml with Jni ???
    while(!feof(fp)) {
    if (fgets(line, MAX_PATH, fp)) {
    if (!strstr(line, "name=\"default_uin\"")) continue;
    for (p = strstr(line, "value=\""); *p; p++) {
    if (*p < '0' || *p > '9') continue;
    if (*p == '"') break; // end
    if (pos > len - 1) return INSUFFICIENT_BUF; // insufficient length
    uin[pos++] = *p;
    }
    }
    }
    return OK;
    }

    int md5_hex(const char *raw, char *output)
    {
    static const char hexchars[] = "0123456789abcdef";
    unsigned char digest[MD5_DIGEST_LENGTH];
    unsigned int pos = 0;
    void *lib = dlopen("libcrypto.so", 0);
    MD5INIT_FN_PTR MD5_Init = (MD5INIT_FN_PTR) dlsym(lib, "MD5_Init");
    MD5UPDATE_FN_PTR MD5_Update = (MD5UPDATE_FN_PTR) dlsym(lib, "MD5_Update");
    MD5FINAL_FN_PTR MD5_Final = (MD5FINAL_FN_PTR) dlsym(lib, "MD5_Final");

    MD5_CTX ctx;
    MD5_Init(&ctx);
    MD5_Update(&ctx, (unsigned char *)raw, strlen(raw));
    MD5_Final(digest, &ctx);

    for (unsigned i = 0; i < MD5_DIGEST_LENGTH; i++) {
    unsigned char b = digest[i];
    output[pos++] = hexchars[b >> 4];
    output[pos++] = hexchars[b & 0xF];
    }

    output[pos] = '\0';
    return 0;
    }

    int passwd(const char *imei, const char *uin, char *buf)
    {
    char md5[HEX_DIGEST_LENGTH];
    size_t len = IMEI_LEN + strlen(uin) + 1;
    char *joint = calloc(len, sizeof(char));
    snprintf(joint, len, "%s%s", imei, uin);
    printf("joint: %s\n", joint);
    md5_hex(joint, md5);
    strncpy(buf, md5, WECHAT_KEY_LEN);
    buf[8] = '\0';
    free(joint);
    return 0;
    }

    static int callback(void *data, int argc, char **argv, char **azColName){
    int i;
    char timestamp[DATE_LEN];
    char datetime[DATE_LEN];
    char *remark = NULL;

    for (i = 0; i < argc; i++) {
    char *col = azColName[i];
    char *val = argv[i];

    if (STREQ(col, "remark")) {
    remark = strlen(val) ? val : NULL;
    } else if (STREQ(col, "nickname")) {
    printf("%s ", remark ? remark : val);
    } else if (STREQ(col, "createTime")) {
    int slice = strlen(val) - 3; // divided by 1000 in decimal :)
    if (slice > DATE_LEN) {
    fprintf(stderr, "invalid time stamp");
    exit(-1);
    }

    strncpy(timestamp, val, slice);
    time_t ts = (time_t)atoi(timestamp);
    struct tm * timeinfo;
    timeinfo = localtime(&ts);
    strftime(datetime, DATE_LEN, "%Y-%m-%d %H:%M:%S", timeinfo);
    printf("%s\n", datetime);

    } else if (STREQ(col, "imgPath") && val) {
    printf("[图片]\n");
    } else if (STREQ(col, "content") && val) {
    printf("%s\n", val);
    }
    }
    // for(i = 0; i < argc; i++){
    // printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    // }
    printf("\n");
    return 0;
    }

    int query(const char *uin, const char *key)
    {
    void *lib = dlopen(DATA_PATH"lib/libmmdb.so", 0);
    char *zErrMsg = NULL;

    // todo: move to .init
    SQLITE3_KEY_FN_PTR sqlite3_key = dlsym(lib, "sqlite3_key");
    SQLITE3_OPEN_V2_FN_PTR sqlite3_open_v2 = dlsym(lib, "sqlite3_open_v2");
    SQLITE3_CLOSE_V2_FN_PTR sqlite3_close_v2 = dlsym(lib, "sqlite3_close_v2");
    SQLITE3_EXEC_FN_PTR sqlite3_exec = dlsym(lib, "sqlite3_exec");
    SQLITE3_FREE_FN_PTR sqlite3_free = dlsym(lib, "sqlite3_free");
    const char *(*sqlite3_errmsg)(sqlite3*) = dlsym(lib, "sqlite3_errmsg");

    char digest[HEX_DIGEST_LENGTH];
    size_t len = strlen(uin) + 2 + 1;
    char *mmuin = calloc(len, sizeof(char));
    snprintf(mmuin, len, "mm%s", uin);
    printf("%s\n", mmuin);
    md5_hex(mmuin, digest);

    char path[MAX_PATH];
    snprintf(path, MAX_PATH, DATA_PATH"MicroMsg/%s/EnMicroMsg.db", digest);
    printf(DATA_PATH"MicroMsg/%s\n", digest);

    printf("password: %s\n", key);
    printf("latest "RECORD_COUNT" message history\n");

    sqlite3* db;
    int rc = sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY, NULL);
    if (rc == SQLITE_OK) {
    printf("database is now open, status: %d\n", rc);
    sqlite3_key(db, key, WECHAT_KEY_LEN);
    sqlite3_exec(db, "PRAGMA cipher_use_hmac = OFF;"
    "PRAGMA cipher_page_size = 1024;"
    "PRAGMA kdf_iter = 4000;",
    NULL, NULL, &zErrMsg);

    char *sql = "SELECT rcontact.conRemark AS remark, rcontact.nickname AS nickname, "
    "message.createTime, message.content, message.imgPath FROM message "
    "LEFT JOIN rcontact ON rcontact.username = message.talker "
    "ORDER BY createTime DESC limit "RECORD_COUNT";"; // latest 200

    rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if ( rc != SQLITE_OK ) {
    fprintf(stderr, "SQL error: %s\n", zErrMsg);
    sqlite3_free(zErrMsg);
    } else {
    fprintf(stdout, "Message history dump has finished.\n");
    }
    } else {
    printf("error: %s\n", sqlite3_errmsg(db));
    }

    sqlite3_close_v2(db);
    free(mmuin);

    return OK;
    }

    int main (int argc, char *argv[])
    {
    char devid[MAX_PATH];
    char uin[MAX_PATH];

    device_id(devid, MAX_PATH);
    printf("devid: %s\n", devid);
    get_uin(uin, MAX_PATH);
    printf("uin: %s\n", uin);

    char pw[MAX_PATH];
    passwd(devid, uin, pw);
    printf("passwd: %s\n", pw);

    query(uin, pw);

    return 0;
    }
    85 changes: 85 additions & 0 deletions loader.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    #include <android/log.h>
    #include <stddef.h>

    #define PACKAGE "com.tencent.mm"
    #define DATA_PATH "/data/data/"PACKAGE"/"

    #define WECHAT_KEY_LEN 7
    #define MAX_PATH 256
    #define DATE_LEN 64
    #define RECORD_COUNT "200"

    #define STREQ(a, b) (strcmp((a), (b)) == 0)

    #define LOG_TAG "CHAITIN"
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
    #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

    #define OK 0
    #define UNABLE_TO_CALL_SERVICE -1
    #define INSUFFICIENT_BUF -2

    /* openssl */

    #if defined(__LP32__)
    #define MD5_LONG unsigned long
    #elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__)
    #define MD5_LONG unsigned long
    #define MD5_LONG_LOG2 3
    #else
    #define MD5_LONG unsigned int
    #endif

    #define MD5_CBLOCK 64
    #define MD5_LBLOCK (MD5_CBLOCK/4)
    #define MD5_DIGEST_LENGTH 16
    #define HEX_DIGEST_LENGTH MD5_DIGEST_LENGTH * 2 + 1

    typedef struct MD5state_st
    {
    MD5_LONG A,B,C,D;
    MD5_LONG Nl,Nh;
    MD5_LONG data[MD5_LBLOCK];
    unsigned int num;
    } MD5_CTX;

    typedef void (*MD5INIT_FN_PTR)(MD5_CTX *context);
    typedef void (*MD5UPDATE_FN_PTR)(MD5_CTX *context, unsigned char *input, unsigned int inlen);
    typedef void (*MD5FINAL_FN_PTR)(unsigned char *output, MD5_CTX *context);

    /* end of openssl */

    /* sqlcipher */

    #define SQLITE_OPEN_READONLY 0x00000001
    #define SQLITE_OK 0

    typedef void sqlite3; /* we don't care about what exatcly this struct is defined */

    typedef int(*SQLITE3_KEY_FN_PTR) (
    sqlite3 *db, /* Database to be rekeyed */
    const void *pKey, int nKey /* The key, and the length of the key in bytes */
    );

    typedef int (*SQLITE3_OPEN_V2_FN_PTR) (
    const char *filename, /* Database filename (UTF-8) */
    sqlite3 **ppDb, /* OUT: SQLite db handle */
    int flags, /* Flags */
    const char *zVfs /* Name of VFS module to use */
    );

    typedef int (*SQLITE3_CLOSE_V2_FN_PTR) (sqlite3*);

    typedef int (*SQLITE3_EXEC_FN_PTR) (
    sqlite3*, /* An open database */
    const char *sql, /* SQL to be evaluated */
    int (*callback)(void*,int,char**,char**), /* Callback function */
    void *, /* 1st argument to callback */
    char **errmsg /* Error msg written here */
    );

    typedef void (*SQLITE3_FREE_FN_PTR) (void*);

    /* end of sqlcipher */