设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
9 ~/ t9 X! A' X; ? 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
: c+ t& X* a v+ L: D' }* I0 f+ _操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
: N& V5 x) z6 e4 t 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
6 W) J/ f6 G t" C% W6 s
( Y. W" ^$ J4 \2 H# H+ S查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
* ?# s0 I& ?3 ]; W6 iif (loadPackageParam.packageName.equals("com.tencent.mm")){ XposedHelpers.findAndHookMethod( "android.app.ApplicationPackageManager", loadPackageParam.classLoader, "getPackageInfo", String.class, int.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { if (param.args[0].equals("com.tencent.mm")) { PackageInfo packageInfo = (PackageInfo) param.getResult(); packageInfo.versionName = "6.7.3"; packageInfo.versionCode = 1360; } } });}[url=][/url]
) F% b( `0 L8 T# m1 @" z0 K/ F+ }& x9 [: ^/ R. u) p9 ^
% H' ?$ \9 o$ |! p0 T8 [- t& H. z 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,,
% p5 q% P$ t* q尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
6 {& A8 p4 g1 z$ X' {+ x- y 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
1 K. i4 e5 k5 K5 T" Z 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
: ~0 q% H& G5 I: c' ]尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的,
& r0 _' d, s/ N1 H自动化程序[url=][/url]
" V( ^1 u. g6 d& \! f( Ypackage com.example.wxreversion;( j% T, L Y5 F C; ^
9 |) N9 [" K! [import android.os.Handler;+ i9 [5 l& _# z% y3 L; L
import android.support.v7.app.AppCompatActivity;( L8 b4 {' V' c: o; e! J8 f2 J
import android.os.Bundle;
3 z' F4 }/ l% t; u# C( i2 `( z3 P& pimport android.text.method.ScrollingMovementMethod;
# G: X) v0 D4 c. Kimport android.util.Log;
+ T/ u- {0 c5 Simport android.view.View;
" b" G/ n6 `0 T) x! t$ Fimport android.widget.Button;
& M+ M5 _8 X* J- e. ]% y! Ximport android.widget.TextView;
9 p) t" Q3 X8 Q' R; X
1 E0 y# S: G1 P; vimport java.io.File;
, D7 D$ k, N* O/ |, T
7 C T% _# K9 E* w6 `$ Fpublic class MainActivity extends AppCompatActivity+ ]; V. V: p7 w* c B/ \9 s
{
, f! k+ @8 ]# m z( T; t( n. X Handler handler;
/ I+ P: E+ Y, T TextView textView; N z( O6 m9 c- P ?. I# k4 G
8 P9 V( v: t) t3 { M* A* W& y
u. L3 b1 U3 D z private void LogV(String s)( K6 a3 V# F* T, R+ x2 m1 p
{& d4 I+ Q) ~$ o
Log.v("test", s);
: m+ c& l* f: d; N9 E( y; z/ y" i }' w3 H2 x0 j; m X5 Z% i
6 K( m8 h6 M. b7 ^
private void textAppend(final String s)" N+ c& ^1 D6 Z' u/ x; N/ t
{
% A( a4 |6 }* B3 Y, s* ` LogV("textAppend:" + s);
7 m; _, T5 J+ J% a4 n handler.post(new Runnable()
, C2 S5 `# X0 h: N1 d- B; [ {
]. ~* Z8 \2 @3 z+ A% X5 l; J @Override) {8 \- h: V' E
public void run()
- m0 z5 Y) W% _. k {
( c9 V0 j* {' }5 f0 N6 P LogV("run:" + s);0 C$ l" o# g) q+ A& C
5 Q) A( K2 H+ R) w1 H) B& T
textView.setText(textView.getText() + s);6 i. z% }0 e! S6 v" \# P
}1 K% F" q8 l2 G: X: k9 ~8 D' q
});
9 O% S& R4 R5 e0 l; w }! }0 A6 i- c% q; j- @ W. u# D4 Y
% @8 O+ a1 I0 D4 F% s4 l: k
private String getPath()
9 i! J! s- T+ n7 t' r8 ] {
5 B8 W0 Z. E& S) {% g/ R String path = null;; E2 t/ A6 ]) V
textAppend("-----------------------------\n");
& y$ A* B# B& Y
9 j( h; c1 O7 m( V) X+ A2 h' R8 d& W& h" E8 }4 W- ^+ V* g3 R
if (!ShellUtils.checkRootPermission())& z4 r& G+ q# c5 u9 P
{
! G5 [9 v9 G& d( {4 k, T0 v6 r textAppend("获取root权限失败,请在设置中授予权限!\n");: {7 m/ u6 L, j, r2 _
return path;* }1 C& w/ T' d5 a& u) c. u3 D; C
}
8 e& O; i; c" I1 B, G) G& r6 ^& F; d4 A. [/ ]" E# T
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;
% k. C4 H! y& E if (path != null)$ p& U; v" V0 V5 J
{! b n" p. W" w; g9 q ?2 r' d+ @
try2 ]8 y( U& m' x
{
5 O. b: c$ u r$ f path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
9 S8 n8 I$ j$ I( F' q T } catch (Throwable throwable)
1 t# `' n* k! @, Q# x+ L2 O {, Z3 U. r) z: c! \ u2 _
path = null;
3 U) L" ?# m# w0 c/ T7 W! @+ n) K }! {+ w, n# f* n9 k
}
5 ~: E4 o8 X3 r. l, V: t; o1 N: O4 a2 r* n; B! w- M' [5 @3 k
if (path == null)5 \* m. v* G. `% s6 C
{) w) u5 ?- c) _2 _
textAppend("未找到微信安装目录,请先安装!\n");. ?* D# E, u- r' Q7 J
} else
. r9 ]1 E1 n5 f& Y: O- J {9 i/ z, S3 V( r( o* _" [
textAppend("找到安装目录:" + path + "\n");
! u. I; T' u/ f4 V, T+ {& N$ R }6 l9 ]: J. P5 f
- c% N0 s& l7 @ return path;
9 F1 M$ u" i* U: ^ ^ }% S5 t5 s( l8 Z2 R( M
9 l6 |" e* v" v! N9 W9 D. X private boolean isEnpty(String string)
; p) l, z# p2 K! R; {4 `& F# z {7 j2 f; J* b3 d5 k
if (string == null || string.length() == 0)
( K5 G E! e9 Z' [4 q& G5 C {
. U- u6 D: Q/ n2 B return true;2 E. s' h* r4 X" b: \0 q
}
) f+ @& N- W) @% Z! v7 B; @ return false;
3 c3 C Z8 u- f% f$ G" C }8 H) {2 I, G2 P# ^+ j* S
' d; U; q6 N# n
private void putResult(ShellUtils.CommandResult result)
( _8 @& M: w( W# f. c- n5 p {3 `: U2 ]5 b" [
textAppend("返回码:" + result.result + "\n");1 |+ q8 J( v6 K/ Z9 r. w( D! a
- \1 M V) G6 q if (!isEnpty(result.successMsg))" I/ y( l" m: ~0 F
{- x2 D6 {) D3 A6 B) D1 J
textAppend(result.successMsg + "\n");3 I. Z# O* s( X5 `. F3 N7 v" F
} else if (!isEnpty(result.errorMsg))
6 V4 E9 O8 q- c {
, A* a/ p/ t4 @ textAppend("错误消息:" + result.errorMsg + "\n");. Z6 c2 {+ |) {# _
}
. \+ O. C* b0 z: u8 y; w }
: V6 \5 ?1 ^0 f8 c3 S
: }+ r. o! w" [* n5 t* P @Override) N3 i: e3 Q; b7 Z% u& l9 b0 B
protected void onCreate(Bundle savedInstanceState); Y$ T+ T0 ]' R$ A
{. M( h t' w- u! T: w
super.onCreate(savedInstanceState);' b: ~3 C, G& _0 W* B
setContentView(R.layout.activity_main);/ j! X' ]% W4 H3 J- R
handler = new Handler();" p1 u9 s0 g* J3 L5 ?, p
- n2 X( g# W& M: p" O6 Z textView = (TextView) findViewById(R.id.textView);
4 T4 P0 e ?6 d+ i textView.setMovementMethod(ScrollingMovementMethod.getInstance());
0 C# j, h* A }# c! B
% f( ^$ }1 F0 m ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()( \1 W3 j p' P5 _& D$ u* e" i
{
% ], A1 K0 u7 H1 l# w @Override; ^. |5 {, |- {/ c8 \3 d) ]
public void onClick(View v)
; A/ T$ N, r& z) n+ \ {1 d& p- _# p6 i, O, _% f6 T
2 G- i; `( D( r. s: l
new Thread()
$ n# {& Y4 |$ Q3 d, }/ C {! Z" T1 ^( @; o3 {
@Override
1 J2 e% m8 T6 S public void run()
) O* d1 c; |- e2 }* K) r# _* k {9 h; a" j, R2 z5 X( }8 Q7 K0 [
String path = getPath();
! _: ^9 t: |& i if (path == null)+ Z. Y' V3 L7 }; E
{
% Y, b" s9 h2 z% d: h return;4 f. m I1 u( \
}
4 ^5 O$ l( q7 q: d U) { textAppend("正在保存安装目录!\n");
( t5 T7 h1 ? j9 u1 F+ P) O: [ ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
% ?7 I2 d7 S; ]) p- _ putResult(result);9 W; ]& W: i1 z* S; p/ ~& L, W
if (!isEnpty(result.errorMsg))% k+ }( i6 s, P; B O, i. a1 S
{
$ ?. W* V' w J return;% N$ {& m4 e6 V( f
}
; A: X: _9 [+ }& d/ y$ d# X# }* l( H, \7 j R
textAppend("正在卸载微信!\n");
3 W+ h& b% u0 ^0 g( }1 c result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用: S# A t6 E1 V l
putResult(result);
/ d) f# X' ?( o! p( h; Y/ _8 F% s if (!isEnpty(result.errorMsg))+ s3 w7 }, ^ ~- L/ P* y
{+ _# C1 Q& P! U- m) l% m' L* D
return;) g8 J4 E& D4 A
}
/ k: X9 z( ~. a9 Y& U" A2 y
4 L; i1 ~2 U6 n. I" i. w textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");
* S7 Z/ V0 E! t. z, E! R }
* p7 W7 i. e3 T* c. Z- U }.start();
2 ?& ^ J7 t0 S7 V6 G) ~- E7 B; f+ D% p* F# l
}& y8 A5 `; f/ z& W( a" b5 j
});
* O2 \/ ]9 L: I4 f' l
% j" |$ l3 ?" b% D+ v& R0 j
" F$ T7 M" M V$ V) i0 h) R" o0 ] ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener() S7 I4 K/ x" t/ B$ [% B
{
, `& `0 ^$ C0 f0 A e. M @Override( y. P6 P/ d b$ e/ i
public void onClick(View v)9 \6 w i# m/ t9 a. ?4 `
{1 ]0 r; @. x* t5 m# {3 n
( x$ M3 `4 Y0 e+ M$ U. o# R' H
new Thread()
+ @& Q& d0 k9 g2 _' d {: u. h% u' f% c( F" E1 Z
@Override
6 l+ v" G7 Y$ d4 ?( c public void run()
; E/ ]( h3 V, z- u {7 }4 P& q$ f0 a2 N2 I
String path = getPath();
4 \) S2 N+ f/ ^" @& d, ^0 X if (path == null)' {( ]1 |# ?7 Z& k9 }
{
; o+ m! ^7 ~) ?+ j* t return;9 k/ [& G0 a! X1 N: z M! J! G1 E
}
) T: P$ {6 ~5 B( j$ A% Y6 h8 J* U0 Y7 \5 x
textAppend("正在检查是否有备份的安装文件!\n");
5 M$ M" f2 h! n/ M, Z) m% h+ ] if (!new File("/data/local/tmp/com.tencent.mm").exists())! h( W1 P! b: ^8 s
{8 d: X* s* b+ o1 k/ }
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");
5 s8 y/ E. a, }, a9 ~ return;7 Q6 K% j6 {. E
}' m: Y- }* S% x/ s
4 v' \# a3 E9 B: b5 Y+ j: V2 m
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");$ ^* ^4 s6 A" p% P& j4 R
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);* D% t1 r! d. P0 p0 N9 s8 K
putResult(result);
" I5 J4 D+ N' m/ a. X if (!isEnpty(result.errorMsg))" b/ H0 m/ h8 a; K9 J9 l
{/ _7 g% c/ Y; t( B/ s$ v0 ]
return; w x! Y- c S
}
: l* j* @" O* M8 j& z# H
; u8 m) \; p3 y. J4 J! V/ B# f$ W' m) m2 W
textAppend("正在覆盖安装目录!\n");
( d) ~9 P3 |1 F5 S1 ^( T" C result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);% q1 }; s6 @- @! N7 `3 W6 \$ |
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);# o& `% o' M- L) S
putResult(result);
b% i2 q: V: c2 `" O8 J if (!isEnpty(result.errorMsg))) e' E' _& j! u
{
2 m6 _0 ~8 V0 n' d- w$ i. K return;
4 A6 W' s2 h E0 | }
9 ~' ^; H8 {% L/ }( p1 r
# F1 ^4 _! c. q$ T5 x8 k textAppend("系统将在10秒后重启!\n");0 ~! W4 p# k% [+ K' Q) T
4 ]/ N% S5 f4 f. i3 o0 @
5 o+ A$ X- [6 f try/ n6 ?% \' D* h @9 ]# T+ y
{
, N- b' J) n7 n' d sleep(10 * 1000);
5 b) F$ ?& j% x3 \* y handler.post(new Runnable()7 U& N) {: H B3 d) H3 ^( J
{
1 H8 A! @2 F# j. P [& _ @Override
$ ?+ o c7 c) W6 o5 ~+ u3 W public void run()
$ v) E# i! P* _1 W: @ {
8 w1 b; I& ^" P. x1 h( j& z ShellUtils.execCommand("reboot", true);
$ q" R8 F5 @' a$ X P+ x }
5 S( H i* c8 j3 R& k6 e });6 i6 W; v3 p. r+ _& u5 t
} catch (InterruptedException e)
. v9 n; {* U9 u9 k8 _2 X {. V. G! h7 O$ [0 X) q3 g1 n
e.printStackTrace();
* Y! a& m' ]5 w* K }
5 f) f; p- s7 v" J6 H4 _ }
4 c/ O' g, F e$ b8 s8 E }.start();
8 G6 e" M+ D" N. l8 r* B! m" A5 b+ a7 m& j
}
* x, q! P' ~0 w. k( b });1 o. w" ~' d) j6 |
: u) c) d/ l0 b8 l& @6 e& k1 n% u
textView.append("-----------------------------\n");
( T6 N/ s3 F$ u: m2 j4 u textView.append("该应用需要root权限!\n");
% @, b( h( V6 L6 a8 B if (ShellUtils.checkRootPermission())
5 |% B' ^- v- ~ {
" Q- A% R, a9 K f) Q9 U: @9 N textView.append("获取root权限成功!\n");
1 Z2 f( b( P# @+ F. p4 v } else, Z" T0 I2 {5 R* Q6 g, K2 p
{- U& ~: g6 z2 I6 h: Q; X( @
textView.append("获取root权限失败,请在设置中授予权限!\n");
1 i0 x% T( U* {5 ]/ q }1 z# y4 S* M/ ]
* J3 c6 z$ z, D" F# N# Y
; C" ~0 }$ A; B) u }$ |6 v6 O! k( L; q5 K
}[url=][/url]
6 ?/ ^! F9 x# F# s* I7 m+ ?
) ^$ X; g/ a6 x p8 W) G1 v' w5 l) B9 e6 t# @4 o, {; S( e* e
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 ; D' z( O# }8 o# B7 b1 E
安装包在release目录中 2 M* _% n q/ H6 @) {
|