设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 ) O* Y. R T2 O$ S. K
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
% I$ e7 |& U+ B操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
0 j3 ?# g4 K& _* S* V! {. _ \ 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());4 K& K5 e Z) r" w) S
5 M/ }, B9 D* m
查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]# ?/ ?2 m4 L9 R, ]
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]
/ G7 @* l" O. j/ V- p3 z. o/ {
4 M# P+ k5 [5 S: T
4 r6 p+ f' i" T6 h! R( F. ]0 }# m) R 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, ; w5 n5 b4 \0 Z6 V" a/ W, ^
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
7 \' R$ Z- R3 U: o! d 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
4 k% n& J+ u' x/ p 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
: c+ X3 P! ]9 b' ]尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, & ^1 H. \5 q0 _- A4 @
自动化程序[url=][/url]
d0 S) @6 t4 p' P$ ipackage com.example.wxreversion;+ E, t1 m6 u; L: _; P- [9 z
- H. d- f; B/ L Himport android.os.Handler;
9 u8 T# U4 j0 ]- B; h) q2 Vimport android.support.v7.app.AppCompatActivity;
: p9 U" X) x* }/ ]# Eimport android.os.Bundle;4 P6 c+ j- q0 Y/ q+ T
import android.text.method.ScrollingMovementMethod;
4 z/ [0 o+ Y4 J5 {( Aimport android.util.Log;0 @6 L8 u$ K* F: {4 q% G8 z' |5 h7 H) Y
import android.view.View;
* b, x* X: i8 s; P3 b' [import android.widget.Button;
# O4 _+ V7 [7 I* Vimport android.widget.TextView;
, l4 w7 m4 X' ~/ c$ |9 u) P4 c" r' h7 o9 c% r1 ?# g6 f7 H
import java.io.File;
/ h; f0 A8 h& N7 n$ C7 O
& ]. E/ @+ [) F5 u4 n- ]* y* |public class MainActivity extends AppCompatActivity
- l% x+ V, `$ O5 W( [* C{0 [& @- {& r. E, q
Handler handler;
X) S i5 l+ ]; Q2 v# t TextView textView;
: n2 I/ X1 ^. i* k) }+ ?
3 W- A- @3 `) a# {) b& h- t' p: m1 I9 z! i. ?$ A
private void LogV(String s)
$ X( R. O% e6 j1 ^. T7 ^- l" I {0 U6 \% O1 N G7 R4 y* O
Log.v("test", s);0 t6 R9 `8 s0 l* K3 U0 H/ m
}
6 Y3 G$ O* b) d& R& b% A. k. E2 s& r0 b% R7 _
private void textAppend(final String s)& w" G/ U# h' c$ B, ^% |
{
, D9 _8 ^% e! q% v5 V. z6 B7 d LogV("textAppend:" + s);
4 q( w$ N( E) {0 q handler.post(new Runnable()( a w$ _, v6 U4 O# ?1 f
{( e6 }% ^0 s$ W4 q. t2 M
@Override
4 ?# M( g' `9 z$ J+ s' P: ` public void run()
6 V! z; ~, M% a* T9 x5 u9 d {
4 ]* H# O: t2 k& M4 N5 c; p1 M LogV("run:" + s);
( O; |3 k4 r3 j7 T! e4 E# Q- }9 V1 M/ d3 E( N- T
textView.setText(textView.getText() + s);
( V. M& B; K" A, O# ` }
* p% D- o1 t# ?/ ~4 ]) i/ |3 q });
+ |! v2 Z$ d9 g8 ~+ |" O }
4 |# i2 ?* q4 b' u7 D) }9 K) m$ c P, @1 y# Y: \0 x% O5 {
private String getPath()
# |) L1 w/ v/ P, u4 S, i* J {
# Q& J% T/ V; E( ^ String path = null;
; q, f+ Q- P; H: @; _ textAppend("-----------------------------\n");3 A( y& g# J4 J: j- B% ?
) X- T0 ^5 z5 u9 a0 ?+ Y1 `2 T
0 Z7 D/ {3 v3 U
if (!ShellUtils.checkRootPermission())- ]% L6 _0 a$ t" R1 [
{
! @( @5 J$ U5 |6 E# d7 o4 H% D( [ textAppend("获取root权限失败,请在设置中授予权限!\n");
+ B: _" E3 X! i) k T. I( h, _ return path;7 x. T* Y; [- l
}
0 h2 R8 s! S! I% o+ k+ N' a% d4 r9 c5 y, T$ \
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;7 t# _4 T( a8 } c0 c# p
if (path != null), B/ R7 O$ j$ Q7 E& j0 u# x- R8 ?
{; H3 Q( U0 }, t5 ^% Z+ W
try5 @0 |* T/ S; H1 u; F' H0 o$ \5 \
{
, ]+ K5 A* w. b3 J& t. L path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));1 I; _8 @. z5 J+ Z0 w
} catch (Throwable throwable)# r3 \! M9 s! s# h C, ]# G
{
$ g( z6 i2 K$ o$ Z" n' I path = null;4 C- q2 \ a( W7 C! p
}5 `# W; Y" O# d6 x0 o) r
}
) f; u' _# l7 i2 J: r
8 ]: q( m7 M) G' a0 W' ^% @- m if (path == null)3 z1 |5 d2 {5 v0 j' j0 _
{
6 {1 i3 U3 U2 @7 a& I* n+ M2 U' n: v! V textAppend("未找到微信安装目录,请先安装!\n");7 s9 d7 ?1 P: w( k: |! ^! ~
} else
) L3 u6 s: }' w A- E) ?4 n, a {/ ?2 @+ D9 U+ A
textAppend("找到安装目录:" + path + "\n");8 @( f% }- b* j+ c
}9 ~& Z0 s Y; |. ]0 M! B) Q/ o
' E9 z8 \7 i% m3 Y
return path;) o# {: w8 A1 Y, L$ \4 [- T# G
}% t: i7 H8 N- a6 f7 f, x' p$ m( D
, K( E# ?' Y' G0 @; k9 q private boolean isEnpty(String string)
2 y0 _ M: ?% t. c0 `! V% Q {$ n8 a/ ^1 W& C9 L- L* c9 y
if (string == null || string.length() == 0)7 ]$ h; S! h- E
{
( ?. m i) P6 u' r% x0 W v! U return true;
% I" a# u3 U2 z2 @ }7 t( z$ l; A/ j/ L) i
return false;# N+ s) n. O* ]- R& c' {
}
( a- g& }4 [$ u6 E3 J7 G d6 Y
; ?& H. `' C: e7 ?' S6 `% S% Y. c private void putResult(ShellUtils.CommandResult result)6 W6 {5 m: x. u' }
{6 V4 r+ ?( z- v/ ~4 G) _
textAppend("返回码:" + result.result + "\n");3 O1 w5 I% z t9 }0 e
3 f( Z! t% l. ]3 Y& M3 } if (!isEnpty(result.successMsg))2 @- j2 H9 s( {& \# n* c
{
# M- X- }0 _2 i/ s! Q! H textAppend(result.successMsg + "\n");! @1 N, [1 l. I3 V( w2 U! z; ^
} else if (!isEnpty(result.errorMsg))& n0 B6 B5 E/ \" i1 n; ]% ]
{
/ j# h+ M; J @1 f. j+ F& ^7 ` textAppend("错误消息:" + result.errorMsg + "\n");
( M* a2 @- y5 I }
3 Y) C2 q% B3 C0 z3 t( }* G5 X5 w }
j$ ]1 ]2 c$ s
* Q$ R# Y5 l& ~; K: D2 H9 h @Override' o1 A0 Q' ^$ N
protected void onCreate(Bundle savedInstanceState)
* ?0 i: k4 H6 g, b8 }% e1 b1 t9 F {' m+ j/ N: j+ t+ p/ m( U
super.onCreate(savedInstanceState);
z7 n, O3 \( n5 R setContentView(R.layout.activity_main);
# Q) r# q; ^7 d2 X% w handler = new Handler();% E, Z! w, a* T* p$ H% U2 [1 N
; M3 \7 N/ P; @, {; {' L! c5 c textView = (TextView) findViewById(R.id.textView);( y( l1 y% F4 o
textView.setMovementMethod(ScrollingMovementMethod.getInstance());0 n' L8 `% O+ l, a2 J9 Z! t4 V. S
! Q* c/ V& E8 b" O* `
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()& Y1 g( j/ E. W( I. {) r/ E a
{6 l, f/ y* A$ y% W+ r
@Override3 J2 V: }1 @- m1 {' v; a' U
public void onClick(View v)0 L) c* l- p- T: {" z
{/ D4 k J. s/ q2 U; d6 n
! V% D, k9 J& c new Thread()
( o# Z4 c$ u( ^' a0 [) |4 t& r5 L- e7 S {! W0 z* w! d8 Y# d
@Override7 i% C \9 b# n6 z, p
public void run()/ m$ O5 [+ E& x* u+ B9 n6 `
{' {8 [* m, @$ @# ^8 T& x, |/ v' o K
String path = getPath();- {$ i$ W! _! c8 A* G
if (path == null)( F; f" n& p6 J6 l+ i* ~ }
{
; F- Y; Y* q5 d% S& b- C$ ] return;- t( M/ \- y! w+ s+ g# r$ h
}6 w0 ^' E! |6 t/ b
textAppend("正在保存安装目录!\n");
4 \6 z9 l7 Q. x* g; _: R3 x; B- r ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);4 B! t, W" }' n* a7 p2 Q; n F7 T
putResult(result);
5 e( S/ l! r p1 [ if (!isEnpty(result.errorMsg))
4 W! f( G$ M1 w8 l {+ G5 ^% f- z( u1 w" m% f
return;
, h6 k: t: v- e3 J# h }( s) r3 ^; y# u$ c. G* ~
8 Z) v) }! d9 \3 f textAppend("正在卸载微信!\n");0 j0 w$ Z" k9 N6 Z& e3 Z% A; p
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用. A9 k' F @) G% @, x* e7 W4 Q' J
putResult(result);
, p5 o8 B! G; n: H if (!isEnpty(result.errorMsg))
& K4 A) H" {* d* ^ {6 H y$ U4 ?( p" z: P
return;
2 }8 q( t7 u+ J }
( m8 H7 m3 G) i3 i, |+ Q M+ s5 _# L5 J- j) S
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");
; d2 R% W' A: h3 C( X. \# } }
# V8 [9 A- z h }.start();) l. h+ d. H$ U- ?6 I
7 r* Q3 [0 e' _+ f }
! E# z. E0 ~5 C* S. i( f1 f1 [! g });
% s* p2 C9 Y- [6 w' m9 `2 l# A3 j9 k5 a- A5 z8 \
4 ^- U7 M g' O7 D/ p9 ] ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()# R" B4 z! K/ v, b
{; a0 q8 L4 m2 u/ `# U! i, d
@Override* ]" U4 _1 ^0 `
public void onClick(View v)
7 H! D& I" q( x$ e+ ? {; U: o. G$ C8 _5 x
8 f7 U% ]% U6 F$ C0 S1 } I F5 F new Thread()& A6 C+ y% y4 ~+ t
{
# _. l9 O: t. R j8 G1 P @Override
' N5 ~6 Q; [& U0 f public void run()
. l1 ?9 P( h+ E R0 R; e8 _ {, @8 _' X# {3 x
String path = getPath();: E$ ~/ _. e* Y* f; y
if (path == null)& ]6 }1 J. y8 R: R' f
{
. B8 k- Y- b- p/ W. a return;
# t4 F4 Y3 T; [4 M }
: D" z' [% Y* E; @- k# M% H* ]) {4 M
textAppend("正在检查是否有备份的安装文件!\n");
7 v% H O. r2 X7 r' e# | X% _" {& p if (!new File("/data/local/tmp/com.tencent.mm").exists())
. \5 | R* ~% s# `) k {
. x* G; \ _/ ~. w& n8 s" j textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");& P; U/ h" ^* ]. o( U
return;9 P) h) t \8 B* F, x O0 |
}
: p, i8 j; h% [: x" y
. j' D# |7 v$ @* ^4 [ textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");8 x& V8 ~7 i! G; M/ K2 K# M
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
, D% v* m4 ~8 \+ d b! z8 Q5 e4 V# G5 H putResult(result);1 Z$ E6 A, ]6 C
if (!isEnpty(result.errorMsg))
$ z, b5 N& r( O7 U* j- y {
# G3 S5 h% m/ V return;
$ s4 C: e; \, ^! I: P% V, u: p }
+ V1 p8 F0 F0 y3 g$ W8 {* S7 Y7 @7 y4 L d$ |8 M) t
0 ]2 w. _* {! P$ {% B
textAppend("正在覆盖安装目录!\n");4 `' J" d, i f! G4 C$ U* J) V
result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);4 N* U6 [) J" D1 m9 l+ }
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
/ P/ ?6 B$ m% A N) V+ ]. f/ ` putResult(result);
8 m3 n/ |* }# C5 b2 w7 W if (!isEnpty(result.errorMsg))) O0 q% o1 W. [3 i+ R; \. b
{
+ n- S9 p2 @7 }) t; v$ M6 U return;
2 {8 a8 G$ n: q! q. R }2 H$ G# O( b- U* w0 u4 q
% B9 t1 K. v) O% l textAppend("系统将在10秒后重启!\n");
+ {( k$ f; u& D9 _( K4 L( d& [6 ^* \& h) H8 {
$ m2 {* U+ \" g! B try( \8 a- L3 f$ C! ^; S. \3 B4 q
{
0 G7 H/ A C$ p+ i7 Q sleep(10 * 1000);
% l& l& _& y* e, I handler.post(new Runnable()
2 F: e8 s- @+ t% T6 u) l- @ {; k# R. {% q! B& w. d9 _8 X
@Override
9 t5 c) Q# h! y; v8 @* H9 g' z public void run()
7 b/ W8 e: t+ P: l( S# `4 x" ?- ^* d {
* x. h+ e( s. B$ P0 s ShellUtils.execCommand("reboot", true);
3 A5 G" ?/ J7 ?% L4 B! |+ O) r1 P } M4 K8 k/ {- x/ c2 o- X6 S; m* l- ~
});- z8 r+ t6 S. Z4 L3 Y
} catch (InterruptedException e). L3 L% V6 q6 M: Y
{8 e+ H8 L. m0 f; q: F p: D. [8 A
e.printStackTrace();
1 o3 r0 K) B# W2 h" `7 k }
/ F" ?/ v# c/ E2 k( S+ v1 G3 t5 F }
' y4 a+ ^& o+ i. u; |1 { }.start();
% U# L6 l9 C2 f3 w3 G# a" g9 e0 c* u
}6 b) V8 A$ y$ }. ^. `( L1 { J w) T
});
9 q9 _( a0 {$ ]4 Q. _
" X O0 \, v! @) `- m textView.append("-----------------------------\n");
: f( o0 t6 h! z @ textView.append("该应用需要root权限!\n");
+ X* H' z( b7 Y* z% P# C V. A: X if (ShellUtils.checkRootPermission())
$ O4 T+ z+ z( y# l( Z1 `; ~7 s {
b* Y9 `- q) n3 F% s) ] textView.append("获取root权限成功!\n");3 h9 @, l' L# y4 U' b
} else4 l: J- v1 Y- ?9 y3 Q
{
$ d$ Q6 X, Q5 |& n1 D. A% U0 \ textView.append("获取root权限失败,请在设置中授予权限!\n");
3 F: ?; E: ~, ^/ u& J }
: m* S. D+ M3 _9 H* e6 i4 q' S$ l5 t- f3 x: a! b' v) m& p
2 _- C' B3 V K" }* T8 w/ G: Z5 l5 [, [
}
" |$ X( O) J2 X% d}[url=][/url]0 e8 u( ~% e# e# o- w, Z" E3 H
: @+ V l" W- T9 o3 [2 X4 ~+ z
8 {6 @ g$ _" x2 G运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
; y5 f. e8 y- r安装包在release目录中
; V; R3 p! q. U9 I7 E3 e |