设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
5 m* ]+ b. r6 X 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
' b% F7 Y9 ^2 J( q& l1 N& d操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;, i% ~* Q4 g% E0 i+ w9 v
所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
8 {3 n ?6 Q+ l i5 J8 p! H* i6 _4 P. v( ~1 G! H
查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url], k; i, c# g4 s1 J; C
if (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]7 V8 c5 ^: _3 L. r; g; ~/ N
% q( T5 M$ m" z9 `9 Z! A+ X6 v: ^- K7 h
编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,,
( u5 E$ b- F. J* P尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号, 2 I- Q0 L& H5 \* e
使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, q5 y, y- t7 F5 ~7 Q0 q$ b H
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, 7 G: q' @# Y; v7 U$ [3 {
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, 5 e Z0 ~+ M, T! `6 S) Z; x. r2 \
自动化程序[url=][/url]1 B' O. L) z, ^2 c' ^+ A5 i
package com.example.wxreversion;
9 |# U1 b% {! o/ m0 a5 L, h- _, B7 `
import android.os.Handler;) x' W- ^: }/ L
import android.support.v7.app.AppCompatActivity;
$ N/ ]8 u* u; fimport android.os.Bundle;- Z2 ?8 j+ q: K; _) B
import android.text.method.ScrollingMovementMethod;# g2 u% z: n% i K6 ^1 |# K
import android.util.Log;/ y0 T4 c: v6 G1 N# T) N! B3 ]
import android.view.View;: T, W' k/ X$ G: J
import android.widget.Button;
: l/ O. r0 b8 T5 D# U( M' {6 Zimport android.widget.TextView;
2 Q" g( z3 X+ R0 K% Y
# ^2 N4 Q' F+ v7 ^import java.io.File;
* j3 ]! _6 s# j! I! k2 ~9 ^/ G. I0 W/ W+ b' d, }
public class MainActivity extends AppCompatActivity* n# W. n5 x9 D7 d
{! ?' ~8 P: D2 v% M, ?
Handler handler;4 k0 R+ i6 C0 u% n
TextView textView;/ B) ^4 B/ X# X2 r
7 F4 F5 W2 ?& D$ l
9 O) t: t. E1 c- s" Y/ e' d. E/ O( ^ private void LogV(String s)
# @1 H: O" q. o" u6 r {
2 X8 C4 b3 p# h6 Y! R7 o2 W Log.v("test", s);
4 ]6 A/ S( D, F$ T+ Y } ?1 ?7 ~+ c Q2 e3 O/ g
2 C" I! Y2 j" t- {% J: P" d private void textAppend(final String s)
m0 E( i! \0 Y) {; D& \% f {
* _- H- c5 ]7 O# w: q, s. M; l LogV("textAppend:" + s);
7 g$ D$ c" T# H) S. @ handler.post(new Runnable()
: c2 k( ]; A7 F: ]2 m {
2 H* y: i* J8 T! G) Q, _( ? f @Override
1 n9 h4 V! l) G$ _( ? public void run()
, N& d# v& K! Z& ~1 y {8 P v; ~ v( Q4 W; ]. n
LogV("run:" + s);- K; S% q5 C! p! \
( Z' N, |1 \! ?0 H' z textView.setText(textView.getText() + s);6 |- F5 ` v$ ~( j
}
, X3 l8 h ?) D7 O1 k- u, _ });# R) K% C( r. F8 x( |) g, [! _0 H, l3 g( L
}) R! i' U' s" X$ N8 g7 t
q1 O' [! o" g7 U
private String getPath()
2 b1 l4 r0 T1 h. h* a* ^ {
1 H5 t3 {, W4 E" M9 @# J String path = null;( y' T9 y3 |9 C l7 E8 T5 k/ {( m
textAppend("-----------------------------\n");
' a- k; G/ F% S" k. O
, d# f I1 Q8 ^' F8 B9 [* W3 n/ T, M# N; h; T7 Q
if (!ShellUtils.checkRootPermission())
; x1 Y' }7 i. v9 Q" f {
: u) o1 H* U! p( |* l' W v textAppend("获取root权限失败,请在设置中授予权限!\n");3 u( U5 H2 i0 Y M* [9 i. d& e. e+ j) a% `
return path;
/ o1 u$ g6 v7 _. m( V! I }
5 Q0 I! q* R* |. J" ~, k5 ^+ Q4 r8 }1 P$ J/ x2 }. r* F
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;# ~1 c- {9 o: m/ L+ W/ R
if (path != null)
/ `+ L% N6 a9 `* Q7 ]* }4 f {
7 y z; T, b9 U5 ^# \' t2 @ try+ I: F9 N$ y; q, C; K6 ]! B
{
; \, M% _' h: j2 z path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
4 [3 O7 S8 Q' {( n0 v } catch (Throwable throwable)
& ^$ ]- V) B/ P0 B+ c {( J6 x( _2 _1 Y5 p* U
path = null;5 ~9 K. ?8 S+ U
}+ \# ^+ F4 @/ I. u2 I
}
$ P' r7 `: t g4 O' p3 `$ l& J- u' q" k+ d" c+ W% L
if (path == null)4 Y+ ?# a; y1 q) y0 h; K
{
- C1 V7 m* z: v) y8 m- O# |; | textAppend("未找到微信安装目录,请先安装!\n");
]2 r3 o3 j! Y# I9 G } else# L2 J$ K" f- ?- M; \& ]
{
9 X" K9 d6 a6 T+ g. h; a# K9 T# e textAppend("找到安装目录:" + path + "\n");
8 U. X. W0 l6 K5 W% o0 ] }0 {: q, _. Y. ?" D) C
2 c3 n/ ?3 j. j v7 l. ^
return path;7 b1 M O9 ]1 m$ R
}
/ F0 m4 u( S* i' x& T8 d& S6 w) _2 ]' ?( j
private boolean isEnpty(String string)
6 E, I& X! |4 N* ^2 V. y$ b {& r4 O. l9 R- u' y5 ]) l+ I8 I5 H
if (string == null || string.length() == 0)! Y, i9 N! a& {$ k
{
! n2 Y h3 j0 ^# h6 Z return true;
( \" R6 B, k) |, E9 I' h' ~0 G }
8 V4 b+ Y8 r2 H return false;
0 l5 u4 d3 T, _" \ } `7 @5 F* e& E# `, x5 r) ]7 a
3 e# d" A! \/ u- z- I5 f" f& R private void putResult(ShellUtils.CommandResult result)
^' _- x) y+ P8 K/ U6 s {7 `* k |5 E- Y
textAppend("返回码:" + result.result + "\n");+ T$ t2 P. | ^' {& x, L
7 Q0 F1 V% ^6 n/ u
if (!isEnpty(result.successMsg))7 K$ m3 m# q& H
{
# W. {1 X; Z. y# U textAppend(result.successMsg + "\n");& v. b# d" z: Q5 r4 e' A/ c8 l
} else if (!isEnpty(result.errorMsg))
) f$ X5 J! G. L$ a {
) Y7 E9 q( A8 X: ?0 t textAppend("错误消息:" + result.errorMsg + "\n");
5 q; i& T( x' x }# a% y- x0 T/ M; a
}
: q2 B; E9 A" q- c0 n+ @
: E+ K3 J* q2 q7 M* \- L @Override
$ p5 a9 O a! a! l protected void onCreate(Bundle savedInstanceState)( @0 {: B# x* }9 o
{
, Q/ \5 ]6 V0 |1 H1 m, V l super.onCreate(savedInstanceState);
; V/ y: [* d1 q+ J setContentView(R.layout.activity_main);1 s1 E% P0 x+ E' S: G- K, ]
handler = new Handler();
2 W- B7 @/ g: E5 w" I2 m- G3 Q# C, ]! F W B r$ O
textView = (TextView) findViewById(R.id.textView);
% D4 l7 m, n6 c1 s6 v- w textView.setMovementMethod(ScrollingMovementMethod.getInstance());* ?1 v- I/ }0 w H
* `4 P: X: V! w, C
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
( |2 z8 c& E2 o' c, [: { {
$ k7 O; N4 Y. f, R# i& G @Override7 y: K/ w# D. D, U* t$ h1 e, k
public void onClick(View v)
% ]' `( \! q# P) F- e" d& ^7 S { i, K! C3 J. f+ Y8 u9 M4 X- M
* G% y- f3 g7 c8 s" r7 D* r new Thread()
8 ~: t v4 K' b7 P7 ^/ ` {
6 X# }1 G" A# D$ j @Override
! w6 K" T0 U# ?1 _$ E2 A public void run()1 G/ y8 d- S p' v0 ~
{
# c5 b# r5 M2 T( Z3 ] String path = getPath();
4 ~2 u% ]- Y! J2 L6 ~; E; n if (path == null)7 @2 u- b3 J- R! V" U
{
$ q' n8 a+ X& W' w" _$ B return;
5 v6 W$ z: h/ Q/ v$ Q, d }7 B% X" B% M2 u+ m4 X9 `
textAppend("正在保存安装目录!\n");
4 {! f8 g$ U6 U" N' u8 i5 e/ t ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);: y: B/ y- Z+ H* D
putResult(result);
. ~: l0 u, |! a! T1 w! N$ b( o$ j% K if (!isEnpty(result.errorMsg))! i0 Z: G& W$ m
{
0 b0 }% x- Q% ?( G0 f return;
' i: E6 s" l( ^% B, ~) C }9 r. ^: e% W2 q7 x
6 R, F9 ~) N I1 _- M1 `/ p textAppend("正在卸载微信!\n");4 {; }8 x1 a8 ~% m" J& v) O9 E* L
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用
3 ^( O/ D0 M( G4 R2 ` putResult(result);) s ^5 `+ K$ {6 R5 g& G- q9 W
if (!isEnpty(result.errorMsg))1 h2 Z6 I0 d0 X
{
5 ` U. `6 M7 h' g0 j3 a! J- R) D return;! s6 {6 o# t- B# p7 ^ b, r
}
% i5 e. {3 p0 M: B) V# w# m0 D* V+ B; R: V9 J; a4 \) b
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");9 L+ T, p2 I9 Z% }( m
}# a+ [" U( j$ M5 B
}.start();
' _6 v+ D% i4 W4 d" W9 k: F0 L* l8 f. L/ o7 @
}
7 M& c) T3 d$ _ });# d5 ^2 m1 ^9 A7 a6 f' y
: F0 J/ C6 w1 c2 Q. u" ^* P* L
4 X4 u: V: z; a- Q8 G ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
0 S! u& q4 s( v6 Z3 s6 Q. } {
, t8 k" b5 Y6 ?: _ @Override- i5 q F6 Y: `" Z- {' o3 }
public void onClick(View v)
( s# B, |6 ^* e! b! j# C {" O$ V- Z' F! d) g; r+ g
5 K* W' S* M1 t$ |) I+ U new Thread()
$ `9 M) ^6 f+ ~! _; w" B {5 J- J3 ]+ A6 a0 j* Q
@Override
) W; ?% i" h( |/ u$ m public void run()
( w7 @( a. z& V$ ]9 W+ m. \7 | {
5 I3 `) x$ f2 _' ~$ {5 I String path = getPath();8 s& X7 i6 v% d( Z4 B0 p) H
if (path == null)
0 g; g. P0 [7 C! X# V. p& W {9 w( u c* t/ g1 a) l; T. U* P
return;
9 U6 |& C, u* |* [ }
0 J0 ~: |+ ]. q7 B
( l5 D2 O7 N) [7 V% b) p1 X9 D7 f textAppend("正在检查是否有备份的安装文件!\n");
+ R4 G- P! Y9 B7 S! e if (!new File("/data/local/tmp/com.tencent.mm").exists()) Y% y/ ~# `( H2 h# ]1 i
{2 q% g s7 a1 {% {" S( O3 N
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");. n5 j- [" q1 Q$ N' r
return;4 t$ c* t G" G4 }
}8 b" N8 T! E: G3 F
3 E" R% |; d* }( q- s9 u3 u. h textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");' z9 \" T, ~. o3 D; z# X
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
% ?3 B: s, u% X% v* H+ g2 p- O% ] putResult(result);
) u i, s! w7 o1 l9 |; e6 j( q if (!isEnpty(result.errorMsg))9 T0 ^' b; U4 y+ B1 s8 p
{
4 |1 o. n/ g% V7 q b return;
3 o$ o1 b: |* b6 Q4 [# K }! w5 y. Y: S- C6 ?
" }$ b4 ?) h0 v
2 ~. _ L: y" w$ {; | textAppend("正在覆盖安装目录!\n");
% P$ d: P' |5 R1 s" z result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);3 T. D4 t+ s/ h2 `, Y
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
/ [: w2 [+ H0 K" W% b4 R% ~ putResult(result);
$ L# A8 K W4 y" f/ q; m2 I, S if (!isEnpty(result.errorMsg))
( Z4 _, S! z9 J/ e* f4 { M9 m, _: _ {: d% C0 C1 _) s- T
return;
K+ C6 U. J* o }
& O8 S, Z) p9 K4 r$ D0 Y/ f5 z, }3 i9 K r
textAppend("系统将在10秒后重启!\n");& e1 J: Z# `7 D0 b; v
/ u# J& f, A. _5 r0 U4 H
1 p' T# x6 T( H7 A; K S$ |5 V try+ T7 X& t/ |: D0 O- `
{
* F1 K3 D9 y4 G/ I# R2 ]0 \+ Q sleep(10 * 1000);
$ k" m3 V9 Z. c* Y- C$ C1 E# H handler.post(new Runnable()
t* v) ^: B8 R% | {
; O) d c! S! m) t/ k4 Q @Override$ {6 P/ \; Q' y/ F* j
public void run()
* g- p" Y) A/ B+ i7 g& t% d! O/ g {
" W& m. J' d% {: S) X ShellUtils.execCommand("reboot", true);/ l; C2 ^3 }0 q& ~+ S3 [
}8 M# E7 T5 g- x
});6 l# q5 d$ ^. \* O& F) i
} catch (InterruptedException e)/ |1 A$ R) n% M8 [
{+ h o7 p3 o6 J6 U; z' Q
e.printStackTrace();9 t& j, u, O& K* F2 X
}4 ~# I! Y4 Y# W
}) k& _% N2 t: p4 I
}.start();4 M- ?$ M6 q+ @
4 U `/ _( y3 d2 e v
}
+ f; d+ F, y9 ?0 o2 E });
+ e$ [, q9 R, `7 A: v% r6 L3 ~2 C' B+ s" g& i# l
textView.append("-----------------------------\n");
/ p; t! Y7 n" F% U6 ]' B textView.append("该应用需要root权限!\n");4 c. U, {. w/ v n# k+ w0 m& E
if (ShellUtils.checkRootPermission()): P5 B. r$ [2 `
{" H# C% u, E2 L
textView.append("获取root权限成功!\n");
( R0 O1 O2 t/ Q! Z } else
& g1 P3 ?" |7 P {
/ c, p' I/ ~9 A1 O) _ textView.append("获取root权限失败,请在设置中授予权限!\n");
$ l3 x4 N* F: }" S8 u }; L# n( x0 a8 C! m& ^1 Z
* h m f4 p" s) J( [" U+ v# |
. ~! [& B9 a/ Y |8 ~0 f- G
}
' ~9 H3 ~$ W9 b0 S3 I0 E& B}[url=][/url]
4 H; u' b2 S0 L# k. x @9 K- f+ S
( I( U" k1 ?# s2 H& _
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
9 n; g7 Z. b" w; T+ G; B安装包在release目录中
" }/ _8 V! a* _. m. E |