设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
; v- V* H! a3 h; G 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
: j5 Q: _ W' T8 ]. \6 o2 }+ {# z! I操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
; A' n1 z. h" N& H8 t+ L4 @ 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
" I# ^: d, H# K: e, Q1 x
" g" L( Q5 J' K4 }; ~: O查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
% E3 D8 h5 s! M) S4 w, Wif (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]& |. }+ }5 i" o! v+ A3 b
! X" b- y" O+ a' H% P c7 E+ \* t( m4 ]" K6 h6 J$ D
编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,,
$ ^7 n' \- Y* R' I; `. a/ F尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
- |9 P3 E# e+ S: J 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, & Q* z1 Q8 |" b6 D+ }2 c
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
9 s5 V$ @& \( y1 u1 J# O尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, * g# \+ a+ P8 t) U4 x
自动化程序[url=][/url]
/ y5 K1 A$ u( H4 t( z! y7 y8 cpackage com.example.wxreversion;
0 A" g) {2 A f0 Q& U! L; d/ D7 |. F2 n/ N' ]4 i
import android.os.Handler;. M2 i- l! b9 N' A
import android.support.v7.app.AppCompatActivity;
4 D3 [, v: w4 H8 P j/ aimport android.os.Bundle;1 w/ P! J0 s* K
import android.text.method.ScrollingMovementMethod;
! \- G6 P' u, F% m* Kimport android.util.Log;
7 Q& Y- @4 V1 y# a& b# Gimport android.view.View;
6 X- L$ f9 z' ^) F$ U3 S, ?7 Aimport android.widget.Button;2 w( |* ^+ k1 p/ K. L
import android.widget.TextView;
1 `/ J, E5 ~4 Q8 e, k! `8 q: i4 X
import java.io.File;- a4 F7 ]$ z* K- N# z U- x4 Q/ `
* u Q/ p+ ^6 G, c
public class MainActivity extends AppCompatActivity
5 X9 Q; v1 F4 b{
+ W! m3 ~. M; n, t% | Handler handler;
' Z( ?% k* K" P: H, ]5 k TextView textView;4 k( K0 N/ q+ a4 j/ U
# _6 F; T/ G3 I u
3 @: g6 ?( K& n1 T2 T' T/ P$ Q. D private void LogV(String s)
c3 p" k( [$ [# y {( _/ C2 s) M2 Y1 O: t6 k# B
Log.v("test", s);4 ^4 g) o* W% X- v; o
}
4 O; L+ y' r* p" _* L: E
; q O; k6 w; g/ Q( m( C private void textAppend(final String s)
5 N M, N3 ?! |$ p6 W$ K+ \" j. { {' S: E8 a& h2 D3 U
LogV("textAppend:" + s);
- j8 |$ L, [ g' c# K handler.post(new Runnable()
+ e9 h' p Z6 ~6 q9 | {
0 e" c. F7 ?& v+ S% K n1 O @Override
7 Y/ J5 e5 z% R& {9 r4 J$ a# J public void run()
^, K' |: {9 ]: u/ m0 U {9 R9 w7 F0 ^. p7 q5 ^; S
LogV("run:" + s);
5 J% y: ~2 n5 f
/ ~% E5 D2 D7 E; b textView.setText(textView.getText() + s);) J% L& h; i- S5 \! t8 r9 V
}7 b2 `/ m& X0 @) _& Z
});
: L( x0 z2 j% E* ~* Z' b8 M; w$ _ }
3 o c% N. n5 X4 y' v7 e6 b
% S$ G# b8 Q# t- e7 d private String getPath()
0 R9 e; T( t7 l {$ x/ }! y t1 ^% f) w
String path = null;
% u8 t8 c+ Q$ L H textAppend("-----------------------------\n");
$ G' x, ?; i0 w! @
- x* L: f9 Y5 r& {" @* U5 i
& f& [3 q9 g! O if (!ShellUtils.checkRootPermission())+ @6 T4 k7 w" [. ?
{7 N \9 W' b, T/ _
textAppend("获取root权限失败,请在设置中授予权限!\n");
$ X% a. |' r# y7 N return path;
5 ^0 e& ?- l* d: r( U7 i2 ] }4 O1 l' j7 ?8 z( _! C! \' u/ E/ O* v7 t
2 ?' q3 e9 w* f9 m2 k8 D
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;) j6 D0 t/ d& M" Q ~+ v8 Z+ q8 ~
if (path != null)) v5 B( [/ {; p# i" b4 o
{
4 y j) ]/ ]. o' ?2 R# x try5 i2 z, T6 |5 G# d* U D, K+ T0 z
{
; ^5 }- Q; F6 n: _& { path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));% I2 p" o# F; K
} catch (Throwable throwable)
+ A4 W( B4 f+ j0 c+ i" G# j# `$ n [ {9 Z2 V" a3 v# l. K: S+ X9 g7 O
path = null;8 k. M: o* h. f0 p+ O
}
6 M) f5 R% |) p% k f+ A }( c$ ]+ e/ Q7 A$ W
! F3 f6 l+ u9 M# g
if (path == null)
1 g) {3 G9 h6 d5 r& A0 T3 D* I {! }/ }% ]6 U- a. c6 s# }' Y4 W) x: J3 C0 t
textAppend("未找到微信安装目录,请先安装!\n");
' {) K9 z$ q- J& b" @ } else4 F2 n6 F( s- ^5 B9 c" a
{
, W/ ^% u ], u0 |, q textAppend("找到安装目录:" + path + "\n");
( h3 `8 z0 @& r! X5 G* i' { }! a+ d1 o3 a7 J4 q* u9 R! m
- s/ I, D( e7 R3 h, ~. H9 p' y return path;; U' U+ Z" ?" n9 L. p8 y% S
}0 b# e1 R, s' U' D/ X& @+ Y
7 Y6 ?6 r" z x ^* w! e private boolean isEnpty(String string); i( G8 _+ D! I: [5 A \
{
1 h& n9 q9 \# M1 [* p, e9 A if (string == null || string.length() == 0)% n! N- i* V. ~# T3 a# z" Q
{5 z4 I1 L5 w# l7 y! H
return true;
% W8 N5 H% C% J6 K }& I W6 f( L- P
return false;3 E% c; M; Q* q$ L& g( @
}
, I2 }; ^; z* c" q* Q
$ ~! n9 V6 h( E9 G1 H8 _ private void putResult(ShellUtils.CommandResult result) ]) ?* e5 ?/ K% j) f: t$ |
{
8 w1 J# b$ O' z textAppend("返回码:" + result.result + "\n");+ z. h8 A% J' j2 B# D: J/ p
5 Y/ V2 C4 Q5 o' J
if (!isEnpty(result.successMsg))5 M4 k6 u4 o U, J( v8 A4 g" b
{9 r! ~9 J( t) ^( C; q
textAppend(result.successMsg + "\n");1 X& Q2 C# w _0 e5 V1 Q/ y
} else if (!isEnpty(result.errorMsg))! b$ e, j5 T+ x7 b4 x
{
1 u. F3 R1 y3 ^) z, c textAppend("错误消息:" + result.errorMsg + "\n");
5 Z/ ~ m2 B$ I- {! p9 C* p }
! o8 X! h9 ~4 I- E% Q }! A, L9 u8 `* a' x' [ L% C
4 H* ]+ s1 q% G' f! N: E
@Override3 l; b, h% g# P6 v. W! C! S
protected void onCreate(Bundle savedInstanceState)4 Z5 v+ f3 {( N2 m8 }! y/ |
{4 O O; V5 k( O' z1 x
super.onCreate(savedInstanceState);
2 ^5 X) v3 ], @) s& c setContentView(R.layout.activity_main);: H- O) S+ b1 b! h# O/ Y8 w. b
handler = new Handler();
2 r5 E2 {5 e5 p5 O
1 J8 H9 ^2 _+ W, M0 _3 R9 j1 h textView = (TextView) findViewById(R.id.textView);$ b+ a V% p- U4 j+ A& b: E, Y" X
textView.setMovementMethod(ScrollingMovementMethod.getInstance());3 V& F2 _2 i9 ?0 y6 B% `, |6 @
3 X: I- \( X I5 z" d
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
% n! i1 M0 v/ A6 m/ J& K0 K" e {) |1 G5 j O) d2 D7 ]5 M
@Override ]0 `" |: \* E; s3 X7 ^. N5 ?
public void onClick(View v)
- r! b2 q" Q' V# H8 X \ {
& X- i! @; S. V& [) a1 ^. K7 o9 N G
new Thread()
; V# R; |8 n' e) \2 G: e0 e5 s. | {
8 K( i1 n- i5 q0 Z& ^* H; J @Override# B' {0 A. _/ R1 _0 X
public void run()
% e2 F6 Y. K5 | {( P: W& g6 e2 Q8 F" E
String path = getPath();5 F" t! i) u$ a/ u+ c6 a2 Y1 z3 k- P
if (path == null)5 Y, J; p6 X+ B+ o5 c
{
5 D! r ?* |, h8 Q& D: c& V return;9 \4 {# e& a" P/ m
}' z0 d* C2 ^+ p& P
textAppend("正在保存安装目录!\n");! D( H2 U4 g, @9 [% F4 a; t8 a
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);" e9 M1 J `, _- a9 O( M g/ s, ?, K
putResult(result);
) y' \: p" F$ g3 C5 S if (!isEnpty(result.errorMsg))1 O' I* z4 U7 V2 a
{
5 v* C4 @3 w9 A7 t return;7 ^+ G, `$ o# p& v+ d2 P
}
$ H3 [" M1 V$ W0 d0 e4 r/ f( z$ M
textAppend("正在卸载微信!\n");' b8 d# S, ^0 p8 K
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用" O; f: x' x6 ^% t* R% a' k9 _, [
putResult(result);3 B9 c/ F: Q% c7 Y+ z
if (!isEnpty(result.errorMsg))
5 \( _0 r2 r7 C+ S7 n/ _ {
. Z" Y+ S! }) u M+ l% U# @( l4 d4 V return;3 s, J0 O w) ~$ n% N
}
5 {. G' O4 W- p9 n. B1 H' f+ C2 @/ U# B7 H
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");0 J7 J7 v4 ?' y/ F% K
}) f4 H" _+ G/ ~ W, H' v% K
}.start();7 W4 l d& X) s' d$ ?( e8 y$ u
: j( b4 X* U6 C# e& v; E( f }
7 m0 J1 u- Z% F4 ~5 R2 b });
& Y7 {9 j& {& I8 P) X) t# f5 _
, j; i( X& W& R6 ` X" g! g# r: K: J: T8 x# C- _# h9 Z# w
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
2 c' j5 ?3 n. l( T; `. B+ j( C {2 U! d8 \; n( A" h `
@Override
, c# _' e6 \. {' B7 E public void onClick(View v)
/ ]4 J8 ~% s. o$ } o0 [4 j' N7 ? {
; Q( d/ j% I5 [; }$ b i
2 \8 F6 V! P( O# U# a new Thread()) u6 m3 Y' I2 T3 a. ] a
{
& H' [- b0 o- p- U5 p& b. R @Override
0 |& e V* U) J public void run()2 R6 t' X `: r
{
! p, e; n0 C9 x String path = getPath();& H7 |7 }) b4 Z: `& n' r) R& b: {
if (path == null)
1 `4 P8 E1 h A4 N. x4 d8 o" m5 g/ }" V { X& I( s" I J6 T
return;7 S0 I+ I$ G& o0 V; ~, z; C2 X5 L* o
}5 ~( d" |7 A7 `0 _: ?! H
" t$ z8 w0 j( D6 Q8 U
textAppend("正在检查是否有备份的安装文件!\n"); X( L ?" C( m5 Q1 ~" w
if (!new File("/data/local/tmp/com.tencent.mm").exists())2 C3 X8 ]- E5 @7 k4 z2 r
{
! w6 O1 f7 m% o8 z, k; O textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");
- i v5 r2 L7 ]8 f; c/ q* d7 C return;% X i1 m, _. v @
}7 y) V* B+ [ Q0 a/ n
r) J% e O+ F1 I v/ l1 H
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");# X" T0 U9 Z2 Y$ w3 s; ?
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);' s$ C8 Y9 N: ]7 T8 k) R
putResult(result);2 E! Z8 g7 c% ^& u! g0 _5 }1 F) f. r. o# L
if (!isEnpty(result.errorMsg))
z' Z- l: N. T* t2 t. o6 w' d3 X {
9 K, Z) q% _) |8 }* E+ G7 a" ` return;# _6 Y" l# p0 h% ~( q$ g
}1 V1 s4 x1 w- f( k' w
) V4 J/ d, f& Q/ r
+ e1 T# |) S# d0 O/ ^ textAppend("正在覆盖安装目录!\n");+ x3 h3 I1 E3 j- Y# b
result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
9 y" `& E" W5 ~7 a' x$ c/ f- F ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);( l( o. z: W! H) L+ _( l
putResult(result);/ x+ \# @" _% q0 @3 m+ o5 A
if (!isEnpty(result.errorMsg))) E% W9 E' f, ]7 v' f
{+ g2 C! M6 X: l( ?1 u
return;+ ~- e2 v, _" H
}
! D" v7 X) A5 E0 h! I3 J* O+ O$ r/ S
5 s: b/ h% X7 i- B$ k4 I2 B0 @ c textAppend("系统将在10秒后重启!\n");* _% ?8 Z5 E+ w# ?4 l3 i/ y* ]2 l: t
" v- \. u* ^- X$ `
1 M4 `5 E% s: [& _5 z' K
try( X/ V& R) z8 H$ N$ O
{$ y; C9 k4 q/ t( G# W
sleep(10 * 1000);
5 K$ l$ W6 ], ?) I( m3 x; F handler.post(new Runnable()( T$ r% P5 A! N( c7 W1 N9 O4 I
{
; L* h( C+ y. E @Override
4 o! x0 N; I3 N7 i4 b public void run()- l8 ^4 K7 m3 x7 b
{
8 E9 T8 m9 R* i+ a, e1 C0 S' s0 T" M ShellUtils.execCommand("reboot", true);3 r5 X3 X8 \8 P, @5 ?, @
}
& O7 Q+ @: d' M. ~0 O });# e6 S! j: k9 l' A3 I) G
} catch (InterruptedException e)( w, F% Y% l5 N, O- L2 o
{
) R" b A& s$ O6 M% I8 ^ e.printStackTrace();9 [. h; V, g5 Z5 z$ C
}
% b2 } |: V% S- ]" l }
+ i' J0 ?! c( r Q% Y' J8 \" j, Z }.start();; X6 j) O; J. H5 B
: \. c R1 J8 b }
9 S$ }0 v' B" l8 O! V! B8 K });, `1 f, c" L& ^! ^! P
5 Z$ H K. J$ h6 ~# F) k, x
textView.append("-----------------------------\n");
- ^ i% H0 @- ^, q textView.append("该应用需要root权限!\n");
8 F4 ^9 o, ^, T* O' n if (ShellUtils.checkRootPermission())# Y5 A l4 w( d( z$ w; ~# m& a
{
* S4 r) L/ O* ^" M; O$ [5 j% c textView.append("获取root权限成功!\n");
4 t; j7 |- K# |# V v: a, W0 ? } else
( Y! V% V0 }: v$ @ {
3 ~! |5 ~/ E* a U7 {+ l textView.append("获取root权限失败,请在设置中授予权限!\n");1 |$ F8 |7 {+ h3 z% u* [3 N8 d- v
}
. M" z3 f& }, A+ g+ z* ]
; d# h0 [ j8 k% v- R9 q6 ?6 V
8 z4 ~# q* x' l9 p }
- {! Z0 r, y2 ]2 U$ n$ i}[url=][/url]! O- p9 B* P" Z6 y! F7 @* J
8 L. C: n$ r" h& t4 Z. \
# H" |3 F T, R3 G9 H, d! J运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 ; Z- h5 |/ P4 A# h$ H& |
安装包在release目录中
9 I6 A5 w5 g3 O |