设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 ; a- ~7 N! [( e, R. [- p
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
$ ]9 X5 M+ f; ]0 s6 ~8 E0 y操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;3 z# c6 c% o: l) w
所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());6 E7 E$ l. s4 y6 ^$ L# S
# a% n/ K% J( ~4 [+ K; F查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]
" w' @# Y& y5 I% ?$ H0 @7 K- T* u% E$ ?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]8 n7 x$ h, U9 }, k5 V, ~, ~
8 ~: g- h# g6 |
6 E+ F) q, f4 j2 N/ V5 x( A- U 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, k% J1 D2 j0 c, B6 J
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
" `5 D, P' ^$ I& J0 S* [! y 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低,
* V. d; j6 V2 L+ y2 U( u 于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,,
: F7 x$ Y. V3 e# G尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的,
" u) p( K: Q8 j自动化程序[url=][/url]
8 W4 A4 A. F% Z$ c. R8 x9 {8 @package com.example.wxreversion;
7 H- B4 ]2 ?( j2 s1 {/ V$ h. g3 m- @1 l; U+ }
import android.os.Handler;
) D/ }* b# {9 h3 fimport android.support.v7.app.AppCompatActivity;) G% z* l, u4 Q- ~6 V, g! S7 ^
import android.os.Bundle;6 a9 G f5 @+ @- H- Z) L
import android.text.method.ScrollingMovementMethod;( X% | L1 Y+ A
import android.util.Log;% G& z0 U1 ^( l" n
import android.view.View;
4 G7 q- `* g' @- Z: qimport android.widget.Button;8 k. {4 F7 P" i5 U* ]5 [
import android.widget.TextView;
. j6 y2 p- L" D1 I0 Y
' o' R" m" D1 M4 Rimport java.io.File;
d/ ^0 s" o, \: F+ c& i9 r
4 x3 A: T- A* r1 |9 A0 I6 O, Opublic class MainActivity extends AppCompatActivity
# k- A5 ^& m0 o* c{
" G$ v6 W- }0 F1 R" h Handler handler;/ H2 c& l0 C/ N2 x) }' U$ b$ J
TextView textView;
# P- L$ {# [* Y$ G2 z* b% M) x+ T
) D/ M6 F/ K: s$ E private void LogV(String s)* H: Q, c2 q, f% d% _) X
{) i4 Z2 n7 {5 h7 ?
Log.v("test", s);
1 [9 e4 y/ Z1 S }
; J' C7 b* q4 Q2 T2 I- c% L. o |2 o# J; o
private void textAppend(final String s)- M; t# c% }# ^
{, D F, I2 p6 R j1 y7 G, b
LogV("textAppend:" + s);
9 b. d/ }* ^4 ^& B, _: U { handler.post(new Runnable() w/ [% t- C; |% B$ D& S/ V
{
- q7 O( {% B7 e1 R8 K0 X @Override$ l. k0 p6 ^: G' U8 b2 e
public void run()
' l+ `- }; h5 m! U, o- ] {$ w( z6 e5 T) B" O- N6 u, N+ q
LogV("run:" + s);
& l- G# l. \' y# |' [4 T5 R1 K" k3 u \3 n& Y6 q# b3 o
textView.setText(textView.getText() + s);
L) ?+ o2 [/ @. r0 S# E }# _% z( B; v) c4 }$ k
});
/ w7 ^& Y+ K% U% V }
" L. A. ?1 `0 M2 h/ q" V" I0 X* I- Q: I9 V
private String getPath()% g9 K0 {! Q% }+ n7 f2 r9 E
{
% k# i% t8 W# _; y: s; N String path = null;# l4 a% U }7 n' I7 h
textAppend("-----------------------------\n");
1 x" S8 a- F2 J( ^8 z1 n, o/ S9 m2 V8 U: U+ [+ S
2 ~, Y: ^. I' b1 s if (!ShellUtils.checkRootPermission())3 D0 P( A* R7 Y* ]
{4 r, T# F! }: e6 O; {/ S2 W
textAppend("获取root权限失败,请在设置中授予权限!\n");1 N- t* B& h: I8 I H
return path;6 h+ J4 B7 W) g* b
}6 s3 P: u8 \ m7 @ W2 F9 o
+ t3 A$ o3 q8 Q5 O$ \ path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;) z- [' V' P7 [; Z; ~; d
if (path != null)
% }8 ?# P) g/ {' ~ {6 j0 U W( g+ h+ b% v) C5 s( B
try
- ]; t2 m8 ~: }4 u; M" M {
4 D1 |8 C- d ]1 v y/ l path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
) o4 x7 d6 A+ O/ l" n% y9 b, y } catch (Throwable throwable)5 A4 U" J+ D$ E& K" C" {/ D! m
{' }' o" [+ q# z# h
path = null;* |8 X' H- d. z2 F0 u
}
4 P' q% C9 o& b6 r5 \ }
1 ^" j3 T% Q8 @, c L7 n+ q7 a2 Y1 C: Z" h( U; w( c
if (path == null)4 N9 z0 s8 N' `, N0 A
{
% H$ E8 `/ l+ } textAppend("未找到微信安装目录,请先安装!\n");
. `' s" @$ Z( I- a( f } else% b) C( s, o" s% g. k
{
Q: s, a4 M4 w B4 r! q& q textAppend("找到安装目录:" + path + "\n");
: i. F, l( k/ W2 [7 y7 M }
|- Y, B/ E# G" o. _) ~3 o4 i- f3 H0 D/ `+ X) {7 r
return path;( v7 S5 \1 R3 p9 K! s$ `" T
}
) E1 W1 q# Q" p+ l, ^3 s' T0 y6 c' C/ k' O/ _4 z
private boolean isEnpty(String string)
$ o( q& t0 b' h* M {
. s. t0 F; N% r$ Q1 ^( z$ I0 } if (string == null || string.length() == 0)
- W; Y: i& p0 ]9 S0 @7 [ {
1 e1 P. ?$ U7 o6 F( y7 w7 D6 Y return true;
3 G9 R6 T3 ~7 k) h }
4 ]" N2 o! i% Z' }9 m p return false;+ n" V% i$ O0 f
}* h' I$ L- n- w, \
& q1 L# \- O2 ~1 z( [- P z
private void putResult(ShellUtils.CommandResult result)
4 J/ Z/ U) j; ^8 I {
+ p( n/ E+ f$ N3 I: a& r textAppend("返回码:" + result.result + "\n");6 s8 H! @! n8 W& H @5 z
* r( h' \9 q+ j- N) L if (!isEnpty(result.successMsg))
" H# f5 k( r5 J1 v0 Q2 ]- ~ {$ e! T1 s( m) r1 l8 l3 N3 E
textAppend(result.successMsg + "\n");6 f# R" w8 ?) {1 c
} else if (!isEnpty(result.errorMsg))
8 z6 X0 M- i% |( A( @. [$ g% O9 c {
7 ^; t+ \& y& V7 q* ? [ textAppend("错误消息:" + result.errorMsg + "\n");3 Y% k- M# `6 G. ^( y
}
, x; h: `/ e* ^) l4 q }; O8 ?. h1 ~( ~( S7 P
3 K# E, N E: ]) Y; }( Y
@Override
6 L/ `! M8 z6 p# x protected void onCreate(Bundle savedInstanceState)
1 a$ a# s; Y; m5 G5 q {
, }7 D( t0 k3 b- E+ a super.onCreate(savedInstanceState);' Z6 J6 n6 Y0 |0 V) M0 w( {% W2 n
setContentView(R.layout.activity_main);
9 b$ D [- T7 O& F, u/ L; C( q handler = new Handler();
4 u; b: j9 D4 ` S+ t* B; a k: n6 p; ^; e
textView = (TextView) findViewById(R.id.textView);
5 P3 S- ]; D1 _ textView.setMovementMethod(ScrollingMovementMethod.getInstance());* A6 c; ^# n" J( ~8 x+ w; F
5 A8 K+ v8 Q) m ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()6 D" Y5 K# ~) o- @" c: s0 A
{2 p, Z7 _% f3 Q, O! C7 s. O; _
@Override# _ [6 ~* b* I% s
public void onClick(View v)
1 c- _% v( J N% Q {1 q l+ |- N9 q& I' H
8 R2 j# R2 k- h! A
new Thread()7 o$ v0 O5 R$ n- c2 V8 Z
{) @: r3 `$ l4 a. k$ ]( ]" {1 |5 y( }
@Override- W$ ?$ d. l0 |2 ~. z ^. N0 k
public void run()
( @$ e U( s# t! R1 Q8 s { c7 h6 h/ T4 d
String path = getPath();
3 L7 ]2 V) Z- K$ j' y N0 Z9 y if (path == null)( t! J! r( q0 t" R2 r6 S# V
{
3 w% U0 K& Z9 C/ d/ H5 Y; i return;! e \( I( f! ], [( S; ]# V" l
}9 O) R% R8 h: z0 @& E" t/ I
textAppend("正在保存安装目录!\n");' Z8 B* s+ S0 y# z! o6 c
ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
& ?+ g K/ Y; r# v: v5 s putResult(result);
# P1 l8 m- U% V( W8 ]- j! O if (!isEnpty(result.errorMsg))3 n, Q) R: `2 K; Y( H/ f4 b
{
! ?3 H1 F9 V# K" V2 _0 z return;9 k8 V/ C' |$ q" ~( a6 B$ ]
}! [* Q" i6 p. Y" J, @8 o
( P4 w9 ^3 d$ u& U I textAppend("正在卸载微信!\n");, Z; M/ t( Y8 {7 t$ K9 p
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用- ?5 c1 q6 S, U* X* I0 g+ F
putResult(result);$ U' s5 j; m' f# M' i m
if (!isEnpty(result.errorMsg))
j8 ?5 @: y: l% K' a/ A {+ L. w" z7 C& W" K6 M- N7 C- v
return;
* V- X. K& k9 `& d- M1 `4 M }
# x9 B/ v2 k- ?! [+ _& a- K7 `9 W5 ~* |$ c" e2 Z! _
textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");( T# O- |. u0 [/ R! z
}
, t/ f" L0 h5 W* V- w" A }.start();
* w! o: K. W9 [. x3 }9 z& z
; r1 E+ G% c8 a4 v6 H4 t" J }! t$ B' Z4 H8 |7 Q* z0 c3 C4 \
});
! F" \) Z$ }! L/ a* z: | y7 E
6 J4 ?3 V& ^9 s# d! @+ `2 }
1 H8 e7 @" k* K4 @& Z ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
- z' O! i. k% j/ Z% K {/ _' t: Y: h6 o; F: O4 U1 B
@Override
2 A8 x C7 u+ t4 o public void onClick(View v)
' X* n9 l" Y$ X1 ` {
: I; g1 W5 a1 S1 Q) U* d; O* x' m
4 j5 T" k+ A# c new Thread()
) \4 Z+ V4 v- i: B {
& @& }% W, b0 a% A* ~+ r @Override# C$ {4 i& a* j% e
public void run()3 ?# W4 Y% W" f+ M' @! a* K
{
; {% ~" m3 }& i+ U" ?# R& r& I String path = getPath();
+ L# n9 Z ~+ T4 Y6 c. s- _ if (path == null)
: I* {& g( A- M5 T9 ? {
9 K/ A$ h; e' X/ t8 E) P* \& d! i return;, C B0 l' I/ |) B
}
( z! m9 I2 A+ Z" w2 n$ z, A# m4 m, x
* x( _7 h- X( ~5 |" R, r textAppend("正在检查是否有备份的安装文件!\n");
- C( J K* e+ i, O) w1 M2 j2 J if (!new File("/data/local/tmp/com.tencent.mm").exists())
* |& p' c- C# o, O& E {
0 V3 N- L+ _8 D" m7 D9 E textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");
) S8 o( v0 V8 S7 w" | return;
& R# t: ]) {6 l/ c* J7 [2 t! [7 W }; _- y" x7 b: A: v& n* i
/ s, Q, t% I$ f+ w( A textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");1 z8 {% c3 d1 V M1 i2 V
ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
y8 J8 h, G3 p% A putResult(result);' i4 M' R& k# }3 r8 y
if (!isEnpty(result.errorMsg)) |' v$ x+ Z' z
{' `' X& a" M# z& G9 r" m
return;
. w$ M$ _" J4 `5 w }
' T% @: t! V1 D& x( R1 W
" i, S4 `& T$ ^ ]
6 A5 K6 d, b& I6 S9 w0 | textAppend("正在覆盖安装目录!\n");% X; u5 \7 N6 T* e, L* F& \# C. q
result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
/ Y g, D9 n5 C M ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
! Y' m* _5 P: u/ n putResult(result);& K9 n, R: G( g6 b0 s9 o: c
if (!isEnpty(result.errorMsg))
$ j- H7 t0 t+ i$ `. I, {" h; C/ P {7 I/ M3 P0 F+ M$ I- w
return;8 X- ]. Z( \% M/ ~- U# B
}" w2 O w/ |* c% Q, ~& E
1 e1 q( ^0 J/ A8 @* B textAppend("系统将在10秒后重启!\n");# U: M. v( j7 u9 ^8 R
* y* |' d, \! E5 a, M- U& V
. J- d' g0 R; ~ try
0 }) {6 @4 G# o {2 l$ |% V* `" D: \
sleep(10 * 1000);
7 w% m8 w- |8 m% A handler.post(new Runnable()2 F/ }" k& [: w3 E! j" i6 F
{
3 J* d( I/ S8 {( g" j/ ?4 a6 S @Override
* r7 D; [7 J$ J/ { public void run()) a- m, Z+ z8 M% P2 d
{" f, l8 ?+ z* g+ y# a
ShellUtils.execCommand("reboot", true);
2 {" [, h' H- _/ b9 s7 U& @/ Q3 v! ` }0 S7 X8 Z3 P q9 g: [/ P& R- u- U
});
3 q7 G$ J: V+ z# P( n$ X } catch (InterruptedException e)
/ n. Z$ E6 v1 w7 s5 q4 U {
$ @; `" r& B" J. e e.printStackTrace();
- x" O9 P0 H7 j& X( m( C }
- [% f' H: q Q1 G }
) [9 H2 J) o7 P6 ^% G( x }.start();
* k4 n% s8 X4 B) l
: x/ c6 t: W7 \4 M& z }+ h. v' N5 w" y, q9 f' @ X
});
# y8 P) [# o$ |% R6 S- ?4 w8 ~* L% x$ e9 }% r% i0 T
textView.append("-----------------------------\n");" O9 e; A- p2 i, M8 i0 C
textView.append("该应用需要root权限!\n");8 q2 I0 f6 b. O4 O
if (ShellUtils.checkRootPermission())( @* O6 f: W8 ^' J8 |. C
{! L# J3 b7 X, ]; N) q
textView.append("获取root权限成功!\n");
+ @# O/ t, N% A) ?+ n } else* {; _, m2 r9 T. W- w+ Y- l7 t# _
{, Z1 M, l6 i6 h4 _# i; ]
textView.append("获取root权限失败,请在设置中授予权限!\n");
2 ?0 n+ |) w0 }6 ]7 f! K9 |/ h }
% Z6 j3 @- q( S6 P# _, y$ g
: b2 }& M, ^0 `: l1 ]0 {9 }
& H/ T2 Y- a+ }2 V; l& j }" O) g5 t; B9 P$ F! }+ }6 n
}[url=][/url]
( F0 Y7 n3 Z, R) L
; ]; Q! B* O3 B$ r' g
2 j4 F5 G* t$ r6 M( ]9 d2 Z运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。
% F2 h9 e7 ^, g9 @# I& q安装包在release目录中
% X# D/ M3 x4 [7 D' i6 S |