设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
9 L0 [8 S! d$ \3 @2 G 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 % e2 r7 Q Z- H' S
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
7 `4 i a4 |" L9 n% J- k 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());6 K2 q9 C; p4 a4 b0 t
" O# ?; L; d8 O1 V
查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
8 z$ Q2 S5 q6 `9 c' ?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]' v8 B* W3 a/ S7 R& h
- E& T& }! M; Z
& Y' A! D- V9 J ?; ?2 T" R
编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, ) i. ^6 z" N) z6 [+ W
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
- u; m$ K& n7 z' {. E5 l 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
4 T. B$ _* i2 J2 S$ e' e( ` 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
! `1 Q1 y: h) m- A* B$ f6 |尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, ) {) @) J/ u3 F' K# l
自动化程序[url=][/url]
/ G, `( T' j( g# P# x& X% U# hpackage com.example.wxreversion;
* R4 p3 u& z, M0 J2 @8 F
9 K# O6 y5 K# M/ Oimport android.os.Handler;* i% L5 `: n4 `4 B/ U
import android.support.v7.app.AppCompatActivity;# F: Q/ e# c. s; R' B
import android.os.Bundle;
' X, C6 h9 z+ ^5 cimport android.text.method.ScrollingMovementMethod;2 e- W; p2 t# K
import android.util.Log;
$ e' Y4 b% ^; n; wimport android.view.View;
3 m7 x% a, O6 ximport android.widget.Button;
! M- U# y. p! @/ _import android.widget.TextView;
& @* u8 m5 K; J9 U8 G( I2 F1 ]) s& j1 n* i
import java.io.File;3 [3 i H. E+ O* E1 Y; g) G
. I. I6 i- t: G* Q7 j9 M# zpublic class MainActivity extends AppCompatActivity
2 c( j+ D* T8 n( F4 M3 M{
) l8 V0 x# [' u1 w Handler handler;
# W7 f4 [+ j) N. ` TextView textView;
8 z& e, T( h! P4 q) E% a! d2 j4 f2 R+ ~& @0 Q( g4 M g3 L
( [/ m Z& X# |# N/ D3 V
private void LogV(String s)1 i: l4 z5 c+ P Z
{5 J U! i, R$ B# N0 T' J" c
Log.v("test", s);# Q; e$ p* I8 z7 j# Q7 T1 q" J
}
+ O8 t3 U" t w4 g% j, E' q
) n3 U# S: t4 J- ^ W& n private void textAppend(final String s)2 u6 G# `' x. Y2 h
{0 i2 g& f2 s+ X- e, i& G0 O
LogV("textAppend:" + s);
' n* }$ }4 D! G a9 I handler.post(new Runnable()) E( ~3 d W- M0 M' G$ T% T
{, v5 R7 y' j0 X: p, E
@Override k& |' F, @ P& f
public void run()
8 \4 V9 r$ o2 h* Q5 E. a {" Y0 l+ Q7 F0 t1 Z* j
LogV("run:" + s);" s( w9 I9 {4 i8 ~; b# S4 T
1 `; `( w. H1 B- o2 e) |
textView.setText(textView.getText() + s);
/ i# l3 l$ x& M5 `/ k }
. y# N) s0 m7 A, e });7 P5 ]6 l0 X7 r4 q5 U" d3 S: {
}
" \! g! @' k6 D' R
0 U8 C$ b4 |! ^2 I) ? private String getPath()
7 N) ?: `# A3 m0 _* M+ j {( Z' o, l/ l4 ~: m
String path = null;8 g; D$ M, N4 r) ]( _, P
textAppend("-----------------------------\n");
% C9 {. k, H! O/ T) ` i# |2 M' O$ z* n4 I$ I, w3 P0 i
W5 C$ M- ?+ c$ F- Q( f3 G if (!ShellUtils.checkRootPermission())
4 D; L9 b" Q7 T+ @ y {
2 |' r1 Z, O; x: l; b) V& R textAppend("获取root权限失败,请在设置中授予权限!\n");
9 h0 O- J% {0 w2 m3 C return path;
% N* n- i, P1 \ }
T( Q9 Z8 G) W% y* z. T1 ?8 x8 }( }+ S1 M9 L9 d4 r' @' T
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;( V1 Z u% V, z% g# I: U
if (path != null)0 S* |) {3 h7 ?* S9 P
{
A) z1 _% T8 b! N' q2 u try5 h8 O4 c# k0 \) M9 d
{
) R0 g2 p7 V( s" I* X) A6 i0 n path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
" ~& r6 A# @1 I) e- Y2 @ } catch (Throwable throwable)5 q6 B) A, `- K8 Q$ j
{
/ S1 x! ~8 a/ k path = null;7 ?( u' o3 `+ n. H( [
}7 _7 P$ R M; H4 y8 L
}+ B! a7 v$ I a2 [9 T& ^
b$ Z( [7 A0 w1 ^ if (path == null)7 M z p1 M! W, f+ ~% Z
{7 [! E; r* V# ]7 G4 E) I
textAppend("未找到微信安装目录,请先安装!\n");& s7 ^# ?& y" l5 v. K# ^7 ^
} else3 B7 }6 E5 N& } G* Q
{- O' K* W* j. ?# p c! R4 X
textAppend("找到安装目录:" + path + "\n");
. p- e4 P! {! F3 m. s3 d) l% o }" _$ m9 M# k1 U& c5 R' E
/ i, N2 t( B& A& k
return path;
0 {' o5 p* @9 x8 q }, e8 g1 p: T. {' A
& o! `# L2 r- J: R7 C' x, q private boolean isEnpty(String string)
8 V: z* g6 X. y h' D2 M {
9 L6 D( F, G8 V9 z' B3 A if (string == null || string.length() == 0)
5 v. g; S+ d$ V# |; j0 v" X {
& ]' |$ X; {) `' A5 y9 p2 }2 f return true;
E4 V+ @8 m5 v" j& r }
% Q" c. q3 h% ~+ F$ T5 V! J; V return false;( N3 X9 ?: T5 l& K1 e
}
; Q6 f; d! K* l& q( o9 q/ P3 J7 B/ f$ O) k+ Z3 K6 K7 `
private void putResult(ShellUtils.CommandResult result)) b* L6 f# \8 b- ~% F
{0 O$ A4 G: h8 C$ A
textAppend("返回码:" + result.result + "\n");
( L$ c! S4 ~9 p" J
# S# t4 g& K1 H- ^ if (!isEnpty(result.successMsg)): B/ E. b3 H' H7 z
{
, l+ r7 o! b; R/ \% q6 A textAppend(result.successMsg + "\n");3 X' e' ?. O9 v+ I* V
} else if (!isEnpty(result.errorMsg))' k/ _7 n4 ?7 s, T& T. z) B
{5 X- Z5 @; O' o* r% A
textAppend("错误消息:" + result.errorMsg + "\n");. S1 E( ~/ \% e9 M4 H7 r
}) \/ `0 z% x0 e2 i6 w( }
} h& z0 O4 S5 u: y9 r7 L
0 s2 _( ?7 K9 m" [! v/ w; i3 H; y
@Override* K- Y# F( H' O9 _
protected void onCreate(Bundle savedInstanceState)
; c/ t# n6 e4 a; m( y {
- h7 O6 _0 @, B# E# n/ @7 u X5 d super.onCreate(savedInstanceState);9 a6 k* B8 Y' e, R& v5 W
setContentView(R.layout.activity_main);1 F8 ]) w( k+ C" a" c
handler = new Handler();
# F& r) j" l' [" b7 j o( N
! O I4 V/ ]" H, N( i9 G textView = (TextView) findViewById(R.id.textView);9 W6 m' `& m6 j
textView.setMovementMethod(ScrollingMovementMethod.getInstance());0 Z4 n& h% G) p
( N6 N. s) K& B5 d% Q' K
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()
/ A/ _& ?- k, v$ k$ f' a {. ?; P: ?! y# r9 g
@Override/ ~, V4 M& f! U3 |1 L
public void onClick(View v)
1 y+ V( ^( @% i. l) s {
! @9 m) v9 z/ ]7 R& p; x/ z! Z: a7 s3 ^1 w L
new Thread()
" J7 D5 x! Q: p; R+ T {
( m4 _$ O- m/ A. C+ h D) n @Override, m5 t' e- r2 U2 [: H$ c
public void run()
2 T# V6 Q! ], b2 S( ?4 B/ Z1 b {
9 O5 m f" j/ V* w5 E String path = getPath();
" z, E* T4 z. v u$ Y if (path == null)
3 k$ l; ~. s, n4 F0 N% ` v$ d {
7 d6 n- a' V* e8 S9 r4 U% Z! u return;
$ _+ p# C# J, I }! Z9 e Y+ i F
textAppend("正在保存安装目录!\n");
}9 Z, b0 i6 }$ z3 o( ~, V ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);, U- m! y5 }% O1 R& E
putResult(result);
: P( x# k" R6 j& h- ? if (!isEnpty(result.errorMsg))' h$ l5 p( C# u3 P1 d v1 k8 L9 K
{
6 w3 d; \! C8 u2 O( [8 q" g9 V return;! A: `4 ` h# g% \# j2 i8 l0 K
}
+ S% }. v' r3 n6 c- x$ a( }: Q8 K
9 H- a! M6 b: u% w# M) _ textAppend("正在卸载微信!\n");
, l" E, t7 b$ z4 w' |9 _8 g result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用
! n: d4 F% d H' D2 Z) D/ \ putResult(result);( _) i1 ?6 k: k8 P: p, ]
if (!isEnpty(result.errorMsg))/ F& e9 B+ v; ]& H. }1 A
{
/ @: ?- R% w) U+ h% X1 T( x return;; I4 Y5 o& J$ X. {* |
}
( U# f& p' t E: n
8 a1 T1 C% ^& g) b/ [! ~8 i- O textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");' A$ j6 W$ W8 k+ n/ \ _+ n7 i
}
% l U2 M6 _8 m+ j! ?) K; g }.start();
$ y& W- t Z; Y
* X" v: `3 S9 _9 @' k7 Y }) f/ S; k1 s2 [
});
. K8 j* v0 N) }. n( Z2 B W( J2 }+ k' d. z A7 A9 {, ^- w5 g$ g
6 `/ M* k& K3 Q: H* x9 j ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
, f; ]# H3 r" Q6 O0 [8 }8 W {
4 p5 l% A9 L/ @6 U @Override
4 n% O( L. c- H7 H3 L* b7 {0 j8 F public void onClick(View v)- I/ [- L3 R0 ~" I6 v4 z
{; P$ ~7 O1 l: y. \- U& p
* e3 f7 U0 X) m% } O
new Thread()- r) `! G7 ~: p4 M) |. ^+ }. E! G
{
) c2 b9 |: Z# f- U3 q1 p @Override
, M4 Y& v: Q1 B- W' {4 m/ k; L8 p, K public void run()
j7 o# D* F( J* w8 f' G {
$ d! o9 V h8 e" Q' W String path = getPath();0 P7 e% p/ U$ u/ [, f/ a
if (path == null)0 I. h4 f% J+ h& l% }
{
c0 T8 {( r) Y$ V return;& q; I `. q% t' O& @
}
2 m3 h5 i+ t/ p8 r9 g. N# S. W8 v- _" e/ a
textAppend("正在检查是否有备份的安装文件!\n");
+ j9 A% f6 ?7 u- U, Z if (!new File("/data/local/tmp/com.tencent.mm").exists())
$ b) D e& m2 P {
4 }, H c0 N3 s" i9 J textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");
% R8 {1 H8 R- ]8 |2 ?# j$ u/ I return;
/ c6 w% E& L: u$ i# m) k; V ~ }
; V: n2 [. a# a3 K# o! N& b) v& }. }4 ~
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");& g& v$ }- L4 G3 o
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);5 b/ D7 {3 k! |$ V8 I
putResult(result);
+ h9 a6 t1 s) Q6 f9 ^ r; ` if (!isEnpty(result.errorMsg))
% P) O, E$ e9 n, X {5 s8 L- x9 L% s3 l3 b G& }
return;
n4 t N& c# L! D }: [' Q. P$ |$ Y* t* ~$ Y) W
" }6 y7 M$ f/ v7 ]& z
: ~! N! N7 B3 l' }( ^, X' M textAppend("正在覆盖安装目录!\n");
/ m& Q( f9 {8 ^; p result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
" u, w0 O4 Z# @4 z" F; E! i ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
6 k- u$ ?+ F X7 _ putResult(result);' y X1 U' O& ?7 d
if (!isEnpty(result.errorMsg))
3 L% E. h8 K+ V6 r i4 @3 q/ m: a) t {
B. }* G+ [ E, v j) K# a3 V return;
* Q! f3 ~+ Y( L: m }
; Q8 r0 T# q* G: Z# ?) {; |/ P. ]4 P$ h' ~9 k
textAppend("系统将在10秒后重启!\n");
+ G) ~, a" s3 P/ @. w& o& ^' N# k+ n& Y) }7 W$ |$ s
; C/ x7 ?, u6 O, b, Z3 h
try
! `6 U$ S. P0 _5 t; d6 h {
3 k e/ T: v; I- _0 q sleep(10 * 1000);% B9 M1 _: ?. F) y
handler.post(new Runnable()
7 {# v, }4 W4 D( I" t. @, _% v {7 v* q, P% P( C1 S# {( L. G
@Override7 q& u$ o3 M; z+ ^* {* N
public void run()+ Q* C" V% Z1 {/ i- c
{
" |0 |7 M; l+ R; ^4 G# m ShellUtils.execCommand("reboot", true);$ `6 y$ I1 R# S% m
}; d. f! X. H0 x$ P; g
});
' ~$ b* U0 `8 X } catch (InterruptedException e)! h3 `4 }0 w* m' ~3 z' Y
{; n# J& @* t# J2 `/ E" a# l- k
e.printStackTrace();
- K. {; n$ x- w Z1 a+ o5 [ } I) p A2 [. G! ~+ K5 G
}0 v9 p2 R `# Q
}.start();
! P# h X7 F2 e
! |. T: Q$ @/ }( L }) e# X& {; ~$ d7 w4 }
});
5 ^ B+ L* D; b* @2 S. R2 n2 p- i4 s: O M* v4 F5 \& ^+ t+ s
textView.append("-----------------------------\n");
% X6 P3 f! x; }; O. [. M8 ~( d textView.append("该应用需要root权限!\n");
! G% x3 O/ R7 O if (ShellUtils.checkRootPermission())
* f8 W! D6 y1 j% Q2 I; Y! q2 R {
0 x' I5 `# x6 I x! T textView.append("获取root权限成功!\n");/ T) a, B) ?- h4 a5 R6 r
} else
+ {, D! x, z9 P1 ~; b* u+ V2 U {0 F$ u8 \( Y4 h- W7 A: T
textView.append("获取root权限失败,请在设置中授予权限!\n");# o1 R0 _; Y3 V. E
}2 n' z& I; u9 Q/ j3 `
9 u! U. U: a2 n) r2 ], I) J( K$ I( x; i) O: v5 n$ ~+ W; n" E
}
: D, N _7 \; g% ~/ A}[url=][/url]
0 I) W# J; b7 N" y- Y/ v2 t- h( _& R" }" w8 `/ Z# h
) t& a0 T* h$ r8 f' \
运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 8 a4 F! V* V O8 \: n8 `; D
安装包在release目录中
/ j( \' v2 K# K; T |