어떻게 위챗 채팅 기록을 내보냅니까

15448 단어
우선, 데이터베이스가/data/data/com에 있다는 것을 쉽게 발견할 수 있다.tencent.mm/Micromsg/에서 파일로 데이터로 표시... 그래, 텐센트에 암호화되었나 봐.
/data/data/com.tencent.mm/리를 마음대로 보니lib에libmmcrypto가 있는 것을 발견하였다.so, 아마 암호화하는 데 쓰일 거예요.nm-D의, 한 무더기의 sqlite3 * 발견;약간 구글에서 sqlcipher를 사용한 것 같아요.그래서 키만 받았으면 좋겠어요.
  • gdb 희망break에서 sqlite3exec 같은 함수로 키를 볼 수 있습니다.android가 자체로 가지고 있는 gdbserver에 문제가 있는 것을 발견했습니다.attach가 올라간 후 호출 창고가 전혀 보이지 않습니다. 원인을 알 수 없습니다.그래서 정적arm 프레임의 gdb를 수동으로 컴파일하여push에 올라가면attach와traceback이 가능합니다.그러나 일단 중단되면 목표의 진행 과정은 껑충껑충 뛰며 원리를 알 수 없다.이 생각은 실패했다.
  • libmcrypto를 교체합니다.so는 자신이 수정한 sqlcipher로 원래의 라이브러리를 대체하고 키를 직접 출력하기를 원합니다.많은 시간을 들여 많은 물건을 교차 번역했지만 결국 위챗은 바뀐 라이브러리를 사용하는 상황에서 붕괴될 것이다...
  • 역방향 + 육안 키 알고리즘은 apktool 역방향 위챗 앱을 사용합니다 (주의해야 할 것은 apktol을 사용하기 전에 프레임워크를 설치해야 합니다. 그렇지 않으면 여러 가지 오류가 발생할 수 있습니다. 저는 이곳에서 오래 걸렸습니다.)grep'PRAGMA 키', 정말 있어요. 앞뒤로 보니까 this 중의 하나를 키로 한 것 같아요...키를 생성하는 알고리즘을 찾을 수 없습니다......
  • injection은 코드를 수정할 생각에 키를 직접 쳤다.구글은 잠시 후 다음과 같은 방법을 발견했다.
    const-string v1, "!!!!!SQL:  " 
    invoke-static {v1, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    그리고logcat으로 로그를 볼 수 있다.흥겹게 코드를 고치고 포장하고signapk을 사용합니다.jar 서명, 하지만 설치할 때 항상 Data exceeds UNCOMPRESSDATA_MAX, 일부 자원을 찾지 못하면 프로그램이 튀어나올 수 있습니다.구글의 많은 해결 방법이 성공하지 못했다.나중에 영감이 떠올라 원래의 위챗만 썼다.apk의classes.dex를 수정된 버전으로 바꾸고 다시 서명합니다. itworks,log에서 키를 보았습니다!한바탕 흥분하여 데이터베이스를 pc에 가져와서 로컬에서 컴파일한 sqlcipher로 직접 읽으려고 합니다...그래도 실패했어..설마 텐센트가 암호화 알고리즘도 수정했단 말인가...어쩔 수 없이libmmcrypto를 직접 호출하려고 합니다.so..그 결과 내 도구 체인의glibc와android시스템의 버전이 호환되지 않았고 최종적으로 교차 호출을 실현하지 못했다. (이것은 아마도 처음에 자신이 컴파일한 sqlcipher가 위챗에 사용되지 못한 원인일 것이다)...나중에 안드로이드에 ndk가 있는 것 같다는 생각이 들어서 급하게 입문하여executable를 컴파일하여 마침내 work를 만들었다.운행 과정에서 mmcrypto가 debug 정보를 보낸 것을 발견했는데 정말 텐센트에 의해 바뀌었을 것이다.그리고 버전이 너무 낡았어. 나무는 sqlcipherexport, 프로그램 dump도 직접 써야 돼...
  • 위챗은 데이터베이스 암호화를 했고 키는 기계와 관련이 있다(심지어 시스템과 관련이 있을 수도 있다). 기계가 튀어나오거나 데이터를 잃어버리면 없어지지만 내보내기 기록을 제공하지 않는다. 허허...
    첨부:dump 데이터베이스에 사용되는 작은 프로그램(sqlite3에서 많은 코드를 파냈는데......)
    #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
    ", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("
    "); 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 *****/
    ", 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, "
    ;
    "); }else{ fprintf(p->out, ";
    "); } rc = f_sqlite3_step(pSelect); } rc = f_sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/
    ", 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;
    "; }else if( strcmp(zTable, "sqlite_stat1")==0 ){ fprintf(p->out, "ANALYZE sqlite_master;
    "); }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;
    "); 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
    ", zIns); f_sqlite3_free(zIns); return 0; }else{ fprintf(p->out, "%s;
    ", 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 *******/
    "); if( zErr ){ fprintf(p->out, "/****** %s ******/
    ", 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 ******/
    ", 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;
    "); fprintf(p->out, "BEGIN TRANSACTION;
    "); 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;
    "); 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
    " : "COMMIT;
    "); } 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
    ", argv[0]); return 1; } void *hdl = dlopen(argv[1], RTLD_LAZY); if (!hdl) { fprintf(stderr, "failed to load lib: %s
    ", dlerror()); return -1; } #define SOLVE(name) \ f_##name = dlsym(hdl, # name); \ if (!f_##name) { \ fprintf(stderr, "failed to resolve %s: %s
    ", #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
    ", f_sqlite3_errmsg(db)); f_sqlite3_close(db); return 1; } for (i = 3; i < argc; i ++) { fprintf(stderr, "exec: %s
    ", 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
    ", 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
    ", zErrMsg); f_sqlite3_free(zErrMsg); break; } } f_sqlite3_close(db); f_sqlite3_shutdown(); return 0; }

    좋은 웹페이지 즐겨찾기