设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 / c9 l# z" ]! a8 q d! k* D9 e
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 & g9 P0 O( Z, y9 W
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
! a. J% N& R8 O1 l1 V# ^ 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
% H7 U8 R8 M4 B7 H
) E; F! ]+ W. @9 P" i查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
. h. _4 S0 ]" Jif (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]- e+ j, v8 {7 o
" v' p# f1 c3 R; }. t- q) f
+ `# \& @2 B( d8 X; x
编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, + c. G- C6 }$ f8 k
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
: C/ I# w: ~% h 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, # a& U5 N& `7 `
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
$ h6 z+ n: I+ d. z尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, . n' [$ W, V& j7 n Z
自动化程序[url=][/url]0 W" N; \' L+ J6 e3 U, {/ _9 `# M
package com.example.wxreversion;
1 o+ q6 V. W# n$ H3 q: C
( t C e3 f* u, G$ i) g$ mimport android.os.Handler;# o4 S' v" U0 m8 k+ R
import android.support.v7.app.AppCompatActivity;, L* u. G4 o1 Y. y& F7 [6 `
import android.os.Bundle;% l! ]5 y$ J* Z8 U3 B6 s
import android.text.method.ScrollingMovementMethod;5 T& e$ I! ]) i \/ T
import android.util.Log;
4 M3 }+ _* t$ |' h4 F' C7 Pimport android.view.View;; c' n7 C' Z c7 I
import android.widget.Button;
: Y' v' k5 b/ B- q0 Simport android.widget.TextView;7 b& E, d% Y. t% |( Y9 _: P
* h% C8 z: i( n8 [" X% Nimport java.io.File;4 o6 E8 V2 f% T* V; [8 N
5 Q" v/ p9 Y& g, v* u/ \0 Zpublic class MainActivity extends AppCompatActivity
* l6 o0 d9 C: t8 _9 `{* c- @- n* P1 D$ D! z
Handler handler;9 G, ?9 e9 ]6 g6 i* N) e0 T0 p( t
TextView textView;0 h7 m8 N% ?$ @# L
$ q- d( l# O. N; N8 g; a9 [/ C, I x) H- R$ P
private void LogV(String s)0 ]: ?) o; L3 x% ?- {. S
{2 V& O' e$ e3 i# g
Log.v("test", s);0 J7 }. _( d& I3 Y5 M
}
# n5 e( x" r7 s; F# t! T. H8 d5 S. c @
private void textAppend(final String s)9 ]8 W$ m7 {) w8 J6 P2 R
{
7 H C4 M9 F5 n, k7 t8 ~ LogV("textAppend:" + s);
+ n2 S! v; U9 E$ p* I handler.post(new Runnable()
! p8 m4 ]% h& |/ } {
' f8 y! E0 W& E5 _% x7 j8 p4 c @Override
+ P4 Q" K' P! d O2 N public void run()
4 p9 W5 T7 X z {
& s" T1 K( T9 R& I4 |7 b LogV("run:" + s);
) K) C' ~2 h2 p" { s9 S7 |: c& E/ e5 m- {/ X
textView.setText(textView.getText() + s);: M6 P; a9 ] W! Q* C6 H. Q
}
f* ?" K# a& u5 Q: @, n: D( J2 X });% ]1 p6 G3 l6 P; e e
}( W* ? `7 h! G
' Y! }+ e9 D* T
private String getPath()5 U8 A( s/ O1 c' Y8 ^ ?* v
{
& y9 R/ p4 k: _9 u& {% _! c String path = null;. Q" e, Q) J1 H6 g$ ]9 A: G
textAppend("-----------------------------\n");9 r) S. O \6 m/ F
" L2 ]0 o( q. U1 W' p" o M" C" c/ a1 X" S, b. r) Z& S# N3 d/ f
if (!ShellUtils.checkRootPermission())
0 ~* h5 A d; S5 P {" ]" e) Q- R6 z; ~3 O* \1 E
textAppend("获取root权限失败,请在设置中授予权限!\n");% T- A8 x/ P9 r" g; A8 e2 r
return path;6 _9 @( @, X; x$ O7 l
}
& @: R# r1 l \& p) ?4 e. P8 j9 H, r, e/ {
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;. A/ Z- v$ J" F$ Z2 t$ J
if (path != null) L e$ P! G5 B) [+ x# J5 a% w
{
3 v" c8 \# |: v k* M3 d5 n try
) S8 i; W% m8 `; d {
% m" f5 ]" K3 C5 V, ` W path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
U; Z$ I8 D. W- Z0 \ } catch (Throwable throwable)
: T% b, ]: h& I- t {
1 {; R8 F; D4 x7 Y! [3 l path = null;
/ s1 O' |0 D& `) G3 }; h }
1 q! c V; S5 E9 m" d }
" i2 K' m" K$ g7 i7 {( _) S
$ E: S% r* I8 T7 K f' L if (path == null)
; \9 w& S) v, o {
& R. n% e8 ?8 S+ d! N1 a2 P textAppend("未找到微信安装目录,请先安装!\n");
& j3 X2 s$ ]: D; ^$ i! a* m } else3 Z" T" K0 J. G$ @( I9 ~
{
2 u7 S% {3 e( w, J textAppend("找到安装目录:" + path + "\n");/ q& S$ R( k1 r# f
}
T1 s7 ~1 M" ~, |9 m
, I. T5 ]' r b z1 h4 C3 p7 T return path;7 }' n& `( Y& P& u4 Y
}/ M; D- L8 a9 n# z
) a- n$ ]( j& R2 E" s private boolean isEnpty(String string)
$ W5 F! P+ F9 M {
6 `) e3 G+ J1 O2 Z( x if (string == null || string.length() == 0)
# I1 u7 m: \1 t$ J {
' O }; C+ W" @- k return true;
9 [! u* J9 M9 e% d0 J5 b7 V( x }
r* C+ k. `! Y, R+ |" O return false;
: W: F' g2 ^6 H5 N! x1 W8 N }- O3 c$ Y9 E2 A8 l9 o, _' k6 a
, e& u3 k$ u% U
private void putResult(ShellUtils.CommandResult result)
1 D3 z% [2 ?% W! j" e/ { {
' D) p, S; `% u3 w" q1 |; q, M textAppend("返回码:" + result.result + "\n");
* P" e4 F' `- h: W- Q$ Q s! A- \ I, [, k; @
if (!isEnpty(result.successMsg)); Q- U5 A& O6 E6 g1 f% L, D
{& o% o9 S( g8 }0 U
textAppend(result.successMsg + "\n");' X. `, d6 A( x
} else if (!isEnpty(result.errorMsg))
, [; X. k6 D1 `4 B {( N6 n) }% }6 p/ ^5 e
textAppend("错误消息:" + result.errorMsg + "\n");
- U4 L4 v6 Y0 J$ P }5 [% O) C- R$ X
}3 ]8 Y( z. c% Y2 b4 r, w! Y |
1 f% P! ~% w+ ?( i0 e! w# ]7 J @Override0 R4 v/ a. }" K8 c J; i
protected void onCreate(Bundle savedInstanceState)3 `7 Y& _ D( c k" H
{8 y1 @, Y5 q) d1 D1 Y, _: C& q
super.onCreate(savedInstanceState);
0 a; _3 h( X7 N; w: z/ }/ A8 |/ P setContentView(R.layout.activity_main);
* m- {- C$ ?2 L' L4 ^$ W/ K handler = new Handler();
9 g% f- {3 K l5 I; `8 S3 w" Z6 ]. U- G
textView = (TextView) findViewById(R.id.textView);9 J6 j% x6 l/ }- ]7 \# b
textView.setMovementMethod(ScrollingMovementMethod.getInstance());
! A+ ~5 r, \& A7 X: \( ^% L d6 h/ H$ w H* I! n' b9 \7 @& ]
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()* e7 `9 h( {" Z+ |& [8 M# I; J
{
1 N! K4 Z$ n! ^+ A/ f* F @Override2 P7 y$ w# w9 h
public void onClick(View v)0 l4 P1 }# [5 I& j8 \: R" R4 C. b5 \$ D
{( u" [7 _7 j( _- f3 ]2 z+ @
6 {- Z5 Z/ t' q. j* U% n+ H
new Thread()
, H- l4 `0 }0 p* P& R {. H, ]$ G0 d+ y6 C, O& m2 {9 Q# X
@Override, v( p8 U& V- |) a
public void run()4 B( b, h! } D6 _' e+ ?" h6 d$ T
{
9 j4 ^! R+ c! Z/ R6 }) I+ v5 K9 V String path = getPath();
5 r2 e: z' B: F/ q3 e# H6 B2 `# W if (path == null)
( b' u5 O! B0 @) |/ v K/ R3 \3 r {( j- J! ?5 D' |; |/ {7 ~5 [ b4 n
return;. a/ w, y1 S4 ]/ N+ ?+ v
}
7 G/ v3 X3 {+ P$ u, [5 u textAppend("正在保存安装目录!\n");
9 Y9 f; O! y# p& X) p. S ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);) `' Q+ i6 K9 k+ g v# S
putResult(result);
5 z# x% d: A# l8 k) y; ~' L if (!isEnpty(result.errorMsg))+ @8 o$ p0 F! r
{
8 z' }3 E8 @) \# _' R0 E/ H8 }8 m return;
& u9 \6 k1 M1 N- x1 c* I }
1 W6 @5 U4 I0 n8 h
' y: A: {( Z% d/ i; u+ D& @ textAppend("正在卸载微信!\n");% `, O; w: r' [0 g; o( }* d3 ]
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用
3 \' N7 N/ ^; I7 P1 L# m! @# s putResult(result);
) u4 Y* Z4 |" d6 m if (!isEnpty(result.errorMsg))/ ]; \) c* A, n! u5 i
{
; m3 q K/ E+ g; E5 r% m; L! K4 C return;' W& S8 ^2 {! n6 T5 n
}
$ S3 S1 y: x' D4 Z6 `/ a8 P" d& b
! k% D( l( X6 `) D. h textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");- C, j( W6 L+ s7 J" F' {
}; v! _( d' @- V3 q x* A
}.start();8 d- \3 P4 W( ?
% F1 i) J3 }/ t3 Z7 @5 ` }
0 K, c$ N- ]+ K# V });, a. H" k" m8 v
7 T. Y/ P% x' k1 Y& Y
0 c; R& N) _- O/ M6 P& Z h4 l5 p
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()( q+ T" B+ f+ x2 a
{3 c! i5 t4 ^- [/ w' R" u+ k$ o
@Override* l# {7 `- N4 V1 r. `
public void onClick(View v)
4 A8 V, W* @$ d6 M, y {1 Q+ h- Y2 X* {9 N0 R/ @; b6 G
) G/ T5 ]9 W+ w- b: ~ Q2 n
new Thread()
3 c+ @1 ^; L2 O: ? |$ q$ ?# C {
6 V3 B8 F: f& L& Z K @Override; y) n: ^# d: U0 P# M& d6 F1 d1 }
public void run()
0 H. q% c: P. T* k- J {
& s+ e d* R1 R& z1 l) } String path = getPath();2 b% y& L8 C+ ] Z
if (path == null)7 w* ~; o9 ]& D8 S6 d& L, q- `
{
( l0 }" x, K+ _& @3 d6 I( D return;
" Q) \$ e( q: V' p }
- A6 [# z5 q, E6 a k6 l/ ^& w0 a3 z- h
textAppend("正在检查是否有备份的安装文件!\n");7 Y' L: j2 z$ ^) p( e
if (!new File("/data/local/tmp/com.tencent.mm").exists())
( f. f7 t, @1 x3 _3 [$ K( i A {: x- M, f! @1 C; R! z
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");8 E7 o/ G: S/ R
return;
4 N2 r% _4 Y5 ?& A. Z. U2 [8 B }
) d( `2 \# E/ m2 L4 ^
- X |/ E9 L2 `& T. l9 P textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");
' b4 F3 E$ h: g0 U/ s" K1 d ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
. b, b. M: F6 E$ F7 m' l- Q! G putResult(result);
! z9 I( A/ }. n5 g y0 X! W if (!isEnpty(result.errorMsg))- P5 Q# h O% b% E) O
{# N% ^0 L. |9 j4 j
return;
0 y% X3 ]1 Q% g9 Z. d) w5 w2 R }
" v6 r4 r- L1 @) r
0 Q3 W/ W A) x5 H9 d) y% G* j0 G6 r# f& A t: P9 k6 R; B. K( D
textAppend("正在覆盖安装目录!\n");
" w W0 E; K* z# e result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);% Q" ^1 }/ d, h( ^. j. ]0 I
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
" _# R% S( Y+ E) x6 a& Q7 ] putResult(result);
" a5 A& `0 M8 A9 a. @: f0 O. }) O5 c if (!isEnpty(result.errorMsg))& ?+ N3 A9 m# d& s6 S, W- R, Z
{
9 l- ~5 G( t, r* N' a9 j6 E return;2 u! k. @4 U6 Z* ?2 N
}
* e, U( ~/ k$ x9 H% s. J" b) o# C0 D" b o( ?( \
textAppend("系统将在10秒后重启!\n");
8 q( [8 q' E. y' y( p' n: C
8 i/ g. s: k& S9 }: {8 M
+ n1 o2 q! H8 \3 A. a$ R+ P try4 v e" o* o: E2 {6 z
{
- q. h6 r% ]% u1 o sleep(10 * 1000);8 L. F/ c5 G4 v, B5 s
handler.post(new Runnable()
- M: X7 i! o' ^2 [" s, y {
3 `% x, b0 a B- N3 Q% j @Override8 l$ V! f% Q9 o9 F8 a$ w# `
public void run()
! o7 z* Z u8 }+ K6 c3 ~# h5 Z. l' _4 j {$ A4 U& F' k$ A v# g! H5 Q
ShellUtils.execCommand("reboot", true);
+ g1 K2 q ?+ w: ?2 V: F: i }8 [) n( n: v% f) r- j
});; w: b# a; P/ I
} catch (InterruptedException e)* f* m. N: r; d. s' ]
{& W3 w2 Z+ j/ ?* \
e.printStackTrace();, N4 u+ \2 B+ Q$ l8 m8 o
}, |7 c& w4 {, T
}
5 N/ n" `4 ]2 A }.start();/ L- I. C& V! M7 L. S: f
8 L+ k1 [9 y, L }
' A( C* r' _ k6 Q$ T+ G6 e2 Q& R4 h });9 H/ P5 f! N# } P( b, s4 ^, ^
. @! I: s+ a; b2 ?: I textView.append("-----------------------------\n");# D6 ` \! I5 {
textView.append("该应用需要root权限!\n");% P b, s! c/ a( t/ [8 i5 X2 B
if (ShellUtils.checkRootPermission())
' k/ k0 m! I2 I" j( [8 N5 E+ l" ?3 ] {. `; s/ Z" |: c& B- \
textView.append("获取root权限成功!\n");+ ^( a6 ]/ d6 u( p; w
} else
b8 O! h" ~3 o( u5 q' [ {
; N- N( Z: z7 N& c2 H textView.append("获取root权限失败,请在设置中授予权限!\n");
* Q* o! Q3 W8 [; ~; C }
/ U& L. l3 r! s% n, k3 G4 H3 _- p! `# j! H; q# X5 c
! X4 r8 s# J( `6 V% `2 }8 L6 z0 H
}
' a7 C$ v3 {, e4 Z& q}[url=][/url]6 s- e- J! j* y" a
1 ]1 L' w* j0 J) N m
$ r+ o$ L. k. p$ M: h运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
2 u$ Y* L( k( @安装包在release目录中
+ @& d9 Z+ k2 b' A9 v |