设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 / N5 t9 z/ O- `
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。
" Z7 s5 [* c! ~$ z% c8 l操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
3 ]3 \6 L# C4 Q1 k' G# D, _" D 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString()); [1 G% Z( T7 j1 |6 O
, n1 o( I, ?. Y" I; v6 s& O查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]" i: v: ^) M- W8 A% b9 a
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]
% Q# }. j* `0 L0 `8 [, }( G4 U) _. t! p: |
' j, {' R5 K, Y3 f, l 编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,, $ k+ j0 y+ H3 k. y; H- S6 e3 g
尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号,
# t H+ v: `4 b! p' q$ i, w, D2 A 使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, 7 r# q1 @+ ?" S. t( s& o: m# i9 d
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, & p8 U; A6 [- ~5 A$ f
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的,
" `& a6 J F% ?" `. C+ r* ~4 O: z) O自动化程序[url=][/url]
$ m4 X4 H- [: ]2 }5 [; g% B3 y$ apackage com.example.wxreversion;
9 i2 _/ W) z9 D/ V) }* h# v; r; Y* m; ]) l: c
import android.os.Handler;
0 u" X# a1 A+ v/ d3 u- Gimport android.support.v7.app.AppCompatActivity; K. |. V- \& O; V" u9 h8 r
import android.os.Bundle;: u6 y0 i2 k) k8 ?# G [$ C8 s
import android.text.method.ScrollingMovementMethod;
- N. V1 W& q. M4 a6 T8 j9 g" W" Dimport android.util.Log;6 L+ L( T& T; a a/ f
import android.view.View;
2 L/ n/ o- {3 ?0 U- r2 O; Wimport android.widget.Button;
/ S$ v$ G. I" ]: Y' I7 jimport android.widget.TextView;
% }, a+ |8 W# D' Y1 Z6 h, x% ]0 q7 B% G. `% z0 `
import java.io.File;
4 W4 [- m; A1 @/ n3 a7 @
, H! ~6 j C9 b! R0 Y9 c7 ]public class MainActivity extends AppCompatActivity
* r0 q0 m3 J/ u$ H8 m{
9 [- F$ q$ _: o) _/ p" M Handler handler;! o3 G4 `" q* b8 X1 L+ D2 e
TextView textView;* e' _, m! U5 P# y+ g O( Z
) u& u! H8 [8 V$ ^
( v z( l- c, u' ]3 C private void LogV(String s)7 K6 H, ^/ _% J+ X( a
{
0 i2 R* b( b& M: z8 p Log.v("test", s);
6 M8 A* V; T% a6 N6 O4 R8 G }8 ^% v& ?* x0 `/ u& T
B) ~$ Q0 A' V5 ^+ t' s- g private void textAppend(final String s)! l, [4 P8 a3 o! w) c0 p
{
* ~! n+ A" `& j( ~9 ]- A7 b LogV("textAppend:" + s); P2 u. _) e/ T7 v
handler.post(new Runnable()
% H2 `* h7 @7 Q" I; t5 A1 [ {
' H9 q4 X% t* ~& C4 f% b! Q7 t @Override! d* t4 g; O# u& W1 T( O
public void run()+ H! @7 }6 M% V! \* y! k8 @: r$ q* i$ f
{
' Q, |& \0 T) i1 o1 } LogV("run:" + s);3 R7 S) r6 f1 u- `
$ O. d1 S) y% j textView.setText(textView.getText() + s);2 b( F1 c# p$ y ?, N, P) Z9 P9 S
}
8 q% D. a \) @0 M& d });
) }# z: f& ?- q) a# t }
, k0 B. H1 N; _2 a3 @4 x, e+ v7 e5 q! [
private String getPath()/ z! S, t! ^' P- Q, p, t
{% y2 D4 @9 D6 Z! D
String path = null;
0 m1 E, R9 a- z8 r, c; u. h textAppend("-----------------------------\n");
2 q" E% b9 ^7 u) L' m
: J* @. x3 q+ D/ O9 N9 w7 U. \5 E
3 V& `7 B% S; h; O* q- E if (!ShellUtils.checkRootPermission())+ C, x* A" l8 Q) P
{
+ C M; n o( a: g1 [ textAppend("获取root权限失败,请在设置中授予权限!\n");' I+ w, H5 B. W3 l+ ~3 L, D7 h1 i" V5 K
return path;+ D5 ?, R4 `' D! p' g( B
}
Q- I6 V. B E2 V+ Z w- B/ R0 |9 B7 w) S7 h/ ^
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;
9 z0 Y, ]. @8 o+ X% C7 R if (path != null)$ S6 P3 [! w9 X2 C1 m
{
: t( \# q* h3 q/ |) ?& b! c try6 t2 b/ @- w* H# F; X3 v1 t
{
$ b4 x- a2 ^" _7 n4 \ path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));3 C( R" u E5 X* t
} catch (Throwable throwable)% U9 S7 w/ ]4 f
{
% h6 V+ @+ ~' f& R path = null;% ?: d2 h: j/ R2 S6 S" v6 u
} D9 u: J/ C2 f. d6 k0 D8 Q" P
}; e' m, B( }) a: e" x
' u" [' Z% p% {) [3 R if (path == null)
0 J( y' S- I2 u {
1 p5 _& f5 y H, n3 Y textAppend("未找到微信安装目录,请先安装!\n");# ^% Z! S2 R D; ]7 @0 Z
} else
, J( m! L4 p" m {7 F4 I5 a& ~ {
1 `# M: }5 m7 `; a& a5 d textAppend("找到安装目录:" + path + "\n");" ?$ E, r/ B' i! W# A& l
}+ F" X" B1 M- r1 f$ W
0 D0 X" I/ v4 K: U. q return path;1 o& F: l; ]6 Q0 q& R- J% @ a
}3 E& E1 ?, x+ J1 k- ^) G
8 m! Q( Y0 i5 u2 z: L- A# L, Z
private boolean isEnpty(String string)8 r2 p; k3 t2 [$ |0 b( F9 N
{1 \4 e2 N2 E& X/ f2 M( p
if (string == null || string.length() == 0)0 x# @! ? n! G+ d( t
{; r$ C; S+ @: C* D# L5 @
return true;
! q4 c6 j5 F5 E. b( b- A }3 D) p/ F0 l( J! i# \7 C
return false;
3 J2 @5 {5 f6 |0 M- I1 x }% g6 \/ K$ X% k* R0 |% U J
% J6 h5 O% @: r( a) z, X- ]0 l private void putResult(ShellUtils.CommandResult result)6 \* F7 U1 Q# `% R2 n% ]* j
{
4 V5 X% p& x; y6 E textAppend("返回码:" + result.result + "\n");
3 U. w( j6 P) Q( Y: j1 L( e/ h) o' U% ?# p/ d1 {2 U& b* g' N
if (!isEnpty(result.successMsg))
U' A% E6 K& `) _# m {
& m( F2 S* z. b, D& d textAppend(result.successMsg + "\n");
2 t, [: W$ Q0 d$ Z' v! S1 ^8 S7 c% ^" L; h } else if (!isEnpty(result.errorMsg))
6 b, g3 N) ~7 J. ~ {
, H. ]5 l& X+ Y textAppend("错误消息:" + result.errorMsg + "\n");' `1 Y, j. x: E( P- W9 j: }
} \% D- ~9 a' m9 G7 u
}% Z( `! ?# r" |1 ]! g
' E- L' Q: M8 \ @Override
& Q, ^$ h3 D: `( e$ H. ~ protected void onCreate(Bundle savedInstanceState)! O' i& a3 |# @( U9 o4 z
{
6 {& h% a& b1 O2 f% p super.onCreate(savedInstanceState);: ~$ u J; E( N9 H1 Y8 |
setContentView(R.layout.activity_main);( v, h1 [3 {- Y& R5 M1 ]
handler = new Handler();, m& g3 O" F! O5 G) d
6 P v3 i! U c" D' `& _' s+ W( l textView = (TextView) findViewById(R.id.textView);
! k7 `1 U0 o- {. B4 S. N' S textView.setMovementMethod(ScrollingMovementMethod.getInstance());
- V* I: o" ^: j; \$ Q4 K4 d/ P8 S% ~8 A' Y$ H8 G# S- o
((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()/ f+ h7 {; ^0 v/ p6 g w
{; L$ K# E) |* W6 s1 W9 [
@Override
. [+ M# S9 Z5 x$ G6 o public void onClick(View v)- Z! {% X% G" b2 Z! R* p
{
( ?% y' _) k+ u+ |
4 T+ S& w; r1 N1 \7 n6 q new Thread()9 ~6 d% z$ l& R p0 I
{
- z$ I2 ^+ p% W0 \: d0 W8 { @Override6 ^6 ~5 I+ X& P7 q# u
public void run()! C, S$ j- i5 C
{6 w, d* s# }$ A/ f
String path = getPath();: y5 ^" h8 v# \4 k, V& ^1 a6 I0 W
if (path == null)
2 ^: }1 E% P4 f2 o3 \ {& `/ D1 U$ r, b6 ?% Z
return;
! ]: S& a- G* x }
8 @$ d# y( o0 v' F5 I$ Z* A: s textAppend("正在保存安装目录!\n");
+ p7 `, H3 @! \6 w. I2 V$ N* G ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
: }1 t3 x! N" P) \" n% H putResult(result);
1 l3 Z( @+ @; W/ W if (!isEnpty(result.errorMsg))
% x) |% T" ]$ T; O4 W+ I {- x) a9 W0 Q/ @+ T& V2 {
return;
" Z) u ?( W ~6 H }
' L. B' l- j% v \' b, R, q, h" e( z2 ~5 s" Z
textAppend("正在卸载微信!\n");
2 [( |! I3 h1 l- a! w0 v result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用7 o/ Q2 ?8 c0 L
putResult(result);
' |( U9 p- i. g if (!isEnpty(result.errorMsg))
+ K0 j [* O9 d, s# e {0 _, `' c" T+ |! O, P3 Y$ g2 X# ]
return;* m% Y; w" I) W# c; S1 `# l% r
}+ M0 c& F9 x. h S) \
- i q1 z# [3 B; t; q$ ~/ I8 { textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");0 P+ C5 f7 b! y- t v+ n9 S4 s
}
1 z N: R9 H7 ~" d! w7 ] }.start();, |$ w% h# L5 e. a9 q! l
* N8 t O$ o4 b# ?
}0 r6 `* Y* n; a% o$ b- k$ B
});
, X) P- G$ }0 P" Q( h& I8 O9 U" Y* C$ {9 C
! ~) |/ N# _7 `2 g% K/ s7 }
((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
h; e: A3 ~+ Y7 S, k. } {1 A) A6 N7 z. i% O0 v
@Override/ ^1 s/ K3 l7 Y6 j3 `
public void onClick(View v)% D( L) s# @/ |& ?4 p8 F( F
{
% T2 M6 P6 Y6 [" s6 d" K9 U
* J. I7 S: G2 _+ H4 X6 D8 u& t new Thread()
# `8 {; Y X5 m' v4 R6 h9 y {
- ]* `7 M- J! |# J8 d/ q @Override1 ~- T j$ O5 }: N, v; B
public void run()
" D! M: ?" B% y$ ^6 ~8 Q/ T# W0 _ {
0 k B r5 R& [ String path = getPath();
. e7 B9 H1 l# i: V7 g1 g$ F if (path == null)3 u% C8 P X4 E) z- K1 V
{
) w( V( A5 c. @7 d4 Q return;
! m8 H! x* y* [ }
' j3 o. m/ y7 s0 _. k2 V5 s# K' Y5 f: V9 y1 n7 s3 u
textAppend("正在检查是否有备份的安装文件!\n");
/ ? ^( f6 P9 T0 o1 p if (!new File("/data/local/tmp/com.tencent.mm").exists())
- v( I/ T* r+ |% j( M0 | { ?5 \; G% \4 K
textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");1 }6 W- u7 z6 }# A% u0 r
return; l5 `$ r+ u6 L5 v
}4 X* q- C5 _2 U. c
; |* C0 _& C- G! ?3 _* c5 Q
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");
! W. c$ H- r0 [! m. B* J0 h- j ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
% D' N8 P6 A+ I& ? putResult(result);
' u+ t& t8 V) b& {" V if (!isEnpty(result.errorMsg))
- G: ]* a% @. D- G, o) O% P {7 U" X6 ]1 g( `6 {! }1 N3 e N
return;
" {# c( u. ~' b& Q& q; g) R }9 Y4 K; a8 W$ W& X. F5 p
$ i- i* _( w6 G% z6 }- v
; K/ H6 m9 }% E3 J% A; v! x' c+ L$ m textAppend("正在覆盖安装目录!\n");
. W r& Y# } z0 ~9 I- p1 U @ result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);/ L) n6 r8 Z4 _# i
ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);
5 y/ Q3 k( y7 | putResult(result);, U5 U. R3 e/ w$ j
if (!isEnpty(result.errorMsg))6 F j' H1 b; Y3 h: E9 \; d
{0 x& Y) [- H ~
return;
+ `5 K4 _8 X; z! c0 @& }1 N2 r }; a" u+ H6 l; w! H" m9 A" B
* {- V) F: F; l- N5 P' F% X
textAppend("系统将在10秒后重启!\n");
1 d- b6 \2 x1 L A c8 H6 D, B2 d Z) x. V
2 @% a+ q1 ~. T: Z
try
$ \" G. T4 D# @ {* D4 q% ~ w& ]. c1 `0 G
sleep(10 * 1000);( ~9 @' z* r" I v f
handler.post(new Runnable()
1 a+ @, U3 E" ]/ r) c {3 R) ~. o* I; ?2 B: y6 W8 J
@Override x3 ]' J( g9 ^- l" ]
public void run()
/ v6 x" D. R* |8 E( Q {$ Q G4 n; N/ [- H' {- {
ShellUtils.execCommand("reboot", true);3 V9 h3 t% G. q- ?' R
}: a, ^" F9 b8 F- |" x' u
});
' |9 `* ~ A3 Y, f" V } catch (InterruptedException e)
, g* x( R1 M/ L1 v {
0 G/ x- O t7 O- ]' w e.printStackTrace();
4 N* g5 [/ T3 R' i }. f' @5 L6 p2 w
}
0 _9 _0 H' X# ?1 R8 j }.start();
# u/ u; P. p* g" y1 v% L+ ~1 F# z3 g `# n7 W
}
" P- g) a2 a- F& R/ Q });; E0 D9 v# X1 ?" L* p
, [2 ~& R; W2 o/ D7 x! ~
textView.append("-----------------------------\n");
' H4 |: O1 t+ l6 l textView.append("该应用需要root权限!\n");
6 A& w7 E/ ?8 z9 a$ ~8 q if (ShellUtils.checkRootPermission())& _& @% s. H( o$ n' Z9 r- c2 v
{
: |2 b/ M3 o' D% c textView.append("获取root权限成功!\n");
! ^8 u R/ c! L; \; o8 _ } else
- c0 h( ~: C+ W6 |, L {
# ]: [8 Y4 K- O- ]0 [$ H' W textView.append("获取root权限失败,请在设置中授予权限!\n");% g& a2 z+ ?6 r2 R, ^
}- T6 d# e- }# s$ C( S
. w1 X! g+ H s4 V
# l' b, o- t: y }
7 _) z- F3 B0 E/ R4 n7 ^& a}[url=][/url]
& n) j# f+ \- G: t+ A5 c! J7 s% f) h
" e. [. J7 Z& j" l9 m3 u% U运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 ! B+ e: F! u. q+ u( V! H: Q7 U
安装包在release目录中
/ h+ W- V' j* g+ ]5 i |