设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
" s5 _+ ~" a8 A) f2 g, D' j2 P! `5 B8 C 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 + B G4 k3 y) X/ o ~, T: J, t) j
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;! C8 y% b) N7 C5 B' Z8 ]1 E& A
所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
$ p9 b. |# \, @7 _5 ]8 Q$ W2 `( c8 S! N. i
查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]: j2 G: @1 b& ^4 a9 C3 w [, W
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 l! h) a; i1 x% }3 S2 @- B
0 U0 L& Z9 p8 v5 d8 R
9 j) }$ u7 L+ c; o4 c 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, % ~! F7 j5 G, @1 P q9 h' v! w. `3 M
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号, 4 F) t( c1 i! _+ Q' n. n& j* a
使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
" n/ p. ?# L* {6 f/ v 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, 0 }, v8 n4 D: Y! I
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, ; [+ b% Z+ U7 W# r0 O
自动化程序[url=][/url]6 s6 W. Y1 z" ^% z# G9 E K3 H# r2 R
package com.example.wxreversion;' `2 m2 @5 ]9 c% F
# F0 x2 m5 e; G3 m6 C/ p
import android.os.Handler;
$ W' y; B3 r# Z- p2 `5 L2 t8 dimport android.support.v7.app.AppCompatActivity;: T, |& p) X0 O" k: y
import android.os.Bundle;
( [2 x" @) q( limport android.text.method.ScrollingMovementMethod;
7 A1 ^2 y8 ^. h- A9 o4 Nimport android.util.Log;
! T% s7 C- U1 |$ Zimport android.view.View;! X1 M R I3 @+ B
import android.widget.Button;
/ K& [, `# n+ ^3 Kimport android.widget.TextView;8 o C9 z5 q0 a. C# C8 S: k8 x7 `
% \$ M6 ~0 [: y& W3 \import java.io.File;
9 N9 }$ X! o7 ?+ c$ v0 w" U6 y" r L, ^* H) B3 Q0 K5 H# }5 \( _
public class MainActivity extends AppCompatActivity
) l2 }, H$ b' X4 ^6 e# @0 Z{
$ Z( h9 s5 d5 Y0 V* g Handler handler;
8 r9 _2 A- W, r: X! @5 A TextView textView;2 _: c6 B$ p2 h6 m! r R; E' _5 |
$ { U {5 h" Y9 t6 F5 }( i1 }
( j+ `. c5 q6 O- Z8 `
private void LogV(String s)
/ C/ A2 V( t4 l$ p8 \) R {' X' x2 d9 M- O3 ?
Log.v("test", s);
; \1 Z! K t2 b }8 a* ^% p! U6 X: D ~ G
% @. \7 |+ a( z' F5 w% y private void textAppend(final String s)3 b2 z5 {9 z. L1 |7 u9 F
{
& ]5 S4 ]2 x! N. h( w) X. R LogV("textAppend:" + s);9 K2 R6 B: D/ D: t7 P. p
handler.post(new Runnable()0 ?8 R' @3 x( x5 l9 E
{
0 n. ^% w4 P6 g5 @" D @Override
* N5 N, f& ~) m4 A6 ?. `- A public void run()
) `, I3 ^( k6 S6 F {
/ c: N2 F: B. j7 m& d# F4 E+ \ LogV("run:" + s);3 h& W5 F$ f) b/ @+ P' ^7 J; Z* |
2 R0 o) w3 } Z( n+ L) }) W textView.setText(textView.getText() + s);
9 g6 u3 t1 |6 E3 I. q }# H9 S) v/ p N/ n1 g2 u8 _) W
});' T. t9 r' x8 e" f8 O
}
3 C# [- `# q; q! q4 f9 r- y. X* o# f9 G' e/ r! E
private String getPath()
; w( g$ Y7 q- x+ z6 L: L {" N; Q+ V ?& z H0 b6 u
String path = null;
- D" C( e7 X( p8 |+ I# G/ ^1 k textAppend("-----------------------------\n");
& H0 n. X u" k) k' l9 y# T0 l5 I0 L
, K+ Q5 b8 K5 Y; k8 k
9 |4 n0 X ?6 y3 G if (!ShellUtils.checkRootPermission())
; }1 {! s D% b/ u3 b- G {
& J1 J+ x3 V: u+ o8 e6 E+ g textAppend("获取root权限失败,请在设置中授予权限!\n");3 X: _4 ^2 ^5 d) ?/ O5 D1 ]; s
return path;
2 Y5 S0 k8 X! N4 Y% a& U5 Z }6 z% ], e& m9 Y5 b
3 a5 I, j5 f2 i7 D! y- H* w4 l4 Y path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;) W" ~! ?5 W3 s* `) [' E/ _
if (path != null)7 ?; G. i& k* i* _, P5 q
{2 d) j2 Q; Y: ]% E2 X9 L/ u6 e
try; C! @ l. ^8 c( n/ q% H! v
{5 i4 p2 x" P" N$ V, j: y. w
path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
# g8 z7 H& }$ @ } catch (Throwable throwable)
6 J7 Z! y2 C2 v7 T0 b/ ^ {
* V; p) | f& X" K path = null;
% h/ ]& d# n: o* {/ n4 ] L }
% D# D7 |; O8 b# G H0 O }
7 M b* X! e/ V+ \) {4 F# a7 d" H( j1 Q- B' h$ \
if (path == null), }4 S9 ^8 N+ |" x. v. s
{
2 E1 |& e; [) F2 t textAppend("未找到微信安装目录,请先安装!\n");
* r' o9 v+ Z- j, e } else
. Y% e" |2 k h. `# B5 ]! @, D& { {
8 o) V+ b3 m0 s0 M* {& f& B( t2 t textAppend("找到安装目录:" + path + "\n");
7 I" H# X" g" N1 b- U6 c- @. h$ B }! u7 {3 v- N" e
( e; R7 z" }) _+ }! M
return path;
0 B+ o$ Q6 b7 h, A, T }. K. h" N7 W k% _' J4 f/ j, m
' H- x9 j* N; H: r1 U8 w' \ private boolean isEnpty(String string)
( o2 d7 w! u6 q; L) ~ {
- ~# \9 A5 f0 Q! b& C6 Q' O if (string == null || string.length() == 0)' L4 i4 p! k6 t+ N- h+ M! @
{2 A0 z0 J# \# c, x5 m' ^, u6 B
return true;
9 l5 D( F1 d' G0 y+ R; a# V m }
0 P: v, \" T. y# y B return false;
9 T/ n8 l3 S8 E7 t" }7 y }
- Y- x' e% P" r& s2 e) T, z1 y0 _; c+ g* q8 R
private void putResult(ShellUtils.CommandResult result)
9 K% }! F/ `3 A: ] {
* q$ Z) M% d" |5 f$ t5 T- ] x2 u textAppend("返回码:" + result.result + "\n");
1 V* V. v; G: y$ p$ X, D: d3 E8 a7 F2 u# S0 F$ ?! ?+ c$ |- R- g
if (!isEnpty(result.successMsg))9 G( F, }6 G: F& _
{3 [ |. H5 t! _$ n3 @! U& H$ k
textAppend(result.successMsg + "\n"); d* L! j$ @6 o m& R
} else if (!isEnpty(result.errorMsg))
1 j; Q( _* Z6 h {2 k/ q7 i1 m ~3 W% h8 o) ?
textAppend("错误消息:" + result.errorMsg + "\n");4 v' W# J5 Q8 S! W4 y+ C$ h0 Y
}4 X$ g$ `" ?8 \
}
r& o" f3 N7 E7 a O" b( e* x& C( A6 w. W4 H
@Override% c/ c. H; N2 D; L4 i
protected void onCreate(Bundle savedInstanceState)- J2 ~. `( {. u4 W9 Q0 L! ?
{8 o0 O5 d! f0 d: _9 ^
super.onCreate(savedInstanceState);
8 _. C; S6 ]4 y r; w; f2 L setContentView(R.layout.activity_main);$ r9 n. Y+ n6 ]: h: t
handler = new Handler();" {8 [, K+ j5 Y, R! k5 a# u
+ N% N2 f+ c& v2 n8 I
textView = (TextView) findViewById(R.id.textView);1 w2 c+ S+ v3 g3 W6 `
textView.setMovementMethod(ScrollingMovementMethod.getInstance());2 a( `: \* h4 e$ V0 n
t/ A$ O# E8 Q; H* l4 C T
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
6 g A# W+ F/ A9 |% A, c. G {* l- v! s* g _
@Override
. n3 O5 G! G* X! e6 ~/ ] public void onClick(View v)5 K" Z; e( ?' E
{) F3 c' T% h, R: V2 ?5 c( y. t, S
8 Z: p8 n3 D7 u9 f. l new Thread()
7 b# y9 _2 p, Z/ n% u* v1 L; I- e {
* Y0 J9 |. _% A9 y2 R# c" W @Override
% D5 M3 Y9 P$ w# A; j$ [7 k+ [/ v9 \ public void run()
* r) O0 V9 r) s: _1 C {0 M6 ]( B3 m% `1 g
String path = getPath();6 a! R8 p- K+ J
if (path == null)
5 G6 X9 @8 ?# L {
$ W* u8 H1 @2 x5 q$ O. V. ] return;
9 e I# s$ ^( Q) c) B% ~# ~' f" Q }
$ g. U9 z0 @1 }6 w1 f: f/ Y textAppend("正在保存安装目录!\n");3 U/ |$ q0 @5 G& r
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);- t8 d$ B; _9 c x" j a
putResult(result);
* i# K6 _0 C: }. g) p7 b. S3 [ if (!isEnpty(result.errorMsg))5 G" C% U3 `4 E: ^; |
{
0 H( z9 D# y# S2 q return;' J8 Z+ o* W6 O
}- `7 G, g" c& l3 X# J* }9 {% j
' `, ~# T$ } \, a7 U5 D4 Y9 Z
textAppend("正在卸载微信!\n");" g) N& e1 t# h! L
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用4 i% V1 i8 c5 z2 ~5 H
putResult(result);
0 [4 G8 y4 [( e% m2 `9 z if (!isEnpty(result.errorMsg)), i5 r9 K7 F) ?7 i8 D: O& ?" K
{% f3 Z& ~2 A2 O' S U
return;0 H0 `% P; E {3 l
}
- Z9 m6 |' Q7 U4 s8 Q
1 B/ b( P$ o( `9 D% W1 w. G textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");+ f7 B" U6 D( V8 T% f
}
6 l2 h0 @' C5 t- h' W! Q }.start();
' ^5 z$ }; A# ^8 w* E' i5 z3 s& ?% W; b, i% X6 x) H. C
}: u( w4 X8 Z5 R& G% ]2 E2 _( a
});
7 h; H/ G, J0 \/ t U v$ h
' A4 n2 ?) W+ B% m, Q3 V8 p: W1 R
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()2 K F" a" S8 J# f% f L
{$ [! P! G0 @' u* |4 Q
@Override
' ~! ~3 P8 A! ^ public void onClick(View v)4 A4 n6 d* k% j5 Y- c. V
{
) J' ], u; }3 C/ F+ r& A
$ C! j: y% C- w% U9 u new Thread()
- ~; ~# t5 Y$ c" u4 @3 i {, M3 x( L* p# t X& F$ @: I$ J$ g
@Override' N! [8 Z$ H _& z
public void run(), ^8 W7 u0 Z( R. j. f: g
{
* H- N: t* V6 X z7 w String path = getPath();8 @& I8 _9 U( j. i! V+ I3 Z
if (path == null)
+ G1 l; n# b% W, f2 i" O {: Y* M8 {2 K6 r: s* Z0 P5 q
return;
5 \" `8 \8 L% C- i- Q } v8 V, L. }! j/ O! @/ M5 }7 v
5 h2 z$ _$ B2 C, q) `% P
textAppend("正在检查是否有备份的安装文件!\n");" \: Z. U$ }- H+ | t- _3 y
if (!new File("/data/local/tmp/com.tencent.mm").exists())
0 A' r Z, w7 V: U. n- F4 I, R- n {
+ L8 v- ], ~0 E( m8 @# \6 @- K textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");6 e; P& @$ J& ]; \* D
return;
. K: b9 C$ A( z }
2 X* z5 o& j: Q3 J
" {- x$ ?, z" _ textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");
3 N' N" e) I S% j0 K ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);: _3 w- U, Q0 D2 l
putResult(result);
( K7 R( ~5 R0 V. w1 M if (!isEnpty(result.errorMsg))( Z: i) y/ x* D& n( w# p8 `7 E
{
8 w% b- L, d$ p( U B& ~ return; V$ o: g4 ~8 V: F* h$ n! V7 @
}
8 `& ~. ` U. @6 |: I( E
% v9 W9 ?( d$ L
' G1 a3 w2 X) o9 U- j textAppend("正在覆盖安装目录!\n");. @% Z3 B# |/ H. Q# c
result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);7 X. U& v0 T$ v+ `
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);; g/ J6 n5 U& O1 D: L9 e
putResult(result);* E2 b+ X3 c3 `- Z. R" X; W( r
if (!isEnpty(result.errorMsg))
S+ a: i5 u; Q* _% I8 K4 a {
\% X& g5 c* G+ b; Z return;
! @* b& J: E9 l( W% H }
0 `' V5 r, ^% p2 D+ r; n1 F8 S) s+ s/ q) g" I9 ~ D& c
textAppend("系统将在10秒后重启!\n");. j4 N% H8 G% h1 [5 |6 i
$ E7 S, y4 T1 a0 g8 S
) L! ~) p8 z9 w V2 x/ U8 T8 f try
5 e, q! P" X* G/ S& f) o {
& h/ i" E" D3 T6 d sleep(10 * 1000);
2 z) W( m: u+ l, {; O handler.post(new Runnable()1 i7 |9 {* \/ ~- V( X' ^% j
{
8 ~4 y1 y7 @* c' Q @Override7 A* O% m9 H5 g: ~
public void run()0 x7 O( g5 |$ h4 j! g9 l o, T
{$ t9 u4 O, U7 ?0 W; U" V5 |
ShellUtils.execCommand("reboot", true);
' c% { g, S- s$ x# ? }) f( L* s0 m' O: h, h1 u# |
});; y4 q" x. [; v. i6 T/ X# f8 j
} catch (InterruptedException e)4 e7 [/ U0 h- l' [+ w
{
* g) `' @8 \* ]9 C e.printStackTrace(); s/ {. _; C5 |$ n
}
% `! s9 D o5 T2 S4 k$ X }
3 r" u6 k1 {1 a+ X6 O! V" d ]' U }.start();
% u/ e/ K; X5 P. z) m/ {; K2 Z1 |
- p7 [1 x3 ?3 F9 D7 l+ U }5 v3 R4 ~* s9 h/ F1 H/ W% q
});
0 v$ Z, X/ U5 r- N4 F6 |
8 P6 B8 Z' S. J& a/ H# v textView.append("-----------------------------\n");6 D6 n4 S" D" ]; V# g C. C6 o
textView.append("该应用需要root权限!\n");
( V, c1 V! W+ M5 S1 o if (ShellUtils.checkRootPermission())
( _. v/ @! Z ]. @: F& O! h {
& d" G- w: r! Z8 K9 F7 M textView.append("获取root权限成功!\n");4 U$ L1 t3 a* l! {8 q& n2 @
} else
1 W- ?# R( Z7 E/ C {
0 {0 T4 r2 N6 w textView.append("获取root权限失败,请在设置中授予权限!\n");$ @3 U- V: g' R6 f8 u+ {" ~, I
}; r$ K4 d/ W3 Z5 z0 {+ c8 W
- [ }" n. X( i" b z+ n/ a" {9 k9 @5 W+ X( O
}! r* g* W& u4 D7 ~
}[url=][/url]# J- C. J0 A; ^& M0 X" y2 k/ U
! n# L5 t& f1 {( x! L! L4 U+ y- ^ n1 P* e: v. Z; W; _
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
, a+ H- |6 L; |- v; d6 J安装包在release目录中
4 i0 t8 m( t$ A1 _2 w3 t |- k |