设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 # j0 n5 k' P/ D( z. p/ w% [" z
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 . d2 Y5 e+ z/ J# ~' D
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;+ S6 E+ Q! ^7 K6 s" ^$ k' w& }( n+ l
所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
0 H& M6 y* Y [
# J: _: b; Q) P6 o. H查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
- @( h7 p J- V* g# Bif (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]
2 ~4 f4 j( w7 e& @7 d2 v) ]0 f0 i% ~' i$ h* I; D
& H ]7 R2 |+ h; r0 S! v. g 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, " R; l D5 |0 n( _( X F' S h
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
' Y' l/ c. m4 P) \( Q& ^ 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
! d1 R k7 A' s$ w! k5 S+ C+ N 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, ) r4 x- ]0 c9 k, W, j5 q7 R+ b
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, 0 b G5 W0 A% L
自动化程序[url=][/url]
/ L, S7 O( k! apackage com.example.wxreversion;2 O! b( j$ w5 F3 J
) Y0 f1 y w+ j6 v# H
import android.os.Handler;0 }+ q" T; O9 ]9 }7 l4 k
import android.support.v7.app.AppCompatActivity;
$ x6 N2 D7 D% `8 i5 P9 oimport android.os.Bundle;
* ], ]* s5 h4 t7 `' w+ u& f% Himport android.text.method.ScrollingMovementMethod;
4 b9 Y4 e' I" s1 S. t0 R" t# Pimport android.util.Log;. ]* |) C; K1 u. p N+ D
import android.view.View;
4 B# O# m0 q4 aimport android.widget.Button;
# }8 t8 D9 V3 h$ Wimport android.widget.TextView;
6 O: t$ R6 D6 M/ d$ G
, D7 M# Y, Y; y# Oimport java.io.File;# e+ }$ B# n% r9 r! R R
7 m8 i6 I" d1 V9 apublic class MainActivity extends AppCompatActivity N; j5 x6 e. N, \# \1 {
{
% q0 a& ~1 v. n7 z* l+ }& k Handler handler;8 g& Z& H, i. W* ]8 U
TextView textView;
7 [0 \8 c! z. y7 g. A4 ]' V
* e# R; K% c( d5 x" D
5 d9 o1 r, Q' v8 J0 r private void LogV(String s)7 h* i& ^2 ]# O
{+ i; Q. l- U) s8 M
Log.v("test", s);
8 Z+ T% B2 Q2 m" W& I- J }
* A& k; l: x5 Q9 H, U6 i( U. i$ c" a7 C. y# a8 }$ _# q
private void textAppend(final String s)
4 e! @$ N- m! @& q: u2 c: H {
9 ]& q T: g* L3 v6 f" `# G } LogV("textAppend:" + s);$ Z& t$ i4 h0 C, |
handler.post(new Runnable()
! v/ n* k! |! P( n {
5 r( u! B7 \$ Q# _- e; a @Override7 f: [ ^, A5 o/ {
public void run()
9 Q0 H) O6 z" a, w8 e {
0 R. Y/ p( d! O# x/ r% O- A4 T: `7 l LogV("run:" + s);* }/ ]" z+ K; t$ N8 I( }" x$ b
6 i# T: `, N9 N I( _! g
textView.setText(textView.getText() + s);
% N, I# `$ W5 L" b! l# {7 ^3 o# F }' t8 e6 O; A' e' X
});
) W- P2 I5 a- K! m }
' A/ o% M% G) W# Q6 Q. |1 v% s" q4 v3 ?9 Q! e. B- A! D( {
private String getPath()
5 w% R! }, Y7 @0 t# @, f {% w/ o& ^6 C% a5 [+ f0 v# z0 Q P/ i
String path = null;
8 m ?2 H; f1 }( @6 ^/ R textAppend("-----------------------------\n");, z- m, ?) K" u* Q0 ?8 y( M
/ v S9 E. i- |
0 }0 ^" c2 {8 ]& ^* l' O: [ if (!ShellUtils.checkRootPermission())
% Q6 ~4 Q4 W' i) S/ E- q {
% e7 U5 D) K$ J! P& R; C/ P textAppend("获取root权限失败,请在设置中授予权限!\n");
: P* E. z! _4 I0 N return path;6 I. i! j" Z" ^
}$ l/ f8 ~3 c9 `" A, l
' h# T9 U+ y% k& G4 _; P path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;, z; m i+ U- Z$ P( M% S
if (path != null)1 h/ ?7 V6 n: P; G
{, I' O9 k5 e- E
try
8 e C& V1 J9 V; ^5 D {1 j9 g- k" \2 d3 d" I, w3 d+ ? D. C
path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));- }/ S7 X& P6 X5 D7 ^
} catch (Throwable throwable)
) |5 W) v6 ]' l2 D& l2 ] {
% l* [3 b6 o6 `6 F path = null;) x# @; X' H& C- d+ `
}
4 q! C( b v6 K# X2 m. T5 q( J }" P5 w0 R" l$ S1 r8 X" \
( \, p, h- a% x5 t& a; H if (path == null)' ]9 G2 ~4 E( T6 e* ?
{
4 n/ k% m. s- K1 s9 P; ~ textAppend("未找到微信安装目录,请先安装!\n");' E$ s; ?. g0 _. r, _: W' y+ r/ o
} else
: k% z& v3 d3 F2 Y8 v, l. C {' g/ |5 j0 a1 I. d; ?4 P# _
textAppend("找到安装目录:" + path + "\n");
0 S0 _0 x8 `3 V5 A2 O }
) r8 E: \0 X* R( c8 o8 h2 \; }3 n6 C2 x! O9 C% I( ?8 Z
return path;
, N: u- h! |/ q }# ^ W/ v7 I3 C$ N( Q
4 E8 q' l2 m; R9 s; ^& h private boolean isEnpty(String string)
8 o4 {* K2 f7 q) ]' c; x3 ` {' }1 H8 _3 z$ F# B
if (string == null || string.length() == 0)
- b6 a1 c) B3 D0 X6 w4 H5 n" E% i {3 ]1 O; j: y4 t4 k% H/ c' v. ~7 D6 n( ?
return true;4 [" F; q. `& L6 S1 \6 z
}2 a" l3 I7 m, F
return false;
7 _+ t/ I% o. V+ P7 z' @) O5 | }7 A. v. F! k0 f l; P
" s; M; N$ ~2 c- ^
private void putResult(ShellUtils.CommandResult result)
! \2 i5 |, h" L {4 M* p3 S3 H* Q( p
textAppend("返回码:" + result.result + "\n");
1 l, [& ~& u @$ w$ C) o% B) `0 b: J) L8 b- k3 l6 s
if (!isEnpty(result.successMsg))
$ v4 x9 M( X6 v6 V, X4 S {
4 Y7 `0 y g/ x9 _; R) d textAppend(result.successMsg + "\n");
6 ~; l$ D, r, ^ } else if (!isEnpty(result.errorMsg))
4 w; x# w1 I- ?4 h6 ? {+ I2 }, g4 I* x5 ?0 E2 a
textAppend("错误消息:" + result.errorMsg + "\n");2 ~) i& B: k! P9 v
}) l' @/ I( X9 V* P/ V& r
}/ F' u) z3 S, Z% Z
6 `" b4 L* G1 O! i5 d @Override6 X S. S* w3 d5 p* q
protected void onCreate(Bundle savedInstanceState)! {: | ?4 E% h# q$ \) B& j) c
{, c% q0 n p* A/ r! z4 K
super.onCreate(savedInstanceState);
1 O7 S! t6 C# C$ n setContentView(R.layout.activity_main);/ u( M7 b; E0 C) p1 a
handler = new Handler();; G( g+ E4 Y" Y. g
' U+ g& ]. | L. Z, g
textView = (TextView) findViewById(R.id.textView);$ x- M) R8 p) ]1 o1 E' ?" s
textView.setMovementMethod(ScrollingMovementMethod.getInstance());6 @5 n: u) s; Q7 i }
) S1 t+ W' ?; Z' g$ w- ]( [; ?
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
# `8 W3 r) D' k8 E. w) q6 P. g2 |- y {
% x- g+ X* @5 J% M @Override
% w4 v4 N" M( i public void onClick(View v)* x1 z, k- v. B W; s
{
' E2 I5 f# Q7 z/ Q8 {
' f7 _3 b9 z! J$ W7 `. W new Thread()
+ x% f8 Q# Q9 m1 a! K4 Y {
0 p7 |4 r5 p& O: t3 b- q/ U) O @Override' P" _, n+ Q1 n. B
public void run()
- L5 M: R- F0 s2 W { p( j( E. _* q* z+ K- Z3 L+ i# h
String path = getPath();
9 \* `8 W1 I4 [/ K if (path == null)1 _: `+ Q) A% c) @: X/ N! _3 e
{8 r" a2 I r7 O+ D/ q
return;
( t/ K4 R/ Z% D: e& W+ ~: ` }
- r) p: n) `8 P4 N8 H. t textAppend("正在保存安装目录!\n");8 X. W: v1 p& \8 P/ j1 N+ g
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
0 a, X( R4 t+ J$ @, e0 t* }! d putResult(result);
4 L! G+ H7 R5 B( L% B7 A% Y$ U if (!isEnpty(result.errorMsg))
$ `* f. {8 l- Y {' l/ [) p1 q/ @
return;
* e4 `! n+ A- T }) D3 {0 |# N! {& k# G( s8 G2 l
; T1 r/ p% l0 Y' D) l1 d& q
textAppend("正在卸载微信!\n");7 x% f. S% d! z' c
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用, x. ^& P" L3 F. V) k
putResult(result);9 i) ^0 r( \! p+ A5 Z6 }7 U
if (!isEnpty(result.errorMsg))
7 V2 m9 g1 t5 e {
! L) p7 i- V4 r3 | return;8 \! Y" ?+ ]6 ]% p, W7 m" i
}; t% l, j, R8 \# h' l0 Q; a
/ A, n# a1 `5 b4 l" T textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");
* d& l9 C$ @- m! F% {5 }0 f& ] }" _+ O% \6 c" x# N9 G: F
}.start();4 w2 k& V, q. ?- o: r2 a
/ [/ \6 O) u) p% T T6 {
}
! ~4 k: L" _" p8 e });4 o, V8 c! f0 f! n8 ~( |. x
I2 K( e- k7 q9 j- O% @: s5 i0 \" {# r2 e- h
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener(), R$ p6 D: Q7 O7 b2 a- V, x' ~5 X) S
{
# D# Y, I! @5 L' t8 K+ n- E @Override- i6 ?. Q3 S) H/ _2 Z
public void onClick(View v)
# a0 |" N8 k' C5 S4 q ]3 Z {
9 t" H# |; c" D, U* Z0 l& ?4 n% S- o' _' E, Z q/ D9 I( o5 Y. x
new Thread()* l1 ~, F" z1 E4 r/ `! e
{
( R$ C; e% D: F+ q4 P1 Q @Override; e9 _, _+ \7 r {
public void run()
! b, t* w6 H* q! V {5 V: x v. F3 b% [3 | F; x+ R" u
String path = getPath();% e% V( l/ F. S$ N) l( T
if (path == null)
+ f+ D e0 x7 M& Y) A0 b6 f) J7 t {; Y6 I: k* D) h1 {2 }7 J. g
return;$ U- Z+ }* v P5 Y" ^
}
* E0 v, x5 J" u1 y" {
( v+ z9 I, s$ `9 v1 i( P! O textAppend("正在检查是否有备份的安装文件!\n");) S+ W3 K) I* t% j1 _, H6 @6 n
if (!new File("/data/local/tmp/com.tencent.mm").exists())3 \: s0 ^+ e, ~9 g6 _ z* G
{
& K* z3 Z" _% u9 H/ ^ textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");" j5 T2 ?, }0 W4 f
return;+ ~! F; T0 ]2 J
}
8 y0 h* r+ n3 ^; u1 d: U5 [0 R8 P
2 @8 j2 L: S/ U$ A. O* ?7 q textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");! ^0 A. e' J9 j+ ?
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);. R) X2 j1 w2 u- s. y, w
putResult(result);
$ \; m+ P1 i! p% P* X) _, P if (!isEnpty(result.errorMsg))! P7 I$ @8 N& S
{
' ~, B( f U& c1 _" n$ \ return;
( |0 b0 }, A2 y8 ]4 { }+ ]* s# c. x# j# l* R$ E0 X
q. v$ D& x. f* N8 y4 H
+ p7 o- G# f5 d+ b% F
textAppend("正在覆盖安装目录!\n");
) m3 a d4 M6 n) N) v: {+ }6 D result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
2 @, J& ~+ [ C+ q4 M ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);2 @% K3 d) @8 e7 p
putResult(result);
* t1 B3 T' h* Z# Q) \0 w- l! [ if (!isEnpty(result.errorMsg))6 ^% k% u4 }4 D ]
{
1 j. g2 `, z7 ?% r return;, L" N; v2 y9 o; g6 F, }
}
5 y6 V, [! F8 M& R6 ^
2 L+ B0 S0 U g- ?' C textAppend("系统将在10秒后重启!\n");) _% V' ]% X8 f: e& z6 L* q, f* H7 z
* i; O" b+ f+ c# G) ^
" `% c* p% F( c' M; S try- y$ E( H, R$ F- Y: W
{+ j- E6 s* V8 T! _1 H: Y2 v) }
sleep(10 * 1000);
! I- X* K1 v& Y- Y: v9 X: Q handler.post(new Runnable()
" \, w; h' T: j/ F D; O {
/ P$ q4 V* Z, Y( s2 }2 \) O @Override3 P% h0 }8 |5 Z8 }9 ~
public void run()5 E& D6 d9 M9 c% ] c: W7 Y
{+ a9 ?' ^6 I2 P. G
ShellUtils.execCommand("reboot", true);
- r. r0 U, Q7 ~8 w }
0 `. }3 c `+ F1 |2 l! |, A });
2 w# s7 U! y& F9 o7 P7 p3 V } catch (InterruptedException e)
. ?7 I- C! w: M' y% |2 V {
_2 ]# f# [: r L' [0 m e.printStackTrace();
5 a5 v! N3 ^* L4 K7 j }
! X% w+ Z5 ]. P+ c* C* @* g }
: \1 ~$ i2 h: ^7 o }.start();
- G6 i* Q# H9 M3 k
+ s0 P/ _! g& e+ h }
4 x* R6 H* H: ^$ S. P });
$ c6 P# |# v, p" Z$ I. G! D, y3 O0 b( L7 ~$ m
textView.append("-----------------------------\n");; e$ K; D, w t5 V* l) @: D* {
textView.append("该应用需要root权限!\n");3 t( G# c& V, U6 R7 t9 G
if (ShellUtils.checkRootPermission())1 j. q" E/ _: W$ I
{
$ v" K: p; c, t$ Q& I+ L! D textView.append("获取root权限成功!\n");
% H8 q: p& w( u# I Z5 d } else( z3 j" A) i- X4 M; u( D& h
{
- H; ~7 l- ?( u3 F$ U6 l2 ^ textView.append("获取root权限失败,请在设置中授予权限!\n");4 t: ]2 m9 i/ M( I% j
}7 X) A9 h; l& }: |" r$ Q
7 }( R/ S) v2 |, r0 J/ W
' z7 J# J3 n' X2 `9 G5 D
}
: ]5 h/ P# V' a5 Y; h. i}[url=][/url]" k% I) |& ^' _
* _4 s6 ~, Y- K$ W6 O( C
3 U# U6 M o! }
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 5 x. R/ q9 h: U; {1 @; \( x
安装包在release目录中 ) v+ [, l+ [" r2 ], p
|