设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 $ k8 k x# K- F
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 ( k8 F* ^, q/ A$ f: q
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
, T3 n p6 M) Y$ B8 I" ^ 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
8 B7 A4 i* _* l& p* q+ R7 u0 C
7 H, o/ M3 _# _( n H0 \& G查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
) Y- E& G" q- N( c- 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]
8 R" r) |& `2 k
: p" [% j! B2 Q4 u
: Z( Q" b" E) O8 @; Y 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, + S+ j- H. Z9 E$ e0 x7 i
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号, ! h& ]' ^/ @3 Z3 C, j6 q
使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, $ H! h4 u" y9 B
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
: [' @. i! [1 f7 W0 f( }. A尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, ) z4 F( g) p$ G+ v* U# e
自动化程序[url=][/url]- H7 m. a1 E0 M/ V8 P# G
package com.example.wxreversion;
/ R% G: S1 `1 h8 A
) L: U/ J5 L: g [& {0 W& n: Dimport android.os.Handler;
6 z8 G' J4 Q" D% r8 kimport android.support.v7.app.AppCompatActivity;/ P: E3 V$ q' S7 k B. O8 R3 h
import android.os.Bundle;
7 ` n& D7 L" i. A/ f+ limport android.text.method.ScrollingMovementMethod;* X, E! `' L( n. j x" n
import android.util.Log;3 b6 R* W U1 c! S
import android.view.View;
$ H, }0 x/ i0 T1 d+ w8 j. Qimport android.widget.Button;
. P, u6 p8 S' O, F t5 Fimport android.widget.TextView;! m' ]; C. `0 ?3 \0 w0 y. ~1 d
: E* ~; l( k" ?, |5 Limport java.io.File;0 \# H9 B1 ]2 A, U
: _! ?& {* Y$ h% v5 Jpublic class MainActivity extends AppCompatActivity
* v6 N. D( N) `1 k{/ `6 d' q! M. @/ l! }1 ^
Handler handler;
5 j- u5 _( d; x TextView textView;
$ J* M3 m1 [% C( w$ c! j, h8 a% g4 ~% I9 h7 _' I" e3 N
: ]' w$ z/ L' o3 J# t X, A* @* _
private void LogV(String s)
$ S/ v4 V- \3 Q1 O. U {* {* q6 u0 u* z4 f* {, R
Log.v("test", s);
9 [; H( Q: `, T1 b! I7 {. D }
& z6 |; a J9 v0 N' M; A- S
+ j+ L! }9 k+ o private void textAppend(final String s)/ _: u# m* x% ]' Q( f6 M
{
' k4 y# |" ~' \6 f2 v$ |+ G LogV("textAppend:" + s);
3 p# Z: c0 @7 x$ G handler.post(new Runnable()$ Z' _2 L7 P! t$ a! S# u
{- M* [4 U9 E5 P F' V. a
@Override( e6 q/ K* i' W$ n* S' M6 }; _
public void run()% E' B4 L# V" N, J9 N. P' |
{) f/ e, k7 } [. J7 _9 I8 A
LogV("run:" + s);$ P# B1 I; K- J2 i
/ V8 N4 K# ]' ]! Z2 H textView.setText(textView.getText() + s);2 C# i) C# k+ @+ |8 g6 ^
}
$ i$ Y! R3 T/ O5 z: T; S$ ] });2 Z, ?! \( b% h1 h7 s/ w8 x7 ]( N
}
/ j6 O! R% V8 L# G0 }0 I& w5 O% q; s9 _" V7 b2 X4 [/ i
private String getPath()3 Z# s; `0 @( A( {$ M/ l
{5 e' S& W" X. e( {: J
String path = null;0 m( ?3 N% V% u% `
textAppend("-----------------------------\n");8 P3 B( h* o& ^3 s' ^. H
# B/ {3 q7 w+ d5 t7 O# |' c S
7 M e- \5 y4 L: B: E# a if (!ShellUtils.checkRootPermission())
* U3 E8 H- O& y- S: e7 K6 F {
* r. p. k3 |. T" ^# b textAppend("获取root权限失败,请在设置中授予权限!\n");
+ C$ i3 M7 l4 E# i4 Y3 p return path;
1 Z) o5 q) ]8 ?! m }) \/ a2 d. p; _3 f
7 p' C* ^: d3 |! U. I/ g1 \ path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;/ Y$ ^- \$ _4 {
if (path != null)
. R. P2 C4 |1 e' A) ` {
( y2 X& K+ L3 y8 `( }7 m& b try
- S9 n5 {! w& @0 k6 _ {
8 y$ P1 z8 \4 w; U path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));" q) s a. B% e( ^4 Z
} catch (Throwable throwable)
' ^$ r4 z% r( G% ^# E {
% i/ c+ _8 T4 x5 N. e# a, d3 s2 |; X" t) k path = null;
" z+ u. g- j0 n }
6 T* ^: |4 k; L5 S" X7 y9 Y }
( O5 R1 b5 r+ k" k/ K5 _) h0 J
! V7 t9 ^8 Y2 }" @: Q/ v if (path == null)
$ z7 j2 e) U. A/ h# U* S8 H' D {
1 |2 F) c: X: @5 o textAppend("未找到微信安装目录,请先安装!\n");
2 r6 D1 u. W# Q# A, |+ V } else
5 X1 n' i9 p7 ?$ z6 B8 ~ {
: b; `; A& Y5 c: P# ]3 k( t; V. f textAppend("找到安装目录:" + path + "\n");
; a5 N0 `2 F( \! D3 ^( Q' e R }
1 n: o# x9 M5 v- M1 i+ u' n: u/ a" @
return path;; {8 c0 u) d$ m" h0 f* p& m; b
}% Q! M! A+ B. n4 c
$ `# ]6 O/ }) m. I$ E4 ?3 r* I
private boolean isEnpty(String string)3 Z2 [! b- s |9 D x
{
$ k8 o# Y0 F' w3 J2 p) _ if (string == null || string.length() == 0)2 ~/ Q) ^: j7 Z
{/ Y, r* D; e9 V+ G, i* v9 w
return true;
2 E# [7 ~7 o ?% {, @1 s% I1 c }
( l a) H* E; r2 M0 H: v return false;
9 q9 {+ M9 n4 I2 O- Q2 E- E& W }
4 P w+ s* \" B. `: h R! ~: k3 ?5 N6 Z% L1 b+ M7 h5 E# f
private void putResult(ShellUtils.CommandResult result)
- _( s+ Z: `4 F. E h {$ f; B0 r( A& J& `; { j, J u4 i
textAppend("返回码:" + result.result + "\n");, ^4 |2 i0 O! V1 ]2 g$ J* ]7 z1 G
" U! `* B) l; n2 U if (!isEnpty(result.successMsg))
0 N" o9 d3 L9 ~: ~! h {7 x3 y) e" ^; T) u& E) R
textAppend(result.successMsg + "\n");
) |) o ?; R Q) M/ F) X) U } else if (!isEnpty(result.errorMsg))
& Q" d" D/ t o' r {5 Q* c+ n) i, Q( H% w1 w
textAppend("错误消息:" + result.errorMsg + "\n");9 {% G7 L, H# }( r4 G8 X0 O( p5 @) W
}
3 e0 W) `$ |6 M% h9 w6 n- ` }" O9 V* U5 D y. {
; X& R3 L7 X% W' T( ~# \0 t
@Override
, A, k0 }; ^# \' |/ Q" w protected void onCreate(Bundle savedInstanceState)' a% t7 r! ?* E! ]( {. f0 W5 U" K0 a
{
, y. G: b" T) Z2 e0 ? super.onCreate(savedInstanceState);5 n+ A. v4 C |+ C
setContentView(R.layout.activity_main);/ \/ Y. L: ?( _# ^2 X/ r1 @
handler = new Handler();- p. x/ X& \; A: ~
" t( _; F$ e# P8 z4 A
textView = (TextView) findViewById(R.id.textView);5 d7 p! K/ [ y7 G
textView.setMovementMethod(ScrollingMovementMethod.getInstance());
9 f8 s, j: ]- d# Q' {4 \& A0 k
( O4 c' }) O/ ?/ l2 o+ b) @ ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
9 s! @$ S2 n( R4 p3 y% a {% j1 I4 p7 [* R5 C# k
@Override# o; b7 R0 o# L
public void onClick(View v)3 U( k4 `. f, v
{
: }8 q' |( G6 m" o
6 ?: _. l* P! {# Y' E$ c# a new Thread()
- z4 \* T O9 T$ O7 g {
! P% W o/ d. u( j# { @Override
( p3 I ]/ W4 q/ ] public void run()
( I. U4 t; W& i# [7 E+ h( A# } {
, C. U+ `. ~8 S' q String path = getPath();
: H" f; l4 _5 K* f# ~1 Y if (path == null)" G2 [+ {5 m7 c
{7 w8 }) L( U' ?4 M$ u" y3 p
return;
3 o- p- V3 R1 D& g+ V, G }; V9 J ~3 ?: M X6 e8 \8 ]9 T
textAppend("正在保存安装目录!\n");( \+ L! w$ l! F1 }' U: y/ U
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);9 [' P9 F" p8 ^7 u' H; w
putResult(result);
9 z* O' x5 ?& M! `, B if (!isEnpty(result.errorMsg))
8 E5 }! y" w( w( r* }( ]3 x {4 d7 @; ~ q4 A& w( d* b' x
return;2 l+ U* g* e% n2 J
}
" ], T' X5 S5 n' i9 M% w1 E1 [8 w- A2 ~/ l
textAppend("正在卸载微信!\n"); ]2 Z, { {( R( S
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用3 u( i3 p; r! ^1 `8 }9 [
putResult(result);- ]0 _, k5 m. h# v, w
if (!isEnpty(result.errorMsg))
3 x* ?5 @1 e6 F9 ~$ R" v {
5 \6 _2 A' O1 N7 r% j' k' `: X return;' l; _7 q$ W8 k0 {9 Z T
}
7 e8 ^, o" J4 K0 }5 Y9 X* I# h4 `8 W1 O
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");8 F0 x' c6 D% m8 G7 M) Y# ~: F* g
}
1 A/ B! T: t/ j }.start();9 ?# j7 H- }; o; ~" E9 j
9 C8 K9 B$ d3 Z0 G3 _
}3 {3 h3 a; ?" Q- e$ q
});
( L% n4 H! } I! N) Q/ b; {# ?
( U7 m8 I' X$ S. v/ t# Y1 b+ f: O( {( D5 ^! R* A- S
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
; E/ A4 S" c* m7 f4 R0 m {
3 L0 G! q% e6 s+ g: Q7 n* Y @Override6 r$ w; o8 ]% N6 O
public void onClick(View v)* `; w# z* g9 g' K
{
; W G# W$ m/ B7 I
2 u( [8 ~8 N/ j- p; B new Thread()% L ^: d6 K* ^+ U
{
/ }- ?* d' Y- \# N+ _ @Override
: ?4 P" ^% m1 k% r/ H public void run()
6 ~' M3 |$ m9 W% [6 W+ f5 u {; \5 m. ]- @+ h
String path = getPath();
4 }4 Z- b$ c8 z- c- b7 i( M4 g& V if (path == null)
3 O: q: |* w1 h6 |& c2 r {
! ]0 w. \# P* D return;
$ D8 m/ r+ u! @6 @- A- V6 ~% F }
5 a4 f- H0 e. \* E! f! B: {2 W+ [( G
textAppend("正在检查是否有备份的安装文件!\n");
3 }$ ?& Z/ S) ? if (!new File("/data/local/tmp/com.tencent.mm").exists())
, I$ b1 s: j) Q3 h {
V2 r$ ?6 p5 v/ l' _4 V textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");! @# r, O6 v% Q3 C% f
return;- A+ k0 f+ e/ ~% E
}7 F. \9 G0 x; v* t- ]$ B
9 j2 q j- e3 s' W9 }/ E; w; [ textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");
2 B/ U& d8 s1 _& | ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
, O7 a. ]+ s$ K" j" b# \ putResult(result);9 _3 d1 e6 e8 ]
if (!isEnpty(result.errorMsg))
6 ^! i; q- B* ` {, p4 }+ e" w; M( V! _
return;5 U3 @" \" C8 I
}
9 w/ ^3 r! r0 H8 T
2 b* d7 ]& [5 m3 n2 H6 ]0 g2 k. r) P9 Q9 G7 U8 S: E
textAppend("正在覆盖安装目录!\n");
4 r/ K1 t0 I: ^% I result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
: S* v" s8 g: Q ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);2 r) a6 z# f4 ?* {. W4 T
putResult(result);0 j/ ~; b* U+ s
if (!isEnpty(result.errorMsg))- C% M4 K. @6 t0 a3 k9 l
{
( M9 v0 X9 X# C. E3 y return;
7 s1 E( c( p& N- {! t# S8 P }3 E/ }4 r: Z4 r2 V0 v6 Z. p; M
/ D0 R3 B d3 O textAppend("系统将在10秒后重启!\n");1 j# u: ^/ `3 Q3 b# |% X
+ x9 J8 V- y5 v/ x2 ^2 l0 M
, s1 K U% u& \; h try; ]) ]) Z8 e( s* o) G1 t
{
: g1 D- D# H! Q; B4 J3 s sleep(10 * 1000);
$ ?) w) i& D3 B handler.post(new Runnable(): K8 F2 d1 _4 ?/ i
{
2 }; L8 W8 `3 C) _ i. L @Override7 P0 V; D7 ]$ C, Z
public void run(); f) u0 e. ^3 P7 `/ q/ T
{* {. W: e0 H5 p( ]; `7 x& M
ShellUtils.execCommand("reboot", true);2 V! S: R J0 v& {# {% h* w% l
}, p9 U% R8 `7 e
});
- N% q& i8 W$ W; c* h } catch (InterruptedException e)( v' Y% V7 l0 l; x4 }
{7 G9 J% E% [$ G/ T- x* W. L
e.printStackTrace();
4 d3 ~2 g8 l1 }* M' r }! i8 S8 N# I, b2 ]8 [; K9 A, F
}
. R. }9 ?6 D1 _: ^ }.start();
8 _( z+ n P, B, `' W9 N
: Q5 r& M2 X! \$ N$ O, R: O# W }* H" V7 s1 n0 x/ f0 v
});
- I, `4 I5 D- J! e8 S+ ^. l3 h0 r" N/ X9 E; ~7 c
textView.append("-----------------------------\n");
' P0 D& A% _2 ?; r, ~: O textView.append("该应用需要root权限!\n");
7 n5 U9 F; Z) k5 j if (ShellUtils.checkRootPermission())5 E0 W2 E" J% ^* N- C
{* L/ U' F. [, ~$ U
textView.append("获取root权限成功!\n");4 |; B: j$ R- g
} else/ Y: u- x/ [3 H* v
{
. W" s1 u: O8 D! T textView.append("获取root权限失败,请在设置中授予权限!\n");& @8 @+ _4 r- S6 B8 S
}
4 K- [; I0 R. P d; V6 m+ v) P. e* V/ O* D9 h4 S2 i7 e
2 \2 @; X0 W. Q9 | }8 \6 W' v& |4 x& m: b" F( B- d
}[url=][/url]0 d' u+ t2 E# k
/ C3 E1 q- |9 b
8 a F+ m$ o$ H% s* r运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
" D$ f( x1 g8 q0 N/ x& S/ A安装包在release目录中
' W: k+ x- f' d2 B+ a3 Q |