设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。
# C0 d) O% g u 但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
^+ v3 O: w) ]" A- T8 i操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
1 j& u9 ^# i" d% D D 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());
9 ?* j' ~ m, N, s; [( `4 v5 L$ T2 v- s! }% M
查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
+ K* _" f- v8 Q* o% m2 _3 M: `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]1 P/ ^9 g% }( i# I" m Q
9 J9 I6 K, U& [$ f4 x
0 W& Q! b: Y' e" l# n. p 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,,
0 _' ^8 P0 \4 V. e. j4 _5 h9 A$ X尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号, G7 t. P& S0 M- ?9 E8 p
使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, . z; V, V8 E9 m! w- x4 g0 z
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
+ Q- N7 F1 e/ U尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的,
0 _: I8 W$ ^; k. }3 l& q/ J5 o自动化程序[url=][/url]
2 i: ^3 `3 ?4 |. W- ^package com.example.wxreversion;
! X3 a9 B! O' w+ a/ j8 F/ a7 n! F6 s N$ P0 U/ `' \
import android.os.Handler;1 |; Q" u* F& h* G; {
import android.support.v7.app.AppCompatActivity;# z" p3 X) | n5 ?
import android.os.Bundle;
/ S6 [5 g* G$ N0 ~9 X% B! _import android.text.method.ScrollingMovementMethod;
5 p; Q& _: {/ |, {. J8 ~import android.util.Log;( y, B+ S' N8 H7 J
import android.view.View;2 B9 `4 V7 d0 p# q
import android.widget.Button;" H( F1 U' r) U7 j* N4 t% }" H
import android.widget.TextView;! _. u8 P. u( T* N) I
_, C$ N" }7 r; u/ Simport java.io.File;
! K; u9 q# L% ]7 Y$ U. p4 U M: D) s" B7 u: l: c- [, t
public class MainActivity extends AppCompatActivity
% y. Y4 b! v+ ^2 S, p( N9 e{
( o; O& u: u/ i" a- X4 }/ u3 s Handler handler;! m# j/ D" O" v# }4 `/ O, I
TextView textView;
8 R @& q$ D. [4 o. i0 n) O. R4 T: n; [! H7 J7 _" ^
& i7 b' P8 Z$ H
private void LogV(String s)
& M# X" U( z1 m8 B {
; X$ ~; T8 @, X. r# x6 E Log.v("test", s);
8 @; _0 W; g2 q$ j1 T }
4 ?" A4 Q2 c5 v W
) V5 B4 e+ _! B( F1 C private void textAppend(final String s)
1 q+ ]3 T& d1 S8 @7 g: X# D {) s" A# m# b; D8 c# Z
LogV("textAppend:" + s);, i% t+ n+ u4 r/ L- H) g% V" X
handler.post(new Runnable()- d/ T! e/ Q, D8 |" V7 M k4 o% j
{
$ o/ W# q3 q& O7 O' t, J3 g- o @Override
' n! F6 t1 q4 F public void run()
& L$ @# g, u% k/ ` {9 q/ e- t, G6 z) M
LogV("run:" + s);/ z+ A+ }! m/ z$ ]# S
' u% H8 P# y$ U3 w- U textView.setText(textView.getText() + s);6 g9 u# v' x& C O, j. T- h
}
- K8 b @* A0 M1 e8 y' r0 m( R2 q& r5 u });
7 i) j: z) G u/ }% N# Y }9 @8 o6 F: @6 k5 c+ c/ t
- ?; g' Y3 H3 ?( c
private String getPath()* T/ E7 J* ]; S0 {
{
) P0 F" b$ A; y, O8 A4 j% s2 Q1 i String path = null;5 o! e' ], `- @, {
textAppend("-----------------------------\n"); |: c2 u& A8 R; [" ^
5 G3 u2 @- r- H; I w3 V3 z
1 S! x: h. o9 \ n+ G1 l
if (!ShellUtils.checkRootPermission())( S; v8 ^5 N6 S6 @4 i, ]- h
{
. T; I. w# h) x; p2 @ textAppend("获取root权限失败,请在设置中授予权限!\n");) b' S. ~+ A9 N) Y. l! H1 u
return path;5 Z! N$ Q4 _( `4 g& F
}
4 j" h- U& g- L2 p$ s' M( [2 \. m' W! _. g" [8 `$ U. I9 e
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;3 d3 X" F h& w& F) Q7 ~7 Z
if (path != null)4 Z5 N" d6 s' g& E: {
{
, s# P9 z; s) u2 B+ F try% S' q( C; r7 @7 r( w; s2 J6 h
{
4 c4 D3 {# i; @' C* M# S path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
* t) }! f. s1 g F } catch (Throwable throwable): f! Z: v- a8 V# X: l: p
{
* }& z9 v8 _8 y8 \ path = null;
" f+ ^( F4 p" f V; J' P7 J0 v }
4 [6 e7 e0 b6 L- F6 H4 }# a/ a% @$ S }
( M: c* h8 D; u5 V. m
, J) ?( ?: s! o if (path == null). }) i% ~; [4 [
{
( n5 m9 O# O l. |8 r% q$ G textAppend("未找到微信安装目录,请先安装!\n");
$ v& ^* m' r) x% \ } else
5 I4 c+ Z& z( g) A {
. v1 d, C. B* }9 T6 @ textAppend("找到安装目录:" + path + "\n");
3 ^4 r& ]7 P! |1 O2 [ }
( K; k- W7 ?8 z) A5 }
0 ?% n5 R1 Y0 w! q# [3 T# o return path;+ x$ v" i# V; \) I9 D
}& }) ~6 J( X# i8 i5 D
# n0 Z( R) k! Z( U# e7 X
private boolean isEnpty(String string)3 ?- V* [! ~+ y# B7 c1 e; _: l- @
{6 D1 E6 w3 l1 G# w9 F9 i6 W
if (string == null || string.length() == 0)
, p0 n- J1 t, r+ g* w {
+ I" |& z4 G9 }: g, t4 u" i return true;
8 u8 s' U5 o F7 N }) {, I/ }! ~" ~6 I! o( ]7 F8 u" l
return false;
8 E2 O- I! E- r+ l/ C }
^ w3 c9 U/ f# L [, B
) y* P. }# X6 h private void putResult(ShellUtils.CommandResult result)
) A! K; T6 j* n/ W) A* Y {5 @4 M3 |# h$ @0 b8 J* S7 g
textAppend("返回码:" + result.result + "\n");+ D% G2 [- k! j* O i
' n5 S6 o8 W/ _/ H9 z% ^
if (!isEnpty(result.successMsg))& ?9 b- D$ P* K
{2 _6 U8 I4 _3 a
textAppend(result.successMsg + "\n");
( p/ H$ D0 s3 m) C; N } else if (!isEnpty(result.errorMsg))) W% W$ r' S/ |& R: @
{% d' e4 n( g+ m5 N _/ j
textAppend("错误消息:" + result.errorMsg + "\n");5 |' U- k' l: r2 _' F
}
' d/ {# s+ m# O9 X! J }* X1 B% v' b" J3 h" m
" j) `' O% M9 o3 C1 h6 N) w! k+ h( D
@Override$ |8 d3 Z$ m" Y$ l6 |
protected void onCreate(Bundle savedInstanceState)
+ I" d/ h. V0 k. y: T; e {
& v1 I; q+ K% e _9 C super.onCreate(savedInstanceState);
, C' g- N d: G- Q setContentView(R.layout.activity_main);7 M4 H5 c' f& j) O2 Q! M0 Z3 W/ Y0 ?
handler = new Handler();; { f. Y7 O0 D' g2 \, b% ~
9 i% F5 V, Q$ X: E6 e0 e textView = (TextView) findViewById(R.id.textView);
7 H8 D# F) u: P: D3 j textView.setMovementMethod(ScrollingMovementMethod.getInstance());$ d- i( T* I* b2 e3 M6 {
9 n% f4 B* x1 e+ X: [; n ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()& O! Q, ]: n4 j% J, G- Z( w
{+ S; q0 |! b j w
@Override
1 h# p9 i, D, p5 n public void onClick(View v)5 A$ J) a r! f0 a `& X
{
1 |! l7 Z8 f* l4 V/ E
6 S/ t; F8 v6 [2 \. E new Thread()
( n/ e6 j& T& D) D {
. J1 t$ @1 \# [8 q" i1 z @Override
$ i2 ?4 v1 `7 \% } public void run()- B+ a6 V- x" L9 H) ?! v0 Q
{
, A" }$ M) Z) r9 { String path = getPath();
5 b8 p, w& _% b0 Y3 w' \ if (path == null)
2 I3 E/ h/ X2 \4 O {6 s( m8 M$ Y H0 C l+ n5 y2 F& k
return;
1 ^# O( \: S2 }/ G }4 V( t7 Q1 W3 N# f% H; s9 o8 X
textAppend("正在保存安装目录!\n");
0 v/ T9 @+ l9 X; L* [ ^# R; } ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
: |" ? w" q G, D+ F putResult(result);. }& l& v7 ?, y8 V& c$ w* k
if (!isEnpty(result.errorMsg))* |+ E) Q2 w' {- D/ E' G( S' }2 E' G( [
{
& ]. S. N- A( f. l return;; Z2 u- K# G- \0 }. D0 w
}* ~' Y: ~+ c: N
/ o v. I9 ~$ g0 \9 o/ k textAppend("正在卸载微信!\n");$ \* U3 ~% Y7 S3 k3 h
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用
2 ~% t; b& |4 i8 u* |: g putResult(result);0 H8 Q3 d) `* h1 E
if (!isEnpty(result.errorMsg))
$ @ X: U3 q9 F y- H {9 c$ S8 I1 i# y ]/ z
return;
6 h- L/ D. f' V) A1 {- F }
7 u2 R5 |' K$ ~* J0 P M6 q. R% i; p6 @3 k c! T" z
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");3 V2 |/ X+ t) p* J
}
! b( ^: w8 ]* m/ O1 u9 b, K; d9 C% h! d$ U }.start();
' Q8 c7 b( R* G: V+ M( {* w
) z4 O; |8 e$ L1 w, V" `, | }0 F# I$ N1 P1 E( l
});; o% s: ~% P9 H+ l
0 P4 X$ Q7 F& I( H2 X6 C
" [: z5 z5 q% e. j0 Z" e5 p
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()9 c4 z# Q# G& f
{ M- C* Q( K, l3 W$ l1 W' G+ B
@Override
/ T/ P9 i# Z/ c |* w8 ~ public void onClick(View v)
7 a4 S/ H0 s0 O {/ [& \- w5 e- u+ ]: @
+ ^% x" P0 B# `6 k
new Thread()
( h0 P" D" q. X% y( S! f5 @ {- i- [/ k3 P* e3 \; D. r
@Override' H% ~+ {. J S- `3 M6 K
public void run()6 H, n& W& E- v/ j
{
, t) O" d4 d. |' R9 d4 u String path = getPath();' `1 D q9 M( w! I9 i, L
if (path == null)
2 ~- h9 {4 S( N& O4 Z$ f ]5 N+ y* w {7 u5 V" e! U7 W( t6 w
return;
2 Z4 R6 n1 c( C5 C }3 k7 r7 q! O, L7 F. v+ N
" c* p2 n; c* o) t( W! h2 r( c" |
textAppend("正在检查是否有备份的安装文件!\n");! g0 y4 @2 R1 V- \7 W' d, J$ @
if (!new File("/data/local/tmp/com.tencent.mm").exists())
% T( N9 M$ |" D {5 W! v8 k% ~, \3 e6 }" _1 O
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");( T& i: E0 f: p8 b9 g
return;
8 d# \% Z% [5 [- i }
. G; K" d, z7 ~. W, `: S5 z
; L. Y& a: B' K textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");2 `$ i# b4 O3 T9 P2 T, A
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);5 { r; p! [$ C" L# W: g& ^6 l
putResult(result);4 O0 o, g+ `& Y% \
if (!isEnpty(result.errorMsg))9 j0 Y" `1 @* C2 f1 P
{! M( D( P; @. L
return;
3 z. C# c8 g1 A0 c* ~ N }3 b; V" g, Z: q. I8 l/ ~) i3 _
t# B# ]& q- N, Y* I9 |% P# f/ t3 v( g
textAppend("正在覆盖安装目录!\n");
, U2 P2 E% `9 }7 ?) e: I result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);! y8 @7 H3 C# |! V ]7 L, K
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);' C& }; {( T* U) q5 m+ ]* G9 f
putResult(result);6 ?- ~' M& g/ J2 q i( t, ?* U- Y
if (!isEnpty(result.errorMsg)): f- N. X1 |! i5 N* d
{
$ \" z' _, q* F5 S6 _8 x4 @0 i% j: e return;
3 @) w% q; Z1 {: p }
. ]* ?* g4 F' a/ r: \: X6 a8 v7 m! ~2 b& ^
textAppend("系统将在10秒后重启!\n");- S: _. t% a9 |3 I5 G6 E. k) ?
. z+ _! \" L# B+ [. f% N, V/ X
2 R$ G; Y* |8 G2 ^, ^" Y9 s' _
try
5 B4 w& ^: ?, j2 H2 S h {
# o+ e. g5 |/ I sleep(10 * 1000);- _ i9 W# V3 a) Z8 i$ x
handler.post(new Runnable()( U* a2 k. d& `9 U8 J+ n+ e
{
0 |. o4 w( C0 c( C, r5 f @Override7 Z6 u) F# f8 X
public void run()2 e, C" y1 s9 g3 H9 Q
{; y! u' H) X: {3 N, e) N
ShellUtils.execCommand("reboot", true);/ ^: y N$ P& l6 Y" q3 O7 G
}
4 }1 |2 j: }% p% M$ E( A });1 P+ x* o9 C" m) G' j" i5 z
} catch (InterruptedException e)% u' }( W* S! z' [: }6 a
{
% C P3 f* @' `+ R. V e.printStackTrace();
: `0 Z0 d9 N9 H9 A }
" y6 o4 b; Y3 E+ U' I, B: u }- C, T# A) Q" b l: \7 r) Q' J- J; E
}.start();
9 g7 M( C1 ^& `: A
5 C B! A0 h) s8 d2 k% A9 i }
$ l) B: f6 t {, K });
9 H. n: n# K, p! |, k3 q' y! ?+ g3 m \' ` z& M2 G: T! ^* Q
textView.append("-----------------------------\n");: |" [4 {' y* Y0 s' H$ c
textView.append("该应用需要root权限!\n");
$ R/ V1 ~0 h* O" M) z0 c if (ShellUtils.checkRootPermission())
' `" b% y0 K7 M! U3 M {
4 s1 E% R+ m/ \: y+ f3 l textView.append("获取root权限成功!\n");
$ `- X5 B' ~9 L% Y } else& j, L1 o: [2 Y6 l; J
{
+ y% j( C9 M1 V textView.append("获取root权限失败,请在设置中授予权限!\n");
# s m( x3 E$ [" o2 N }
0 @% ~: N+ U z% E0 o- w! ^' A& f* b9 i0 j( d
5 t+ |7 S7 s" s3 C4 c* t
}
, P" c" e0 i& f! K0 T; ~% b}[url=][/url]- M( v o. V1 N
" K6 D3 M2 @) r
, g4 E2 R7 b B9 D+ `) d. ? t X运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 4 q) ^' q* k; k5 H, M3 @6 l) `$ S
安装包在release目录中
; u- Y0 X0 ^; [0 f |