首先,很容易发现数据库在/data/data/com.tencent.mm/MicroMsg/<some id> 下,file之,显示为data…… 好吧看来被腾讯加密了。。
在/data/data/com.tencent.mm/里随便看,发现lib里有个libmmcrypto.so,大概就是用来加密的了。nm -D之,发现一堆sqlite3_*;稍微google下,觉得应该是用了sqlcipher。于是只用拿到密钥就好啦。
const-string v1, "!!!!!SQL: " invoke-static {v1, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I然后再用logcat就能看到日志了。兴冲冲改好代码,打包,用signapk.jar签名,但进行安装时总是说Data exceeds UNCOMPRESS_DATA_MAX,会有一些资源找不到导致程序蹦掉。。google了很多解决方法都没成功。。
#include <string.h> #include <stdlib.h> #include <dlfcn.h> #include <stdio.h> #include <sqlite3.h> #include <assert.h> #define DECLARE(name) \ static typeof(name) *f_##name; DECLARE(sqlite3_close) DECLARE(sqlite3_column_text) DECLARE(sqlite3_column_count) DECLARE(sqlite3_errmsg) DECLARE(sqlite3_exec) DECLARE(sqlite3_finalize) DECLARE(sqlite3_free) DECLARE(sqlite3_initialize) DECLARE(sqlite3_mprintf) DECLARE(sqlite3_open_v2) DECLARE(sqlite3_prepare) DECLARE(sqlite3_shutdown) DECLARE(sqlite3_snprintf) DECLARE(sqlite3_step) DECLARE(sqlite3_trace) #define UNUSED_PARAMETER(name) (void)(name) static int callback(void *NotUsed, int argc, char **argv, char **azColName){ UNUSED_PARAMETER(NotUsed); int i; for(i=0; i<argc; i++){ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } /* ** An pointer to an instance of this structure is passed from ** the main program to the callback. This is used to communicate ** state and mode information. */ struct callback_data { sqlite3 *db; /* The database */ int echoOn; /* True to echo input commands */ int statsOn; /* True to display memory stats before each finalize */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ FILE *traceOut; /* Output for f_sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ char *zDestTable; /* Name of destination table when MODE_Insert */ char separator[20]; /* Separator character for MODE_List */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ char nullvalue[20]; /* The text to print when a NULL comes back from ** the database */ const char *zDbFilename; /* name of the database file */ const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ }; /* ** Execute a query statement that will generate SQL output. Print ** the result columns, comma-separated, on a line and then add a ** semicolon terminator to the end of that line. ** ** If the number of columns is 1 and that column contains text "--" ** then write the semicolon on a separate line. That way, if a ** "--" comment occurs at the end of the statement, the comment ** won't consume the semicolon terminator. */ static int run_table_dump_query( struct callback_data *p, /* Query context */ const char *zSelect, /* SELECT statement to extract content */ const char *zFirstRow /* Print before first row, if not NULL */ ){ sqlite3_stmt *pSelect; int rc; int nResult; int i; const char *z; rc = f_sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, f_sqlite3_errmsg(p->db)); p->nErr++; return rc; } rc = f_sqlite3_step(pSelect); nResult = f_sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ if( zFirstRow ){ fprintf(p->out, "%s", zFirstRow); zFirstRow = 0; } z = (const char*)f_sqlite3_column_text(pSelect, 0); fprintf(p->out, "%s", z); for(i=1; i<nResult; i++){ fprintf(p->out, ",%s", f_sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ fprintf(p->out, "\n;\n"); }else{ fprintf(p->out, ";\n"); } rc = f_sqlite3_step(pSelect); } rc = f_sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, f_sqlite3_errmsg(p->db)); p->nErr++; } return rc; } /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. */ static int strlen30(const char *z){ const char *z2 = z; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* zIn is either a pointer to a NULL-terminated string in memory obtained ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static char *appendText(char *zIn, char const *zAppend, char quote){ int len; int i; int nAppend = strlen30(zAppend); int nIn = (zIn?strlen30(zIn):0); len = nAppend+nIn+1; if( quote ){ len += 2; for(i=0; i<nAppend; i++){ if( zAppend[i]==quote ) len++; } } zIn = (char *)realloc(zIn, len); if( !zIn ){ return 0; } if( quote ){ char *zCsr = &zIn[nIn]; *zCsr++ = quote; for(i=0; i<nAppend; i++){ *zCsr++ = zAppend[i]; if( zAppend[i]==quote ) *zCsr++ = quote; } *zCsr++ = quote; *zCsr++ = '\0'; assert( (zCsr-zIn)==len ); }else{ memcpy(&zIn[nIn], zAppend, nAppend); zIn[len-1] = '\0'; } return zIn; } /* ** This is a different callback routine used for dumping the database. ** Each row received by this callback consists of a table name, ** the table type ("index" or "table") and SQL to create the table. ** This routine should print text sufficient to recreate the table. */ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ int rc; const char *zTable; const char *zType; const char *zSql; const char *zPrepStmt = 0; struct callback_data *p = (struct callback_data *)pArg; UNUSED_PARAMETER(azCol); if( nArg!=3 ) return 1; zTable = azArg[0]; zType = azArg[1]; zSql = azArg[2]; if( strcmp(zTable, "sqlite_sequence")==0 ){ zPrepStmt = "DELETE FROM sqlite_sequence;\n"; }else if( strcmp(zTable, "sqlite_stat1")==0 ){ fprintf(p->out, "ANALYZE sqlite_master;\n"); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ fprintf(p->out, "PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = f_sqlite3_mprintf( "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); fprintf(p->out, "%s\n", zIns); f_sqlite3_free(zIns); return 0; }else{ fprintf(p->out, "%s;\n", zSql); } if( strcmp(zType, "table")==0 ){ sqlite3_stmt *pTableInfo = 0; char *zSelect = 0; char *zTableInfo = 0; char *zTmp = 0; int nRow = 0; zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); zTableInfo = appendText(zTableInfo, zTable, '"'); zTableInfo = appendText(zTableInfo, ");", 0); rc = f_sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0); free(zTableInfo); if( rc!=SQLITE_OK || !pTableInfo ){ return 1; } zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); /* Always quote the table name, even if it appears to be pure ascii, ** in case it is a keyword. Ex: INSERT INTO "table" ... */ zTmp = appendText(zTmp, zTable, '"'); if( zTmp ){ zSelect = appendText(zSelect, zTmp, '\''); free(zTmp); } zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); rc = f_sqlite3_step(pTableInfo); while( rc==SQLITE_ROW ){ const char *zText = (const char *)f_sqlite3_column_text(pTableInfo, 1); zSelect = appendText(zSelect, "quote(", 0); zSelect = appendText(zSelect, zText, '"'); rc = f_sqlite3_step(pTableInfo); if( rc==SQLITE_ROW ){ zSelect = appendText(zSelect, "), ", 0); }else{ zSelect = appendText(zSelect, ") ", 0); } nRow++; } rc = f_sqlite3_finalize(pTableInfo); if( rc!=SQLITE_OK || nRow==0 ){ free(zSelect); return 1; } zSelect = appendText(zSelect, "|| ')' FROM ", 0); zSelect = appendText(zSelect, zTable, '"'); rc = run_table_dump_query(p, zSelect, zPrepStmt); if( rc==SQLITE_CORRUPT ){ zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0); run_table_dump_query(p, zSelect, 0); } free(zSelect); } return 0; } /* ** Run zQuery. Use dump_callback() as the callback routine so that ** the contents of the query are output as SQL statements. ** ** If we get a SQLITE_CORRUPT error, rerun the query after appending ** "ORDER BY rowid DESC" to the end. */ static int run_schema_dump_query( struct callback_data *p, const char *zQuery ){ int rc; char *zErr = 0; rc = f_sqlite3_exec(p->db, zQuery, dump_callback, p, &zErr); if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); fprintf(p->out, "/****** CORRUPTION ERROR *******/\n"); if( zErr ){ fprintf(p->out, "/****** %s ******/\n", zErr); f_sqlite3_free(zErr); zErr = 0; } zQ2 = malloc( len+100 ); if( zQ2==0 ) return rc; f_sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); rc = f_sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); if( rc ){ fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); }else{ rc = SQLITE_CORRUPT; } f_sqlite3_free(zErr); free(zQ2); } return rc; } static void dump(struct callback_data *p) { /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ fprintf(p->out, "PRAGMA foreign_keys=OFF;\n"); fprintf(p->out, "BEGIN TRANSACTION;\n"); p->writableSchema = 0; f_sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); p->nErr = 0; run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" ); run_schema_dump_query(p, "SELECT name, type, sql FROM sqlite_master " "WHERE name=='sqlite_sequence'" ); run_table_dump_query(p, "SELECT sql FROM sqlite_master " "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 ); if( p->writableSchema ){ fprintf(p->out, "PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } f_sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); f_sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n"); } int main(int argc, char **argv){ sqlite3 *db; char *zErrMsg = 0; int rc, i; if( argc < 4 ){ fprintf(stderr, "Usage: %s library DATABASE SQL-STATEMENTs\n", argv[0]); return 1; } void *hdl = dlopen(argv[1], RTLD_LAZY); if (!hdl) { fprintf(stderr, "failed to load lib: %s\n", dlerror()); return -1; } #define SOLVE(name) \ f_##name = dlsym(hdl, # name); \ if (!f_##name) { \ fprintf(stderr, "failed to resolve %s: %s\n", #name, dlerror()); \ return -1; \ } SOLVE(sqlite3_close); SOLVE(sqlite3_column_text); SOLVE(sqlite3_column_count); SOLVE(sqlite3_errmsg); SOLVE(sqlite3_exec); SOLVE(sqlite3_finalize); SOLVE(sqlite3_free); SOLVE(sqlite3_initialize); SOLVE(sqlite3_mprintf); SOLVE(sqlite3_open_v2); SOLVE(sqlite3_prepare); SOLVE(sqlite3_shutdown); SOLVE(sqlite3_snprintf); SOLVE(sqlite3_step); SOLVE(sqlite3_trace); f_sqlite3_initialize(); rc = f_sqlite3_open_v2(argv[2], &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if( rc ){ fprintf(stderr, "Can't open database: %s\n", f_sqlite3_errmsg(db)); f_sqlite3_close(db); return 1; } for (i = 3; i < argc; i ++) { fprintf(stderr, "exec: %s\n", argv[i]); if (!strncmp(argv[i], ".dump", 5)) { struct callback_data p; memset(&p, 0, sizeof(p)); p.out = fopen(argv[i] + 5, "w"); if (!p.out) { fprintf(stderr, "failed to open file %s: %m\n", argv[i] + 5); break; } p.db = db; dump(&p); fclose(p.out); continue; } rc = f_sqlite3_exec(db, argv[i], callback, 0, &zErrMsg); if( rc!=SQLITE_OK ) { fprintf(stderr, "SQL error: %s\n", zErrMsg); f_sqlite3_free(zErrMsg); break; } } f_sqlite3_close(db); f_sqlite3_shutdown(); return 0; }