数据恢复软件图

中国硬盘基地

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5495|回复: 17
打印 上一主题 下一主题

手机数据恢复 导出微信聊天记录

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2015-1-8 09:27:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
手机恢复微信聊天记录数据,首先要考虑找到微信聊天数据库,恢复数据库后导出数据,详细描述了导出微信聊天记录的方法,镜像手机emmc存储芯片或者通过数据线读取到数据本文没有描述。

首先,很容易发现数据库在/data/data/com.tencent.mm/MicroMsg/<some id> 下,file之,显示为data…… 好吧看来被腾讯加密了。。

在/data/data/com.tencent.mm/里随便看,发现lib里有个lIBMmcrypto.so,大概就是用来加密的了。nm -D之,发现一堆sqlite3_*;稍微google下,觉得应该是用了sqlcipher。于是只用拿到密钥就好啦。

gdb
希望break到sqlite3_exec之类的函数来看到密钥。发现android自带的gdbserver有问题,attach上去后根本看不到调用栈,不明原因。于是手动编译了静态的arm构架的gdb,push上去,能attach和traceback了;但一旦下断点,目标进程就会蹦,不明原理。此想法失败。
替换掉libmmcrypto.so
希望用自己修改的sqlcipher来替换原来的库,把密钥直接打印出来。花了很多时间,交叉编译了不少东西,但最后微信在使用被换掉的库的情况下总还是要崩……
逆向+肉眼密钥算法
使用apktool逆向微信的app(需要注意的是使用apktol前应该安装framework,否则会各种错误;我在这里耗了好久)。grep 'PRAGMA key',真的有,前后看了下,发现貌似是把this中的一个东西作为了key。。。没找到生成密钥的算法……
injection
想到修改代码把key直接打出来。google了一会儿,发现了以下方法:
  1. const-string v1, "!!!!!SQL:  "
  2. invoke-static {v1, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
复制代码


然后再用logcat就能看到日志了。兴冲冲改好代码,打包,用signapk.jar签名,但进行安装时总是说Data exceeds UNCOMPRESS_DATA_MAX,会有一些资源找不到导致程序蹦掉。。google了很多解决方法都没成功。。

后来灵机一动,只把原来微信.apk里的classes.dex替换成修改过的版本,重新签名,it works,在log里看到密钥了!

一阵激动,于是把数据库拿到pc上,用本地编译的sqlcipher想直接读出来。。。但总还是失败。。难道腾讯还修改过加密算法…… 没有办法,想直接调用libmmcrypto.so。。结果发现我的工具链里的glibc和android系统里的版本不兼容,最终也没能实现交叉调用(这大概也是最初自己编译的sqlcipher不能被微信使用的原因)……

后来想起android貌似还有个ndk,于是糙快猛入门,编译了个executable出来,总算work了。。 运行过程中发现mmcrypto打出了些debug信息,估计真是被腾讯改过了;而且版本好老,木有sqlcipher_export ,还得自己手动写程序dump……

微信做了数据库加密,而且密钥跟机器相关(甚至可能还跟系统相关),一旦机器蹦了或丢了数据就没了,但不提供导出记录,呵呵……

附:dump数据库用的小程序(从sqlite3 里抠了不少代码……)

  1. #include <string.h>
  2. #include <stdlib.h>
  3. #include <dlfcn.h>
  4. #include <stdio.h>
  5. #include <sqlite3.h>
  6. #include <assert.h>

  7. #define DECLARE(name) \
  8.         static typeof(name) *f_##name;

  9. DECLARE(sqlite3_close)
  10. DECLARE(sqlite3_column_text)
  11. DECLARE(sqlite3_column_count)
  12. DECLARE(sqlite3_errmsg)
  13. DECLARE(sqlite3_exec)
  14. DECLARE(sqlite3_finalize)
  15. DECLARE(sqlite3_free)
  16. DECLARE(sqlite3_initialize)
  17. DECLARE(sqlite3_mprintf)
  18. DECLARE(sqlite3_open_v2)
  19. DECLARE(sqlite3_prepare)
  20. DECLARE(sqlite3_shutdown)
  21. DECLARE(sqlite3_snprintf)
  22. DECLARE(sqlite3_step)
  23. DECLARE(sqlite3_trace)

  24. #define UNUSED_PARAMETER(name) (void)(name)

  25. static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  26.         UNUSED_PARAMETER(NotUsed);
  27.         int i;
  28.         for(i=0; i<argc; i++){
  29.                 printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  30.         }
  31.         printf("\n");
  32.         return 0;
  33. }
  34. /*
  35. ** An pointer to an instance of this structure is passed from
  36. ** the main program to the callback.  This is used to communicate
  37. ** state and mode information.
  38. */
  39. struct callback_data {
  40.   sqlite3 *db;           /* The database */
  41.   int echoOn;            /* True to echo input commands */
  42.   int statsOn;           /* True to display memory stats before each finalize */
  43.   int cnt;               /* Number of records displayed so far */
  44.   FILE *out;             /* Write results here */
  45.   FILE *traceOut;        /* Output for f_sqlite3_trace() */
  46.   int nErr;              /* Number of errors seen */
  47.   int mode;              /* An output mode setting */
  48.   int writableSchema;    /* True if PRAGMA writable_schema=ON */
  49.   int showHeader;        /* True to show column names in List or Column mode */
  50.   char *zDestTable;      /* Name of destination table when MODE_Insert */
  51.   char separator[20];    /* Separator character for MODE_List */
  52.   int colWidth[100];     /* Requested width of each column when in column mode*/
  53.   int actualWidth[100];  /* Actual width of each column */
  54.   char nullvalue[20];    /* The text to print when a NULL comes back from
  55.                          ** the database */
  56.   const char *zDbFilename;    /* name of the database file */
  57.   const char *zVfs;           /* Name of VFS to use */
  58.   sqlite3_stmt *pStmt;   /* Current statement if any. */
  59.   FILE *pLog;            /* Write log output here */
  60. };

  61. /*
  62. ** Execute a query statement that will generate SQL output.  Print
  63. ** the result columns, comma-separated, on a line and then add a
  64. ** semicolon terminator to the end of that line.
  65. **
  66. ** If the number of columns is 1 and that column contains text "--"
  67. ** then write the semicolon on a separate line.  That way, if a
  68. ** "--" comment occurs at the end of the statement, the comment
  69. ** won't consume the semicolon terminator.
  70. */
  71. static int run_table_dump_query(
  72.   struct callback_data *p, /* Query context */
  73.   const char *zSelect,     /* SELECT statement to extract content */
  74.   const char *zFirstRow    /* Print before first row, if not NULL */
  75. ){
  76.   sqlite3_stmt *pSelect;
  77.   int rc;
  78.   int nResult;
  79.   int i;
  80.   const char *z;
  81.   rc = f_sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
  82.   if( rc!=SQLITE_OK || !pSelect ){
  83.     fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, f_sqlite3_errmsg(p->db));
  84.     p->nErr++;
  85.     return rc;
  86.   }
  87.   rc = f_sqlite3_step(pSelect);
  88.   nResult = f_sqlite3_column_count(pSelect);
  89.   while( rc==SQLITE_ROW ){
  90.     if( zFirstRow ){
  91.       fprintf(p->out, "%s", zFirstRow);
  92.       zFirstRow = 0;
  93.     }
  94.     z = (const char*)f_sqlite3_column_text(pSelect, 0);
  95.     fprintf(p->out, "%s", z);
  96.     for(i=1; i<nResult; i++){
  97.       fprintf(p->out, ",%s", f_sqlite3_column_text(pSelect, i));
  98.     }
  99.     if( z==0 ) z = "";
  100.     while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
  101.     if( z[0] ){
  102.       fprintf(p->out, "\n;\n");
  103.     }else{
  104.       fprintf(p->out, ";\n");
  105.     }   
  106.     rc = f_sqlite3_step(pSelect);
  107.   }
  108.   rc = f_sqlite3_finalize(pSelect);
  109.   if( rc!=SQLITE_OK ){
  110.     fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, f_sqlite3_errmsg(p->db));
  111.     p->nErr++;
  112.   }
  113.   return rc;
  114. }

  115. /*
  116. ** Compute a string length that is limited to what can be stored in
  117. ** lower 30 bits of a 32-bit signed integer.
  118. */
  119. static int strlen30(const char *z){
  120.   const char *z2 = z;
  121.   while( *z2 ){ z2++; }
  122.   return 0x3fffffff & (int)(z2 - z);
  123. }


  124. /* zIn is either a pointer to a NULL-terminated string in memory obtained
  125. ** from malloc(), or a NULL pointer. The string pointed to by zAppend is
  126. ** added to zIn, and the result returned in memory obtained from malloc().
  127. ** zIn, if it was not NULL, is freed.
  128. **
  129. ** If the third argument, quote, is not '\0', then it is used as a
  130. ** quote character for zAppend.
  131. */
  132. static char *appendText(char *zIn, char const *zAppend, char quote){
  133.   int len;
  134.   int i;
  135.   int nAppend = strlen30(zAppend);
  136.   int nIn = (zIn?strlen30(zIn):0);

  137.   len = nAppend+nIn+1;
  138.   if( quote ){
  139.     len += 2;
  140.     for(i=0; i<nAppend; i++){
  141.       if( zAppend[i]==quote ) len++;
  142.     }
  143.   }

  144.   zIn = (char *)realloc(zIn, len);
  145.   if( !zIn ){
  146.     return 0;
  147.   }

  148.   if( quote ){
  149.     char *zCsr = &zIn[nIn];
  150.     *zCsr++ = quote;
  151.     for(i=0; i<nAppend; i++){
  152.       *zCsr++ = zAppend[i];
  153.       if( zAppend[i]==quote ) *zCsr++ = quote;
  154.     }
  155.     *zCsr++ = quote;
  156.     *zCsr++ = '\0';
  157.     assert( (zCsr-zIn)==len );
  158.   }else{
  159.     memcpy(&zIn[nIn], zAppend, nAppend);
  160.     zIn[len-1] = '\0';
  161.   }

  162.   return zIn;
  163. }

  164. /*
  165. ** This is a different callback routine used for dumping the database.
  166. ** Each row received by this callback consists of a table name,
  167. ** the table type ("index" or "table") and SQL to create the table.
  168. ** This routine should print text sufficient to recreate the table.
  169. */
  170. static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
  171.   int rc;
  172.   const char *zTable;
  173.   const char *zType;
  174.   const char *zSql;
  175.   const char *zPrepStmt = 0;
  176.   struct callback_data *p = (struct callback_data *)pArg;

  177.   UNUSED_PARAMETER(azCol);
  178.   if( nArg!=3 ) return 1;
  179.   zTable = azArg[0];
  180.   zType = azArg[1];
  181.   zSql = azArg[2];
  182.   
  183.   if( strcmp(zTable, "sqlite_sequence")==0 ){
  184.     zPrepStmt = "DELETE FROM sqlite_sequence;\n";
  185.   }else if( strcmp(zTable, "sqlite_stat1")==0 ){
  186.     fprintf(p->out, "ANALYZE sqlite_master;\n");
  187.   }else if( strncmp(zTable, "sqlite_", 7)==0 ){
  188.     return 0;
  189.   }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
  190.     char *zIns;
  191.     if( !p->writableSchema ){
  192.       fprintf(p->out, "PRAGMA writable_schema=ON;\n");
  193.       p->writableSchema = 1;
  194.     }
  195.     zIns = f_sqlite3_mprintf(
  196.        "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)"
  197.        "VALUES('table','%q','%q',0,'%q');",
  198.        zTable, zTable, zSql);
  199.     fprintf(p->out, "%s\n", zIns);
  200.     f_sqlite3_free(zIns);
  201.     return 0;
  202.   }else{
  203.     fprintf(p->out, "%s;\n", zSql);
  204.   }

  205.   if( strcmp(zType, "table")==0 ){
  206.     sqlite3_stmt *pTableInfo = 0;
  207.     char *zSelect = 0;
  208.     char *zTableInfo = 0;
  209.     char *zTmp = 0;
  210.     int nRow = 0;
  211.    
  212.     zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
  213.     zTableInfo = appendText(zTableInfo, zTable, '"');
  214.     zTableInfo = appendText(zTableInfo, ");", 0);

  215.     rc = f_sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
  216.     free(zTableInfo);
  217.     if( rc!=SQLITE_OK || !pTableInfo ){
  218.       return 1;
  219.     }

  220.     zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
  221.     /* Always quote the table name, even if it appears to be pure ascii,
  222.     ** in case it is a keyword. Ex:  INSERT INTO "table" ... */
  223.     zTmp = appendText(zTmp, zTable, '"');
  224.     if( zTmp ){
  225.       zSelect = appendText(zSelect, zTmp, '\'');
  226.       free(zTmp);
  227.     }
  228.     zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
  229.     rc = f_sqlite3_step(pTableInfo);
  230.     while( rc==SQLITE_ROW ){
  231.       const char *zText = (const char *)f_sqlite3_column_text(pTableInfo, 1);
  232.       zSelect = appendText(zSelect, "quote(", 0);
  233.       zSelect = appendText(zSelect, zText, '"');
  234.       rc = f_sqlite3_step(pTableInfo);
  235.       if( rc==SQLITE_ROW ){
  236.         zSelect = appendText(zSelect, "), ", 0);
  237.       }else{
  238.         zSelect = appendText(zSelect, ") ", 0);
  239.       }
  240.       nRow++;
  241.     }
  242.     rc = f_sqlite3_finalize(pTableInfo);
  243.     if( rc!=SQLITE_OK || nRow==0 ){
  244.       free(zSelect);
  245.       return 1;
  246.     }
  247.     zSelect = appendText(zSelect, "|| ')' FROM  ", 0);
  248.     zSelect = appendText(zSelect, zTable, '"');

  249.     rc = run_table_dump_query(p, zSelect, zPrepStmt);
  250.     if( rc==SQLITE_CORRUPT ){
  251.       zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
  252.       run_table_dump_query(p, zSelect, 0);
  253.     }
  254.     free(zSelect);
  255.   }
  256.   return 0;
  257. }

  258. /*
  259. ** Run zQuery.  Use dump_callback() as the callback routine so that
  260. ** the contents of the query are output as SQL statements.
  261. **
  262. ** If we get a SQLITE_CORRUPT error, rerun the query after appending
  263. ** "ORDER BY rowid DESC" to the end.
  264. */
  265. static int run_schema_dump_query(
  266.   struct callback_data *p,
  267.   const char *zQuery
  268. ){
  269.   int rc;
  270.   char *zErr = 0;
  271.   rc = f_sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr);
  272.   if( rc==SQLITE_CORRUPT ){
  273.     char *zQ2;
  274.     int len = strlen30(zQuery);
  275.     fprintf(p->out, "/****** CORRUPTION ERROR *******/\n");
  276.     if( zErr ){
  277.       fprintf(p->out, "/****** %s ******/\n", zErr);
  278.       f_sqlite3_free(zErr);
  279.       zErr = 0;
  280.     }
  281.     zQ2 = malloc( len+100 );
  282.     if( zQ2==0 ) return rc;
  283.     f_sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
  284.     rc = f_sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
  285.     if( rc ){
  286.       fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
  287.     }else{
  288.       rc = SQLITE_CORRUPT;
  289.     }
  290.     f_sqlite3_free(zErr);
  291.     free(zQ2);
  292.   }
  293.   return rc;
  294. }

  295. static void dump(struct callback_data *p)
  296. {
  297.         /* When playing back a "dump", the content might appear in an order
  298.          ** which causes immediate foreign key constraints to be violated.
  299.          ** So disable foreign-key constraint enforcement to prevent problems. */
  300.         fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
  301.         fprintf(p->out, "BEGIN TRANSACTION;\n");
  302.         p->writableSchema = 0;
  303.         f_sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
  304.         p->nErr = 0;
  305.         run_schema_dump_query(p,
  306.                         "SELECT name, type, sql FROM sqlite_master "
  307.                         "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
  308.                         );
  309.         run_schema_dump_query(p,
  310.                         "SELECT name, type, sql FROM sqlite_master "
  311.                         "WHERE name=='sqlite_sequence'"
  312.                         );
  313.         run_table_dump_query(p,
  314.                         "SELECT sql FROM sqlite_master "
  315.                         "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
  316.                         );
  317.         if( p->writableSchema ){
  318.                 fprintf(p->out, "PRAGMA writable_schema=OFF;\n");
  319.                 p->writableSchema = 0;
  320.         }
  321.         f_sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
  322.         f_sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
  323.         fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
  324. }

  325. int main(int argc, char **argv){
  326.         sqlite3 *db;
  327.         char *zErrMsg = 0;
  328.         int rc, i;

  329.         if( argc < 4 ){
  330.                 fprintf(stderr, "Usage: %s library DATABASE SQL-STATEMENTs\n", argv[0]);
  331.                 return 1;
  332.         }
  333.        
  334.         void *hdl = dlopen(argv[1], RTLD_LAZY);
  335.         if (!hdl) {
  336.                 fprintf(stderr, "failed to load lib: %s\n", dlerror());
  337.                 return -1;
  338.         }

  339. #define SOLVE(name) \
  340.         f_##name = dlsym(hdl, # name); \
  341.         if (!f_##name) { \
  342.                 fprintf(stderr, "failed to resolve %s: %s\n", #name, dlerror()); \
  343.                 return -1; \
  344.         }

  345.         SOLVE(sqlite3_close);
  346.         SOLVE(sqlite3_column_text);
  347.         SOLVE(sqlite3_column_count);
  348.         SOLVE(sqlite3_errmsg);
  349.         SOLVE(sqlite3_exec);
  350.         SOLVE(sqlite3_finalize);
  351.         SOLVE(sqlite3_free);
  352.         SOLVE(sqlite3_initialize);
  353.         SOLVE(sqlite3_mprintf);
  354.         SOLVE(sqlite3_open_v2);
  355.         SOLVE(sqlite3_prepare);
  356.         SOLVE(sqlite3_shutdown);
  357.         SOLVE(sqlite3_snprintf);
  358.         SOLVE(sqlite3_step);
  359.         SOLVE(sqlite3_trace);

  360.         f_sqlite3_initialize();

  361.         rc = f_sqlite3_open_v2(argv[2], &db,
  362.                         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
  363.         if( rc ){
  364.                 fprintf(stderr, "Can't open database: %s\n", f_sqlite3_errmsg(db));
  365.                 f_sqlite3_close(db);
  366.                 return 1;
  367.         }
  368.         for (i = 3; i < argc; i ++)
  369.         {
  370.                 fprintf(stderr, "exec: %s\n", argv[i]);
  371.                 if (!strncmp(argv[i], ".dump", 5)) {
  372.                         struct callback_data p;
  373.                         memset(&p, 0, sizeof(p));
  374.                         p.out = fopen(argv[i] + 5, "w");
  375.                         if (!p.out) {
  376.                                 fprintf(stderr, "failed to open file %s: %m\n", argv[i] + 5);
  377.                                 break;
  378.                         }
  379.                         p.db = db;
  380.                         dump(&p);
  381.                         fclose(p.out);
  382.                         continue;
  383.                 }
  384.                 rc = f_sqlite3_exec(db, argv[i], callback, 0, &zErrMsg);
  385.                 if( rc!=SQLITE_OK ) {
  386.                         fprintf(stderr, "SQL error: %s\n", zErrMsg);
  387.                         f_sqlite3_free(zErrMsg);
  388.                         break;
  389.                 }
  390.         }
  391.         f_sqlite3_close(db);
  392.         f_sqlite3_shutdown();
  393.         return 0;
  394. }
复制代码
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 转播转播 分享分享 分享淘帖 支持支持 反对反对
  • TA的每日心情
    郁闷
    2017-1-19 14:34
  • 签到天数: 29 天

    [LV.4]七品知县

    2#
    发表于 2015-1-8 10:34:47 | 只看该作者
    赞 好东西!!!!!高大上
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-8-26 10:59
  • 签到天数: 9 天

    [LV.3]八品县丞

    3#
    发表于 2015-1-11 08:45:53 | 只看该作者
    向技术流致敬!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2017-6-4 10:24
  • 签到天数: 1 天

    [LV.1]布衣百姓

    4#
    发表于 2015-2-13 16:38:49 | 只看该作者
    学习学习   
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2015-11-7 12:49
  • 签到天数: 2 天

    [LV.1]布衣百姓

    5#
    发表于 2015-4-7 15:59:20 | 只看该作者
    有点看不懂
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-5-3 10:49
  • 签到天数: 4 天

    [LV.2]九品芝麻官

    6#
    发表于 2015-4-13 22:20:11 | 只看该作者
    向技术流致敬
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    郁闷
    2016-4-25 16:01
  • 签到天数: 56 天

    [LV.5]六品通判

    7#
    发表于 2015-4-20 11:11:39 | 只看该作者
    向技术流致敬
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2015-4-27 09:28
  • 签到天数: 1 天

    [LV.1]布衣百姓

    8#
    发表于 2015-4-27 09:57:05 | 只看该作者
    好高大上,膜拜楼主
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    9#
    发表于 2015-6-17 01:22:17 | 只看该作者
    没看的太明白哦
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    昨天 12:01
  • 签到天数: 1339 天

    [LV.10]一品大学士

    10#
    发表于 2015-9-19 13:43:29 | 只看该作者
    不懂,无视了。。。。。。
    回复 支持 反对

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    北京数据恢复培训 北京华客数据恢复培训
    快速回复 返回顶部 返回列表