设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 " c" T0 y; x, ~8 G$ v: D
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 . D. u' a4 |8 s$ W5 g0 L
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;) ?9 ]/ V, t1 k. ]6 V
所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());6 E: e8 _" C4 F' X& z
. w2 R5 O, B! x8 R/ W+ N查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
7 C' r& a8 d* ]& j5 i8 Z) 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]
" `8 U4 H' q. m+ F7 h! V0 v7 `) ~, ?3 W# k) }/ s7 ^
, X9 F: [9 ~' _% ~ 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, 5 x* U# ]' d- P! @$ F% y- \
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
5 m+ B2 o' u! U9 E1 ` 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
5 I& w4 \+ m2 J- F- P* j1 v5 F% S5 ~ 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, ; e5 y1 Z9 H/ |$ \+ O) S
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的,
! {9 [4 [6 E1 k# T# u自动化程序[url=][/url]
# J6 O# u+ H3 u" G* O+ Z; {package com.example.wxreversion;
; F6 b: O3 g) r" i/ k
8 @/ L6 p/ s/ J9 M# iimport android.os.Handler;
" w0 V! k i2 uimport android.support.v7.app.AppCompatActivity;6 b) Q" U% W8 i8 u
import android.os.Bundle;
& U; J+ t2 H. p+ m# Uimport android.text.method.ScrollingMovementMethod;3 F6 ~' o1 R& ] p9 J- G
import android.util.Log;
$ h& m& _! y0 i1 V% p+ z$ wimport android.view.View;
* h D% {/ M: C3 Zimport android.widget.Button;
/ k9 e* l' \% n1 \% w/ simport android.widget.TextView;
* U6 N" r% T7 L' e
5 o3 D$ H6 E: ]* z Y. H3 nimport java.io.File;
6 J% j @* g0 A4 o1 `
8 Q, f6 a1 s! a( c ppublic class MainActivity extends AppCompatActivity
% o) H6 ~! \6 ~. M8 q$ P$ i+ K{
7 _ a# w: _. j Handler handler;
5 G' A6 z* L @9 H& ` j TextView textView;- Q- u, l) u9 t
$ g- w$ G' j1 }4 i* a2 m
0 z2 |* ~7 U+ @8 W5 o: F4 [ private void LogV(String s)7 `' h; H9 R4 k9 e h
{) p7 G b( }# c" X1 G( M$ ^
Log.v("test", s);
0 q4 Y* {2 I: a. v1 \$ j) x( i& Q# ]- S }
/ x& R6 I, Q) K( N% {0 r
) r. g! B$ C7 K; |$ D/ q private void textAppend(final String s)
V( t" q g) u4 h4 s0 }- Q* L% d {
+ Z6 r) p9 D/ S, T4 W9 F: K LogV("textAppend:" + s);
/ Z- r' [9 f6 w% K( h. n handler.post(new Runnable()3 f7 p5 A& }- Q, P& k. K
{
$ e# y. K1 i- Q$ I2 T! p$ I% Q @Override
. R' P% W6 V X) h' p( L public void run()2 s( l- r8 u% S: `* \
{
8 f6 p1 Y/ r- a- z) o4 P LogV("run:" + s);
$ d( v- _, B* _: \& \- T p( x8 L4 Z2 C; N1 K. H- W/ c' @
textView.setText(textView.getText() + s);% t. j) }; R* D1 Y
}
5 F) D/ K% j2 W& t });7 Q7 e O/ K% t r& W; W6 n
}6 U% {9 K7 K, Y
3 Q" L" o9 F$ ?8 V" [& f
private String getPath(); R, i: t5 L6 A! T5 ?1 p# L( V: C
{6 h: V- B7 X! P
String path = null;2 F' Z5 M9 _7 {% O: s
textAppend("-----------------------------\n");1 u3 T' {* e \4 K v6 ?
5 r7 {( [9 }2 K2 a% |* y% e4 ~
- [, @3 f9 c% v if (!ShellUtils.checkRootPermission())
7 v% i/ k% \" s' l. E8 L G3 b {' |/ Q1 ` F2 ]
textAppend("获取root权限失败,请在设置中授予权限!\n");
! ~- e4 S7 ^& e6 U C: @7 u! L+ n return path;/ B; W8 u9 |3 x, j
}1 `- u, }4 B( I2 u5 ~3 z2 }
+ p3 k7 w; T% a1 R% R path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;# F8 f4 p5 r% |5 Y) ^! B$ W
if (path != null)$ b& H8 ]6 `* F" L- @
{
& D) M, h+ x0 a" x3 X5 P try
4 s4 y5 ~& H4 M) ]! f. R {/ s4 H$ D* g) I! Z. _( Q: q
path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
4 W) z1 S ^5 W. L4 E } catch (Throwable throwable)
7 S( ^# {' _ u3 N2 l {! I( k X' j: ?8 A; M& W$ L9 a P, [
path = null;; I# v- b! D4 M2 ^5 |( t+ X# S3 u# d
}( k1 z% b) |6 c. I! j; E
}7 P# b! Q! O' f9 f S
; A5 T! u) D8 e1 |) v! ?& I3 Y if (path == null)& R- L& N3 x$ i$ `
{
, ]8 _& K* G' ~0 G textAppend("未找到微信安装目录,请先安装!\n");
" M6 `4 O N+ | X4 I } else
7 B# h5 a+ }& m/ X3 s% {2 n, _ {
3 d+ g* e; w, b! q* u9 H' ^ textAppend("找到安装目录:" + path + "\n");8 y: [. [6 P. D) d i
}) P8 ]2 @5 J: n! ?3 q5 j2 E
, C5 }; G, V- x& A) D3 e" T, w return path; o/ N% M3 Y; X ~' Q. V4 t: a
}
6 Z& c" Z' L8 m1 n8 w4 Z/ c" c A. Z; K1 N: u, j x: C
private boolean isEnpty(String string)# R7 C& ~) q- F, M/ F8 z/ ]# B
{
( |( z2 N2 ?9 D1 c if (string == null || string.length() == 0)( W4 g0 Z/ B, S$ z) p7 _4 W' V
{
t3 w; r- g' P4 ^5 u% k$ Y return true;$ S5 v% l' h- ^
}
7 ^: ~ Q7 c/ y5 ~/ l1 i! p* F return false;9 a# F0 r( \3 A/ }. s; R
}
* E/ y. W% v% U9 J+ P( [( q7 a; Q. i+ T% R0 }- A
private void putResult(ShellUtils.CommandResult result)5 x8 o: W) j" j+ C1 o1 H3 h+ C
{5 A; \% _( v' q- L0 L' z. M
textAppend("返回码:" + result.result + "\n");
" w& B; Q" P" b7 l. X a( h9 [
: G0 S: B# n' D& K4 d if (!isEnpty(result.successMsg))
% y$ E; ?/ |$ |6 t {
) b; \" x5 h$ P$ P) S& w textAppend(result.successMsg + "\n");1 x$ {* n" L- ^) P4 g" k, F
} else if (!isEnpty(result.errorMsg))3 u7 l& p+ b ^" a% X! d
{
) I( b8 L" Y9 ]# p; V textAppend("错误消息:" + result.errorMsg + "\n");9 K b1 a+ W8 M
}1 b- b: B1 e4 X) H4 T
}
# @9 d9 B$ b0 B/ {- @3 d; }$ x5 r+ X0 P5 X
@Override x5 [+ o3 K8 D6 ^1 I
protected void onCreate(Bundle savedInstanceState)2 E7 D5 C1 s' A' W/ o7 P
{$ ^ E& u+ y& U% a
super.onCreate(savedInstanceState);* C5 ?. n) h3 x: f; Z% [6 i
setContentView(R.layout.activity_main);
. u1 {- B# |- m+ ^ handler = new Handler();
4 J) Q b/ B% w& g
+ o& I# b5 X& J3 C2 ?1 g textView = (TextView) findViewById(R.id.textView);
( O$ j) o9 p( ]2 v/ Q$ ~) o: `, ]& ^ textView.setMovementMethod(ScrollingMovementMethod.getInstance());: A, r& ~9 ^8 p/ e3 V
; t( t1 a4 m% S ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
0 r5 P5 q2 \8 Y3 T' E( j, u {
" V* D- T& V& h# k/ S: S0 h, i5 @ @Override4 U D8 m% @6 O& Y# n
public void onClick(View v)& v! A8 x( F# n6 U- o) d
{9 j4 K6 C' E* I4 Z
# u8 d$ I7 L# I/ T& H$ m
new Thread()
; O, e2 b8 w+ F! N {
# K* E) {; ~8 H, g/ M @Override
0 ^4 q1 [$ O0 E6 D% A4 _! n6 X public void run()
- e- u: r% R( I/ {$ e8 R, V+ } {& \3 I$ q k8 ?! F( _& W7 V
String path = getPath();
2 i: _1 u4 B/ @4 ?2 x4 z. S if (path == null)
7 R+ V5 T. ~+ a8 ~/ b" {) Z. x, H {5 u# [: e5 Z/ M5 @
return;
& _) B! I! p! Z& B7 O }
! c4 Q# X( n; V9 E/ T0 a( Y/ E textAppend("正在保存安装目录!\n");# t( @/ T: I4 R" v' O! O
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);' X. d t5 c5 F1 }2 K
putResult(result);9 ?1 I# O: q. v* L* D+ q
if (!isEnpty(result.errorMsg))
6 u9 a& ~2 A4 l# _( i2 [7 f+ g$ E {' }; \* `% \: `) Y
return;6 q! g1 u$ h2 x! p4 u
}
+ ?0 f4 ?5 I. |. b! ]* m3 W' e1 S- K1 A5 \7 u& g& W, t) l
textAppend("正在卸载微信!\n");
) \6 V4 ]: Q. |/ F; g- ` result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用
$ T6 i+ ?# n7 T) u: f putResult(result);, B. x; R: E* k3 l3 m( h' ]6 ^
if (!isEnpty(result.errorMsg))
# b4 c! S6 A) i0 p { p: D! h% b' X8 U
return;
9 `6 i) ~& T8 [* C }
4 R' U4 E' Q9 O: R- Z, x. Q: h8 J% V) R/ F- C
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");& _1 e5 [2 z5 t4 E6 [1 _
}4 K3 f3 c" N+ r9 w# f6 B5 t
}.start();. B7 Z' q2 B9 t% i- O/ g. a
- O: {" {+ j) D# g
}( y. C0 \. j9 i1 y9 g+ x, h
});* `' Z* m' P. j+ v3 q1 H |: M
- }- d0 o6 \- j
3 y E8 ~+ M! q7 ~) \& E$ c ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
% `3 q# O# y& I+ ~5 \0 m" K {
+ G2 \: q* j4 C8 a" k0 M @Override0 v3 v4 y. X( h4 M. X3 g
public void onClick(View v)
0 M% o; [+ \3 s8 Y {
9 }0 L3 i3 D. ~: W- R
: E2 Z# d! b4 \ new Thread()! d) x5 o: q+ c0 s; M/ i
{: I# b1 h8 q8 q v B* _
@Override! i a3 K; n6 R1 V5 G- `
public void run()
0 h1 Q, _' h, G3 z0 O {* E6 {# \! Q/ Y" Z# x, u- J0 e9 V
String path = getPath();
% w1 U0 {. v! F% p" t if (path == null)
& ^" s; U3 E' i {: B$ N* e4 B/ e* a. P% L
return;
4 |/ O- s: \* e# i }5 w4 s; ^' m# i- i' B8 k4 F
, q/ M# S( E9 n6 d) X2 n7 Q& T textAppend("正在检查是否有备份的安装文件!\n");
6 S6 C: \2 d) Z* ^% r if (!new File("/data/local/tmp/com.tencent.mm").exists())/ U4 k+ s% t8 I7 X _, \; m
{
4 z, ?3 j# d; x6 E textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");
3 E$ T# F; ^+ H; @3 T1 l% \6 X0 R. g \ return;
) o( z6 }0 Q6 v9 M w0 o. k0 t. F8 ] }1 L" |$ t2 u/ S4 j- {% w! F
+ O8 w$ \) m' r, g. ?
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");7 h0 f6 x' g# \- J! v5 G
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);' Q0 h! ~! ~% v
putResult(result);: ~9 G0 }' \; `& P7 |) F
if (!isEnpty(result.errorMsg))& n2 A1 j1 D9 M, S. H7 S" ~
{4 Y" S4 s' G4 J( W0 Z2 A) _# D) n$ A
return;3 Z: r2 r2 T5 I y8 \
}
9 I6 L4 @% z5 ?4 \4 w) e# w( Z, R- O+ o& x% k( @ z& n+ S
: w7 r6 |1 J$ Q+ i4 J3 U% m( r. i0 t; n textAppend("正在覆盖安装目录!\n");
2 W }& i# W* p' y result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
J1 e5 K; m1 w# |! a- o ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
$ d! L0 z0 B B) H" l" l& i, V2 C putResult(result);
0 H# T- \- v' ?1 d6 ] if (!isEnpty(result.errorMsg))& k( J9 F) F9 a5 Q
{
9 j$ t% v5 T: [9 [ return;
+ O0 u0 X8 r1 l* S3 J" T }
3 {; ?# \ P: e" c
& s. m" h: t/ K- p7 p0 k$ m: b2 N textAppend("系统将在10秒后重启!\n");
3 _5 c3 ]- Y& t2 E" X& U" ~2 a2 r
7 g$ {7 J j5 l% E
. x. x3 ^9 }5 j* \& O! S1 @6 J try
0 g1 [" a% E A( t v/ E {
" ]2 K/ x/ V$ k: H5 Q' C' q! [ sleep(10 * 1000);* x0 |+ p [3 n' @0 `$ k$ D' G% P
handler.post(new Runnable()+ r5 r% l6 y- C( v
{
4 {: C% n- f- w" O' d0 N! f O( P @Override3 E$ k5 k8 }0 X- j5 W" R# i$ x
public void run()
3 b; V4 r5 o1 u# e& r" ^1 x- Y {
+ G; _# M$ `5 s f9 b4 o4 u ShellUtils.execCommand("reboot", true);+ s- [) A) G3 O5 K( l! S
}
! j" [6 W* C/ b# |0 Z" r& W4 B/ e3 L' I });3 ]" j3 N0 E4 g8 [ @) h. k H+ D
} catch (InterruptedException e)" j8 \3 L2 f; u5 Y
{
* A$ H0 [/ q0 v- `, g2 w e.printStackTrace();6 g/ f% y4 H. k1 y8 {' X# X: k
}' u- u- X' Q7 f8 A* q P- e
}) T0 e4 n" Q1 o$ z2 z3 I
}.start();& M) h1 W( P, f& _. U1 B j, B
' k& N, h8 v7 J
}
/ a+ |6 g+ p8 ]7 b6 ] });% R9 R; ?& y8 g
& n* ~. \. Q9 Q) S textView.append("-----------------------------\n");1 h" i3 c$ i6 [! y! K5 b
textView.append("该应用需要root权限!\n");
0 e1 _: a2 }* o if (ShellUtils.checkRootPermission())( J' Z6 N1 T- ]6 v2 a
{
4 p# v" J! b$ y) H textView.append("获取root权限成功!\n"); F7 p, W! R4 h8 C- ?
} else$ B) T: y2 o' k$ ~4 r. Q
{" d4 E2 k; U4 _
textView.append("获取root权限失败,请在设置中授予权限!\n");9 V7 U& J2 m G" q% d4 A, ]
}
7 A! J+ Z+ f' D1 T- {3 p: n! g8 I' v! X/ ]' ~$ [# D
) S4 T& X* V! {/ [3 q& g& O' i7 q
}
. z" R* u- l1 ]; z; f8 R/ h6 g+ v}[url=][/url]
& A7 _8 F4 O) O' @; L* ~* N- |
# |! X* N7 U8 }( G4 h$ {( \, G# m" X5 ~8 z, `5 S+ w" d/ D1 D
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 * j6 }- ?5 T: Y+ h# s
安装包在release目录中
% Z* u* ^+ f" w% i; K2 w |