设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 1 p+ F- c. n5 q1 c/ `2 E
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
& f: Q7 W, S" s. M操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;( o4 ?6 i7 o9 c% b1 f
所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
) @! e& s6 v$ \4 R" F
( @7 q9 ^. o h7 R' ?' i查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]8 L+ b0 B' o C9 r+ n3 F
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]/ E7 X4 Q/ E Q4 M* ]- b
6 T2 \6 f) K) D
! m v% O* S3 P( r! u# H 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, 5 S1 u1 h% Q# n; X+ b
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
/ {& m% S* _+ n, ^8 E2 [( x2 H! A9 y 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, : U, L/ v+ P: A/ }# M
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
) d& E( ?& m$ t尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, / Q5 @$ J4 z" @% Q% _2 L! b k0 y/ F
自动化程序[url=][/url]
; ?2 x+ ?$ e: k3 Y; u2 ~package com.example.wxreversion;
* ~% W( d- x+ H) g# \/ w" A2 S. a( l% c! P m+ [+ L" X
import android.os.Handler;
, `" ?1 F' U: V1 x/ S1 G0 Rimport android.support.v7.app.AppCompatActivity;% n9 U: k& B p3 y
import android.os.Bundle;
z G( k1 n" N, f/ e) ^* u4 a8 jimport android.text.method.ScrollingMovementMethod;
6 a/ ]7 E% u+ R1 E2 Z" Zimport android.util.Log;$ ~) }/ x- i+ U% W* d
import android.view.View;
0 r" ]* x9 j' Z1 |import android.widget.Button;2 Q `( S! r$ x& m
import android.widget.TextView;
7 j6 x9 F* T% Z8 Q$ l8 e9 V! K
% ~; f$ o; ^$ c: D5 X& o6 bimport java.io.File;
$ _4 t& A' @' K- \; B
7 B2 ?* D& B; H( ~2 Hpublic class MainActivity extends AppCompatActivity
5 V) H; U' Z- K) Z* O+ P" C9 i{
4 }/ ~: {' T P. k) E( Z! O Handler handler;
R$ `+ [$ h- c0 O! Y, z6 k5 l TextView textView;
' c7 d) j8 l: t7 `3 g" s3 a7 I5 C/ H5 c) z
2 s: u" j2 V# z5 i9 b8 V* l4 O
private void LogV(String s)0 X# W, o' |& ^4 s
{8 z( y1 q- L5 t/ `: e
Log.v("test", s);$ q2 a$ r4 W' ]& e" I0 B& r
}! Z6 d) `* ~" S Q3 D4 L
! ?/ R! f( Q' w/ ?7 \5 c
private void textAppend(final String s); }9 u4 H: S8 A( g% ^0 f1 M7 c
{
8 ]8 `- B% E- k% L3 W LogV("textAppend:" + s);
' i6 Y N# M/ S: V, c( i0 g3 L% z8 B handler.post(new Runnable()
1 u6 R) E! s" g {
$ f0 W0 T [# D, Q' a4 K# q% K/ i @Override& Y6 _0 W2 ^4 O, G2 l1 {
public void run()
) I3 U0 u" V( U {
* A) r7 \3 }8 B) w: R LogV("run:" + s);3 g/ Z9 _; R( V
/ |" _2 i$ I& p; T0 w textView.setText(textView.getText() + s);0 a. x& v1 e" s# V: A2 U
}
8 v8 F4 D7 \8 b" @ n" r0 F });
; w" i4 |1 f6 `; Y }4 r8 s3 K0 J8 C; {, c
" U' ?. E" {$ u9 ~2 h private String getPath()6 |9 B0 s3 x# T# g3 n
{. C2 T" N9 Z. {- Q/ k% L: H4 G
String path = null;
. N; D2 |0 X! ^+ ^) U' a textAppend("-----------------------------\n");
1 F& t. V* D/ h+ ?# F0 i' a, P" ~7 o: K* A
& |" }) @ h1 k9 E
if (!ShellUtils.checkRootPermission()); o# Y& W s- b: j
{6 ]2 F) _$ L" j" [8 \' J! H4 Q
textAppend("获取root权限失败,请在设置中授予权限!\n");, H( {3 I+ B* S. {
return path;) t1 k: F3 ], x
}
% B% @" C6 K8 W: u4 \, x4 K9 q5 @" P; p- b2 U
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg; h; M# W, j9 v" J- O
if (path != null)
4 z* I3 I2 ~' N5 f! p) R" I {/ @" g1 i" ?% J! l9 J
try
# s: p( _$ Z+ }" z# K { ]) ?; x+ d/ ]% g
path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));4 ~/ K& a- Z w) K7 G
} catch (Throwable throwable)3 M) ^; P6 A, ~: C" \4 }
{
" i }. H2 ?! F" v' U path = null;5 o# f" ]7 d4 f: X$ z# b, L) x
}. U& T3 d- B0 k' z E+ `6 C" E
}" m% m) P- {; |+ N( [5 z; R
4 j1 t! a3 {3 |2 P4 n! Q+ _2 p if (path == null)
; G* S' l" Z2 @( n {
; @* B) Z2 d' R* f- a r. G; } textAppend("未找到微信安装目录,请先安装!\n");# I2 u+ _ P2 R' G. t) U! ~% j @
} else2 G( x7 S2 o3 s7 }( m) G
{: E' A3 `3 q, b( O/ N
textAppend("找到安装目录:" + path + "\n");
! v* G1 j+ t! u& s, q& P }
& h; r- b/ J; W$ a% B1 F% m: `2 U' a6 M: W+ ~
return path;
1 r' `! P0 @& e, V/ x. C" J( ` }) a- z7 Q+ ?% Q) T5 D. H `3 _2 @
/ e1 Z$ v+ r1 C) q( e
private boolean isEnpty(String string)
( W! n& q; r" c8 L* @* j {- [: {& ~2 `7 m9 r' g4 y5 y
if (string == null || string.length() == 0)
# \: x+ s6 X6 n2 }% K {
3 W) n/ X* l/ m/ k' q9 W% Z* {! w return true;
# h5 L+ q0 d! O) s2 l }: Q7 y! R4 y- N- w: Z
return false;% }+ J5 x6 y6 J2 l. }$ G& X
}
# ~2 w% J# s3 w) M- E O) }
y5 l m; W2 {5 H: p3 b private void putResult(ShellUtils.CommandResult result)
% S$ n6 l6 l- s) Z0 E5 V- c3 K {
. A e3 s" ~! K4 a6 F; d textAppend("返回码:" + result.result + "\n");/ H; c; D9 w0 l- m8 D# A
7 e6 Y1 v. _9 g
if (!isEnpty(result.successMsg))9 D" @2 |8 K Z( C# l4 t& _
{
, d8 x" x3 ^& s' Y' g5 I- T% p textAppend(result.successMsg + "\n");4 z8 d8 p/ A# Y$ I
} else if (!isEnpty(result.errorMsg))
+ a* b% z* w- w8 Y; R+ R% k {
G+ t, _) c: k textAppend("错误消息:" + result.errorMsg + "\n");% t( U* B$ @/ [+ o) r* f
}' }8 k; O& L9 v% a
}
! z) y8 o7 F* l! O% x0 Q( e2 Y2 h0 ?, }; T
@Override
) K7 }0 ^4 ?; d6 X0 ?% K protected void onCreate(Bundle savedInstanceState)' Q2 A: U4 h: _9 w6 x- Z
{
+ {) f+ c% ?: ]& q1 x1 ~8 C( | super.onCreate(savedInstanceState);: q! E7 U4 n# z' K9 B8 H% Y
setContentView(R.layout.activity_main);3 z d6 j. H7 G4 t1 w5 K
handler = new Handler(); L8 X) q* u6 ]8 R" {
. s# h3 ]/ z( d1 j0 _3 m5 k2 S
textView = (TextView) findViewById(R.id.textView);* |! t/ Q/ Y# `7 j& e/ z1 o. v6 x
textView.setMovementMethod(ScrollingMovementMethod.getInstance());( `7 s+ k4 R, Q, n; {
: h7 h, g! P: Q! }5 A% C7 B ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener(); [" v2 a# F' J/ |/ S7 \
{
" \! n, }) Y4 M @Override/ D" c0 J, r# \# S/ i! D4 d
public void onClick(View v)6 g7 v3 a0 @1 ~& U3 X- {
{0 s4 l; b, U5 k6 y# T' P7 Y6 O3 ?
' c7 T' T/ Q5 g1 x
new Thread()
3 g$ t8 C$ O& {; @) { {
: k3 x7 P7 j6 N. t @Override
7 r' d7 A1 j- V public void run()0 N0 z/ K @( ?3 I# _9 T5 U) [
{' k) v8 D. K4 s' U0 g: y
String path = getPath();# h1 d$ ^9 Q8 E, r
if (path == null)
' Y; d! s, m0 T {
! i( T0 J; T; x3 t- |9 _ return;0 E- K7 \# p) R* ]
}
2 B$ g" q) u7 l* Q9 d5 F3 B textAppend("正在保存安装目录!\n");: K' J: z8 X+ T" i
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);+ M. ^( `* Q7 \
putResult(result);# h& A2 M6 @" c6 F1 s) `
if (!isEnpty(result.errorMsg))
& M0 E( Y2 G; y# v1 L# a' n {
2 `3 X/ W2 t5 [) l& R return;
3 O5 d$ b7 @( T v5 K$ D! H u }* v6 X# S* p0 e" f6 G% t2 n
* t! b" i& Y0 b8 W7 ?3 ~ textAppend("正在卸载微信!\n");
) {$ |6 G* C1 y4 c) l result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用
' ^2 R* ?9 ^. b! ^) A8 n( H putResult(result);4 P3 Q, q! B5 E8 e& r( y2 U4 y
if (!isEnpty(result.errorMsg))
. Y- w: E/ S6 ], ]/ N6 e {, K6 a5 k% e) I. t% G$ M( z. L0 |2 b
return;1 @0 G- x! l+ O4 C3 `
}
# y8 @+ n a5 m. L- L2 H8 w+ e! Q: J% ?" ]( {% t$ `% C
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");/ d! s+ T) Y( [0 d$ k3 U4 f
}
3 P: V4 m$ c7 k% ]% u. ^! U" U }.start();
# _& y- k# F5 b2 v: o ^
3 M' N4 a3 i, D" T; \ }
4 v* Y# X( F5 v2 J* |( s) C+ y });
+ O3 r7 r# \- ^
6 K5 S8 b) l6 E3 o2 A4 {' m8 {$ @9 ~* {0 {9 Q
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener(): K7 V" k: [- c6 ]2 A Q, [
{
! t' {! c' f9 [* f1 H @Override+ u+ S8 m [. Z* _' m
public void onClick(View v)
% Y5 N- p) x; n: t' i; D* ~/ u {) P, v1 j; R4 Y
L& w7 ?- `- T% C new Thread()
! }& H u+ \# Y* [ {
8 }3 T" |, q, s, | @Override
( x" z' Z* z$ p public void run()
& V- K) _7 Y! Y+ \' t v. _. r) z) U {
; I7 m F$ D c/ }. w String path = getPath();
; k/ k) t5 {$ v4 R. ]7 \ if (path == null)
T* J" l8 { @0 w {. T5 E$ O. `6 A
return;5 a# _. n0 U# l) I' Z% D/ X) r
}
$ K3 M T C5 d ?+ Y
! l0 V. w8 Z0 ?3 f ~ textAppend("正在检查是否有备份的安装文件!\n");
$ Q5 p# s8 e8 O# H if (!new File("/data/local/tmp/com.tencent.mm").exists())
* X$ J9 s/ x& J; k {2 g4 ?0 ^1 p Z7 m* o* g. u
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");
2 z; A8 g; l U& u- Q8 u2 [ return;
! C4 f5 N4 I( v4 r4 v4 S0 W }! C" P; ?/ y. H2 J1 u2 Q: z9 W
0 _: n# u$ {& c# f- U) M9 e
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");! N( j7 H. J) H5 M1 b
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
: `% t1 X* R, l7 ^6 e putResult(result);: j# d! u% P8 H1 q1 @
if (!isEnpty(result.errorMsg))
/ W5 e7 \( d* `" E+ e* y {
Q$ M" ^. P6 p6 |' g return;
7 T, R$ L& l( g# v }. U' K: _3 Y, z
; g7 h0 g) z( s! ]8 ]" t1 y( h; F( R& L
textAppend("正在覆盖安装目录!\n");# m4 d" b8 _3 F0 k \ N& y2 z) F
result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);' H P" V$ j) ~" x; s2 m
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
) j9 n9 K* M) J* j1 _" J! u putResult(result);
2 Z/ _8 I, o7 u- a1 h if (!isEnpty(result.errorMsg))
. u- x6 j5 D- D/ n {$ P5 i, o9 a5 f" W/ G, d: B
return;+ G% Y Y. [, F% ] X+ D3 y
}- Q F0 o4 [3 r2 H. K) M4 h1 M% j/ g
# g# e' T: V% ]6 b textAppend("系统将在10秒后重启!\n");
9 g' J% i, X5 ^3 M5 p
$ ] R6 \$ {( m0 A$ `
( B! [+ P* f. n0 }' p+ ? try
3 r& i& d- a2 C4 { {
k# g m T \ sleep(10 * 1000);5 ?5 j0 \. F! Q3 w
handler.post(new Runnable()/ U3 `& \. E7 j) M4 d7 y2 _) D6 {
{
; T( T1 H$ \$ e2 g% f8 J( \ @Override
5 [' E( D! n% e5 b7 U {! s public void run()
4 S4 Q: h h# d: ?% b {
- W/ L% c U( u ShellUtils.execCommand("reboot", true);
# b& F" z/ G5 z5 n }
- x0 @# Y, I, p# B });. U8 A8 U* p3 f, ~1 ]" p" _
} catch (InterruptedException e)* c- K, J. w$ ^& E x! q! K2 Q
{
. ^# h3 L) o8 Z: a# ]5 B& I; J' W e.printStackTrace();* F D7 A5 a$ O& K7 c. K
}
, X6 H! ?' c. _5 J8 J; b. ^ }
* T1 F8 F; P0 V Q. _) U/ d }.start();; j5 S4 C2 f% B9 ?
$ r( K! _5 ?4 J4 S- o
}( V( U+ R. _1 w5 x. g9 a* D
});
5 r/ x: r, ^" Q9 W- v$ o' P4 |2 d2 a U: }. w5 P
textView.append("-----------------------------\n");) L! `, g! |) V7 K+ U$ Z. k# E: T E
textView.append("该应用需要root权限!\n");) S* T& R$ W. ]4 e! q: ^
if (ShellUtils.checkRootPermission())
j) k0 y' |% E% m7 Y {, M$ K: j x: `" H. u3 V
textView.append("获取root权限成功!\n");2 j9 w7 V0 q u3 n1 V. ^1 N
} else. g' e% }6 V, H" c ~
{9 L9 z4 U4 y5 Q9 L- E
textView.append("获取root权限失败,请在设置中授予权限!\n");
8 Q+ J: j& u% m# ^5 t }6 \+ O$ P4 V# O- K' `
6 J' n# w* Z2 S. C5 m8 n3 A
$ { ^! S" Y- f$ x! Q0 O! p/ _ }
& p( a0 i7 `- d5 q% Z}[url=][/url]
) }$ B- m9 h( N" g4 C9 [) C% @2 f- J; w& G% _- V9 y+ p1 X! p3 y
O" n' ?( Y, v/ i运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
, v) f$ Y1 i% f" U8 M* r1 p安装包在release目录中 % _2 l$ B! _$ p& `
|