设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
% S, q" d) B( y) W- p 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
4 Q+ `: k, e, {+ ?7 w1 @* C操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
, }5 i# r7 A6 }) f0 I$ x 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
" X0 ` S# b! g' k
* R, {/ P; n, P9 N& G7 q查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
. K* {; ~6 w g* ^% dif (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]
; w2 v. V& t$ O$ x3 t
% p4 A8 f( h Z/ X6 O5 T# \% D) h: p, Z. ~2 @( P# h( h
编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,,
+ N, {4 `) C( N. r6 \' \5 }* |尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
& m ] X, ?! o8 R, u- H" D 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, " h3 W! ^7 ^! z; P, v6 x
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, 1 D$ q. n0 C! l9 C/ ~6 A# a
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, - d' }0 e. f# E; ?, j( ~" X( X
自动化程序[url=][/url]
6 [3 F' Q2 a0 z, ]& I8 Epackage com.example.wxreversion;7 e1 j1 T) r0 A& X- y. n
$ g) d$ D$ Q( z* L+ S1 Eimport android.os.Handler;- w0 r4 {9 k, ]
import android.support.v7.app.AppCompatActivity;* N' ^6 u s( _: _$ O0 N- O
import android.os.Bundle;9 B& }& f' M" j* P
import android.text.method.ScrollingMovementMethod;
0 l1 F) m! l1 n1 y4 d/ Bimport android.util.Log;
% s: C o( h# N4 bimport android.view.View;# Q" U$ y4 a4 a$ k- i) }8 w9 S
import android.widget.Button;
- i- L. Y( w; Oimport android.widget.TextView;
- Q. h' z# K7 C/ W
3 C% x% ~; P: T7 ` _8 o7 Z* Zimport java.io.File;
# m5 N5 Z) R" t; U# c R0 U
1 R* c4 }+ v8 ^' |' b7 w) g: C0 upublic class MainActivity extends AppCompatActivity
$ i3 _: c9 s: u$ S) C2 r{
$ L3 M6 v2 U- L8 O' ~3 ? Handler handler;
% L1 V. R5 T1 K% L0 v2 z TextView textView;
7 g6 n* H# w/ P8 P$ V, n
- p4 X9 B1 I1 P' @% C3 i e
! I& D: ]4 n4 O2 H3 S6 ? private void LogV(String s)
( P' V6 i% [1 ?! X! g( s {
3 M# Y3 K& [* j' L0 r0 B8 U Log.v("test", s);
* v( Y- l- j) k }. U* k3 ~ ?. Y* B6 ]$ `
. h7 l4 z4 ?0 l; F7 ~1 ]& {
private void textAppend(final String s)
6 |1 e( C0 h0 l& N9 Y {2 Z$ R4 S6 J2 u. _4 [ q( C% d
LogV("textAppend:" + s);: \0 Q5 u0 T7 }+ X
handler.post(new Runnable()' I2 j! i s. @" E1 L4 s" r
{) z& F. [8 h0 I. |; F
@Override* s, |* G8 d% {4 Y
public void run()4 M8 a) p# G0 [! s/ P
{- i) g- K4 T' P( V+ a7 b
LogV("run:" + s);
3 H1 _3 h$ |- [0 t- Z z9 W6 @
: ?% K3 g) U) t textView.setText(textView.getText() + s);
1 ^5 L5 L0 X6 {- i* l9 x* ` }
/ m7 V2 ~2 \/ p5 Y# R });
$ {, Q S: T" O% {7 q0 ?7 e% q }
3 r3 K4 \9 a' d, L, N
- S. W2 [; f$ m0 ^$ I- s private String getPath()# X3 X; Z& s# r* h' m
{
i/ V' l4 G$ o) z8 e String path = null;6 r$ W: p6 o. }5 H9 v% n% n) ]
textAppend("-----------------------------\n");
7 v: A% P+ m) E7 \8 ?: l
4 F+ ^& }$ O" m3 B K
. g0 U" R3 s' l, |; t6 h if (!ShellUtils.checkRootPermission())
- X0 n6 U9 u8 w$ F# ^4 N5 G {* t) O% W6 \, P
textAppend("获取root权限失败,请在设置中授予权限!\n");
4 d3 ^# X+ x Q, f return path;
i: r2 l( M& u9 K" [ }, Y# M& w- m) Q; s6 p: V; v8 g
* l5 x4 |6 [! a
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;
8 l( [9 o, \6 l if (path != null)
9 o% a# a" F' s! u3 Z {, k( J% b, z: J J3 f% e
try2 ?, X4 D! v0 N1 T* e
{
5 `8 b* _2 d) J+ S, d7 [4 g path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
3 n5 k; e( I1 ` q# K7 h; r& x } catch (Throwable throwable)
% N; v, z# B1 `4 d1 D6 f2 Q* V% h {8 o1 w9 Y8 l' C5 g
path = null;
+ B0 f8 R/ e J( Z }% U/ F+ h6 z/ r% e. }# Y
}
: R4 \) p0 s3 V/ x+ r8 J/ |6 R; K0 x0 l! f h6 a( K; q, ?
if (path == null)
9 f1 e+ X$ J( m) n6 |7 r4 R. k {
- [! `; z1 S b- `! L5 e0 c) w+ h% y textAppend("未找到微信安装目录,请先安装!\n");, q1 w* R, i n1 r$ f! [
} else4 N$ {0 x) \+ Y ]$ q& }: C
{0 r* `+ o. S( E2 c
textAppend("找到安装目录:" + path + "\n");$ b1 b* i- d" |9 S0 r4 ^0 \: ^
}
' }' P. U7 F% W) w& {& j& S" q" b
7 U |5 b! P* r1 s+ c* z return path;2 ~, J" G9 P8 v* c Q! ]
}
9 {! |% b# |# U. K0 M) v; N
Q' N( p3 y* b# P' S# K private boolean isEnpty(String string): }' d9 o, d3 Q( s/ }* j: L, n
{$ ^8 R" r! S3 k5 q) p+ P5 n
if (string == null || string.length() == 0)7 m" r; \: {8 c7 s
{3 L: s' b$ n& @4 A8 P+ ]; e
return true;/ R+ r" S( w) Q5 m8 {
}
5 D# t- B$ S; a7 k2 c' q$ | return false;5 |0 V' W3 w1 j- N2 u
}
. V, U- q; g$ n* x( N2 a5 b. x3 d6 B6 ?, X. W" [
private void putResult(ShellUtils.CommandResult result)
2 [4 O4 s, x2 e5 F {9 R& N& X" Y$ v: E; j. |5 H
textAppend("返回码:" + result.result + "\n");5 n' a. T) q; [4 `
* w$ T Z; q4 u
if (!isEnpty(result.successMsg))
H9 G: y9 ?9 a6 Y {
4 G5 V) v. K6 i7 V0 v$ N4 f3 H textAppend(result.successMsg + "\n");
( R# \( n$ P! ?3 J& {* \6 P } else if (!isEnpty(result.errorMsg))1 M* u1 B q: p W# p" \5 t
{) t6 r' I( B( ~0 F# w! S
textAppend("错误消息:" + result.errorMsg + "\n");
4 f7 G: U3 k+ U }( z0 v7 y) V. Z% M: C# H
}" I+ A4 L1 s, x4 F1 `7 [* i
6 F; W2 x1 y" m8 R" x1 ]. A" m @Override
0 a5 A8 _5 ^2 U: w9 K, R% B6 k protected void onCreate(Bundle savedInstanceState). r# }( I& V _7 H
{+ e3 e \ ~% R& O6 F, |" {9 e
super.onCreate(savedInstanceState);
$ N+ W8 ^0 Z% `2 Q4 U% D/ m* y0 \ setContentView(R.layout.activity_main); N1 u+ q5 W- y: n: i! _. L" d/ h
handler = new Handler();" D0 y4 N, C8 a7 t; Q, H
+ L, r; Y) \9 \2 V9 f! [
textView = (TextView) findViewById(R.id.textView);! `1 b- T0 j' t. y( u" o% Y8 m# T9 @
textView.setMovementMethod(ScrollingMovementMethod.getInstance());" d( C1 O: G2 X7 N! S. g
9 _2 o3 n* L7 ^! n( d9 t9 M* k ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener(): I+ P9 d. y* I
{
% n- V/ L# H, L8 V6 v+ u @Override
# R3 C" m2 |+ J' h4 K public void onClick(View v)
% J. \ T6 m. [! c" o$ r {8 X! n& Y4 p8 ~' y! b I% r3 A$ F1 E
% w0 G i; K2 N( `
new Thread()
" K: ~8 O$ n" ^9 h+ n" y0 l {
( }3 j" {0 {, [& R @Override& A# r. L$ c7 [% B
public void run()
- x! F- I/ H3 S {
# t# M8 v# r* k6 F% ?9 t String path = getPath();" H! v' N; c, s+ C
if (path == null)
3 H/ a; C, ]" k5 j) ?+ u {9 J$ G/ ?$ f+ [& ~! C
return;4 a6 c q, P! c4 J, J
}
% T/ z8 C6 f+ s textAppend("正在保存安装目录!\n");
" _2 k* a( w: I: N ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
' O4 T- |$ J7 |# s/ h% H2 H) N8 d) V, n putResult(result);$ ~, q4 y. D! M/ {, ^# B
if (!isEnpty(result.errorMsg))
& B) V" x* v/ O& \# ]" `) X {
& i( q# _; w4 ~( T L6 W: t return;, r9 S8 v) g1 _& z( K) W% M
}( l& S" `9 g) Q9 c# k2 i" L8 Z/ A
: D/ m. S: F- R( `
textAppend("正在卸载微信!\n");+ C! L) M" Y0 C$ o1 z3 z/ a' n
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用7 v( v1 j. v' _4 D
putResult(result);# a) M% v4 ~) W/ d5 ^
if (!isEnpty(result.errorMsg))% V6 H# L9 ^; V& J; z1 R
{
" w+ A6 I0 T" j$ C$ ]4 s return;
$ F. |. i9 X9 N/ V3 @ R& \6 N }
9 y6 Z! a' a, R7 G" g7 p8 ?+ M, p2 x4 ^* v$ z
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n"); c6 x" M5 s" L4 }1 F6 U
}
' i# _' ^- \# h& ~7 k }.start();
8 n4 E5 x- a' x" U) q5 r' ^) Q5 ?8 g0 z
}
6 {5 u3 {. T1 C# Z& v/ w });- T6 M! v# N* v1 m" r, t
5 x8 S2 e; |8 S% O5 B
' d/ L7 u; I( _2 r# s ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()/ e" N5 ^3 F) H! M9 ?6 }" S
{
7 L( J5 ]0 Y, N @Override! K7 F4 y% g+ y4 e) }- A) M
public void onClick(View v)
6 B+ I0 D( b1 e& _& O1 I {' Q# i0 i; s4 k$ {
3 F2 W ]; Q# v+ d
new Thread()
- V+ N9 \1 t/ c# t% |! r {
& x- r& ~' Y/ \. k& G; w, b @Override
- O6 V3 A- L; i; I public void run()
1 i' v9 N5 R' e! \+ H! F6 ?( r" f {+ g$ _5 e& t0 F9 ?2 C
String path = getPath();% l" l* v$ _8 q
if (path == null): C) m% y7 a, O3 D3 L0 Y7 [4 I
{6 W, ^7 Z: I$ E# K& @* d- x
return;# s/ E( |; |* P: Y6 L$ R
}
5 m) m" z( w2 M' s/ V( ~9 U
" ^. m& w8 A8 I& F8 u& F% w textAppend("正在检查是否有备份的安装文件!\n");
0 o0 d' n. ]; } if (!new File("/data/local/tmp/com.tencent.mm").exists())/ U8 S0 E& Z( B0 ~2 A
{" `( p9 E/ a0 l
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");( X; a. S' B; ?# O* x
return;
% S! g1 l! h9 h5 c. I }
: T' b" o" B8 m' k, x# t* b6 }( F5 D) m! m$ k5 u
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");
* N9 y: s, c& a( ?' L+ B8 j ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);& \2 ~' \' o% a- x! y! F
putResult(result);
3 K. j& E4 j. \" ^! I) Q if (!isEnpty(result.errorMsg))
! w# I3 {! A7 y2 U- r {. j3 }; P2 c2 W$ T
return;. p, N0 S9 u# h" c" m* k
}
* s, R3 q# [; Q4 R. ?9 |
3 q' C9 s& \+ Q: X4 I, U9 U, J6 l5 }/ j6 Q! z! z: j( N8 B( d
textAppend("正在覆盖安装目录!\n");
+ p5 Y' q) |. } B& o result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);. I3 }; p5 ~ S7 l* m" U
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
! P' p7 a0 q$ W! E, f" K C& z putResult(result);2 c; [# W1 S6 t, a5 d# r6 v& X% U
if (!isEnpty(result.errorMsg))1 w3 y7 [5 @( L! @+ m
{' k/ {" Z c F4 D0 v7 Z
return;6 I- F, a n. k) v
}% G h4 c+ l, ]5 z
# k6 r- E( r! r. [9 E; D7 ^% x
textAppend("系统将在10秒后重启!\n");
* l% i7 p: G8 H# p% v
8 p/ M0 P% C7 D- z# L/ c: \: f
; v0 m4 i$ f) B7 g try
+ @2 v6 P- ~0 [9 q3 V {
* @$ ?" D6 I, t8 y. x sleep(10 * 1000);/ p" P9 j7 s6 e& Q
handler.post(new Runnable()
/ J7 u7 ]+ ~8 G {) y( e8 Z. y- _+ F& L. [4 c
@Override+ e( g- F+ F3 v7 D" G2 m2 V; g# E
public void run()
% ?* p5 U- R5 v Q$ K {
1 y* n1 M/ t' E9 q ShellUtils.execCommand("reboot", true);
+ b3 X ?9 o" _% r( }( E4 _8 V }8 Q& v0 \6 a3 m4 ^, H1 h0 b
});+ _, r/ V. V2 \! `: u0 M2 R
} catch (InterruptedException e)/ e6 X' h6 h$ C6 z1 n4 G3 d
{$ N) O0 v5 x/ V- p( V; n
e.printStackTrace();
% ]0 x7 _9 s" { }
0 T" z8 Y+ J* {" E5 q } ^3 u8 q+ h+ Y& Y4 G# Q2 p
}.start();) V3 j6 t! J7 [0 q
* k% P& ?3 k2 Y }
7 D& r, Q0 d: C4 q; c });
( T; w1 [7 H# V& }' A; i; l4 Y( F' c0 ?8 G$ C7 l2 {: D# F
textView.append("-----------------------------\n");9 Y! y1 y) q! T1 k. C4 N+ M7 y% i' B
textView.append("该应用需要root权限!\n");
0 H1 k0 p9 x6 n7 l W6 ] if (ShellUtils.checkRootPermission())
7 G/ D0 n: m) n' m# a {
8 ?- D6 S& J+ p7 ]% e textView.append("获取root权限成功!\n");
: c# T9 P8 P6 w% f4 [4 `& d } else% m3 y, B( {, ] y
{+ N+ M8 {( p( p: I/ z
textView.append("获取root权限失败,请在设置中授予权限!\n");) k, Q" n( B3 R& a; ^
}
, {1 N& o/ S B! a9 r. M+ h% y- H( j7 ]1 [- _ [$ F! M0 X3 v
! M; B/ g5 O( L& ] r }
$ v% T5 R _ @* m; o9 ] [}[url=][/url]
4 e- l7 n6 N" v4 O% z+ K! ~1 f( s% u% k1 d" }% p
9 j m2 x$ V* U2 x4 S5 }, |" t
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
+ x# p" b. d. C1 m8 S! }4 s安装包在release目录中 0 z$ F9 ^- `; L3 I
|