设备要求 已root的Android手机。 注:下面使用的两个微信安装包,com.tencent.mm.apk为旧版本6.5.16,weixin_new.apk为新版本6.7.3 背景 最近在弄一些关于微信的东西,测试过程中,本来打算强行停止后重新启动微信,结果手残点到卸载了。当我重新安装后出现了尴尬的情况,登录的时候,提示微信版本过低,需要安装最新版才能登录。 / T3 i3 r* d; I; d! O
但是之前做的一些东西都是基于老版本的微信,所以不能安装新版本,必须想办法在老版本登录才行。 4 K2 _5 q' P7 s, ?' L; m$ W
操作过程尝试1、替换版本号 最开始的想法是,既然要验证版本,那我就把旧版本的伪装一下,让它变成新版本的试试。 但是,因为没有时间去仔细分析微信是怎么验证的,于是就抱着侥幸心理,写了个xposed模块替换版本号, 一般情况下是通过以下代码获取版本号的: PackageInfo packageInfo = getPackageManager().getPackageInfo("com.tencent.mm",0);int versionCode = packageInfo.versionCode;String versionName = packageInfo.versionName;
# s+ `" q0 H. ?) c) J 所以去hook getPackageInfo方法,将其返回的PackageInfo中的versionCode和versionName替换成新版本的值就行, 但是,由下图可知PackageManager是一个抽象接口, 所以不能直接hook它的getPackageInfo方法,要先获取getPackageManager返回的对象的真实类型,先随便创建一个工程,通过以下代码获取真实的PackageManager类型, Log.v("test", getPackageManager().getClass().toString());0 `5 l' S5 Z! K- b; D- D/ x3 ^6 @! C7 W
4 u' ~+ d3 l2 |9 j2 C! U0 {% P查看日志,可知真实类型为android.app.ApplicationPackageManager, 然后通过反编译最新版本的微信,获得versionCode和versionName, 最后的hook代码如下: [url=][/url]3 {: t/ o/ z% S p" T
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]" y5 ]9 j' G) B7 C& T, {, `+ @
4 d0 |9 J @3 x% t) J+ e: P3 O" l
4 H7 e7 g% c! Y' p8 }- H7 U
编译安装后重启,一边输入帐号密码,一边祈祷,结果还是提示版本过低,,
. j0 e* n6 V/ F$ }5 F尝试2、pm uninstall -k 命令 想着之前旧版本的都能正常使用,说明只要有登录数据,就可以使用旧版本的,所以想着先用新版本的把帐号登录,然后使用 pm uninstall -k 命令,卸载应用但保留数据,然后安装旧版本的。 先安装一个新版本的微信, 登录帐号, / o) p C g0 k" O, z, C
使用pm uninstall -k com.tencent.mm卸载后安装旧版本的微信, 结果打开还是不行,还是提示版本过低, % s0 Z3 v2 S7 J$ S3 y+ t
于是怀疑卸载的时候数据被一起删了,根本就没有保留,再次执行pm uninstall -k com.tencent.mm,查看data目录,微信的目录已经不存在了。 尝试3、adb install -r 命令 同样先用新版本的把帐号登录,然后使用 adb install -r 命令强制安装,结果还是不行,不能安装比当前版本低的,,, . v: F) }: f# |( c, P( ?* Q7 j+ ?3 a
尝试4、替换安装目录 既然用 pm uninstall -k 卸载时保留数据不行,那么就尝试手动替换。 首先安装低版本的微信,把安装目录复制一份,然后卸载。 再安装新版本的微信,并登录帐号。 用之前保存的旧版本安装目录替换新版本的安装目录,然后重启手机。 重启后提示登录错误,重新输入密码即可, 不知道为什么,这个手机在我写这篇博文的时候,关于微信的页面还是新版本的版本号,而我之前弄的另一个手机就是真实的旧版本号, 但是这个手机系统设置中微信的版本号已经变成旧版本的了,也不影响使用。 另一个手机的, 8 D7 ]6 t9 S' k% f% e6 Y) Z( n- ?
自动化程序[url=][/url]
' B* s& ]6 F: ~8 T, ?# o8 |2 Apackage com.example.wxreversion;
. W1 M! y0 x2 d$ ]7 P" C- j0 B7 s. o2 G0 P, |! ]
import android.os.Handler;6 G* H. g, D$ I6 Y/ D1 V7 _8 K
import android.support.v7.app.AppCompatActivity;1 Q& L7 a& A& Y/ N6 d& y2 ]" ?4 Z
import android.os.Bundle;
. S1 g$ L& y6 Z) ^( p/ u$ Y) aimport android.text.method.ScrollingMovementMethod;- q P4 P$ V9 |6 p7 l' R3 ]
import android.util.Log;
' c7 h( D2 Z* i/ T5 k2 }- simport android.view.View;
7 C( k. N8 y! }' K; q, A4 C! Timport android.widget.Button;( Z5 ^6 X+ B' y5 h' J) Y
import android.widget.TextView;' g+ v- d6 ], `2 Z/ x
/ A, c7 U5 }3 F( Z# u; [0 U
import java.io.File;
) _" h4 r8 Y7 j* M) Z' b0 s- |0 T1 o5 |% T# F$ B. F
public class MainActivity extends AppCompatActivity
2 u% K0 q1 x6 X# j+ ^8 o{
" J% x" j4 F& ]1 [7 W& D+ R, ? Handler handler;" D7 V- o6 h/ c* Y# b7 R1 G
TextView textView;) G7 q- M, \: ?: M s, Y
+ ~: x# B' s ]( O6 P% m4 E
: U' q9 q/ \5 S4 ^* {
private void LogV(String s)
% t1 q' ~9 F) D+ X {) `. x$ g; p$ q2 T# X
Log.v("test", s);# ~6 g1 w( F h5 e
}
8 `( P. [7 f8 K& x m0 b$ G
- O- P o& }" E1 K& B% x) ] private void textAppend(final String s)# F; t D4 f {
{
& z& h" r$ `/ F4 r LogV("textAppend:" + s);
2 @' b6 t/ k4 P) w' `' v2 \ W# r handler.post(new Runnable()& W2 l+ u# y- a8 D, S+ ]9 J' B
{
; R- ` E) W/ N) O) D0 t9 s0 h) ^ @Override
e9 T& k' a! d9 h8 R8 J9 ? public void run()
; S' o. F, C- P- U0 m! [4 t0 G {
0 {- G) v/ G" f7 a0 A LogV("run:" + s);1 v, c: i" j0 v
* X& A& L, c m0 W/ s* W8 d
textView.setText(textView.getText() + s);- K* Q9 ^- _( h' B; K6 H
}0 x) B, h8 A$ j3 y
});! _- Y' j' N' U4 w- w
}
6 r T6 E& C+ |4 a" ?0 _* f9 G
, d. ~0 I, Q& ^' M* R. _/ S+ e _ private String getPath()( c" o2 L/ F/ G! C
{/ r/ a$ ?+ d9 \
String path = null;
4 e: S6 V( R o# X2 N; q+ D textAppend("-----------------------------\n");
4 y" S3 H' a- |8 P" |! ~! v
% }8 t$ Y/ o5 Q8 i
, ]( w( M* e8 s3 _ if (!ShellUtils.checkRootPermission())0 v+ k2 x# i+ X. F
{
, X& z- p& E4 |0 _2 C& T* P' d/ ]9 C textAppend("获取root权限失败,请在设置中授予权限!\n");$ a" t7 J& z+ s* E
return path;% R7 r2 J. |! y# W
}
6 d8 T$ t$ w; \+ _, Y, m+ `. e7 o2 d6 B, k
path = ShellUtils.execCommand("pm path com.tencent.mm", true).successMsg;, M; B7 `/ G) T; f
if (path != null)$ [% ?( j" [3 w3 Z- O) w
{
1 I+ g2 h g8 W try1 r! ?% Y& R- `: }* Y, B( y( h0 U
{: t' t5 J t X2 A- v% Q/ F
path = path.substring(path.indexOf('/'), path.lastIndexOf('/'));
* o9 J) e e( Q9 r3 d8 \" } } catch (Throwable throwable)
% g( O j0 A0 W4 h5 F {
8 G: M! D3 Q+ \3 C s path = null;5 C1 O; g) ^4 y( d3 Y
}- E- [& j' Z2 q" U K6 j# q
}* Q* m1 Z/ a: _+ x
5 N5 S* [5 v0 ^$ M- O2 G' p if (path == null)( X4 x: v' x3 J1 j
{
8 W- V9 C8 y# O3 m1 g textAppend("未找到微信安装目录,请先安装!\n");
/ k1 Q, {+ Q8 l6 { } else
/ E3 Z* W) s ? {
: H" o- a+ E" p, N3 G) b$ g* u textAppend("找到安装目录:" + path + "\n");. o, a% H& B( b/ X' w* u9 [7 w
}+ s( A$ ?2 {& p2 O( R6 w; V
% T- X' b2 \# g7 R& ] return path;! O( T- O. A3 [
}9 Z" h1 d; f+ i- i r$ E5 A
6 w1 u! V' z2 C- ~
private boolean isEnpty(String string)
# R7 c" h* r8 k% p {
; A8 |5 r3 m& U if (string == null || string.length() == 0); R. X8 v2 g% n# z: c! D. P$ W
{
5 g! t% g& P0 ~& y' A$ e return true;
2 U. r: d Y+ m; c7 r }
8 e$ w$ J2 ~3 H4 @" ]5 J0 u return false;
" l3 a; l3 X, r }
$ I7 c( P9 S0 ~7 H3 l# j- O5 Y% g9 A5 e9 t
private void putResult(ShellUtils.CommandResult result)
; T4 S% m. U& K& _3 u" \; _- n. I {
7 H* T( M7 l3 ]; B textAppend("返回码:" + result.result + "\n");8 I& u% x6 e* ]1 ]; U8 ~9 d5 L
- q' w7 j) R8 H4 G1 x# |
if (!isEnpty(result.successMsg)): D/ X% @8 V8 [% h" @4 e
{
+ ]. x2 P# a2 S textAppend(result.successMsg + "\n");2 M4 c& Q6 H' Y3 X+ i2 { Y
} else if (!isEnpty(result.errorMsg))
1 |; n; v6 t" W {% B6 V$ F. n" \$ I
textAppend("错误消息:" + result.errorMsg + "\n");$ \, U( |# ^7 d( S
}4 V- g& h( H2 }3 b, c% G
}, g5 J$ s/ d. S0 U
9 t) x5 d0 O. ^# `2 e
@Override" m% ~' g, L9 S
protected void onCreate(Bundle savedInstanceState). ^2 X7 d6 n+ Z# W x; Q' a
{4 k0 D& _$ m, T: {$ o
super.onCreate(savedInstanceState);
% q' w3 A; E' @+ x: f setContentView(R.layout.activity_main);, G% n, ?- w, t
handler = new Handler();
, \- P/ O3 G u' `5 y) f- r
6 o7 \ O. u- s/ O6 H9 Q$ H( {2 M textView = (TextView) findViewById(R.id.textView);) S. p- e( \% v5 d( W# `- W
textView.setMovementMethod(ScrollingMovementMethod.getInstance());
8 \ H1 v, L3 c4 \' j4 h) {
' k0 a2 D \' g/ }$ ?- y ((Button) findViewById(R.id.button1)).setOnClickListener(new View.OnClickListener()2 s& H/ d$ f, k# W+ ?) _
{
4 C4 B4 D& w& Z @Override3 H: B$ V, m. O9 M4 }
public void onClick(View v)$ U- R2 m: R& c) V
{
3 e2 O6 A7 i8 B) \% y+ O: K3 D2 p
new Thread()4 t( L$ {2 q7 x' P
{# ^% q, y6 v' V+ z0 `
@Override
6 [" S% a) D G2 P { public void run()
) o* T5 _7 {7 l& `7 o {4 \4 ?$ G; C/ ^$ x
String path = getPath();
% R1 D B8 c5 E7 B8 l6 R5 g if (path == null)
& B+ ~! ?" y* c) ^, x5 e5 r, \ {
0 u g5 P& U0 r; b- ?) d) P return;4 S/ M' p6 K. Z* F8 @9 p0 t6 ^1 b6 T
}( s5 s, ~& M3 `% k% A
textAppend("正在保存安装目录!\n");
0 m! m- o. L1 Y# ], A# `" ` ShellUtils.CommandResult result = ShellUtils.execCommand("cp -af " + path + " /data/local/tmp/com.tencent.mm", true);
- M6 ]0 ?. g$ C' v8 B putResult(result);
. ?" N! _8 {5 d5 C0 t if (!isEnpty(result.errorMsg))
& u% a$ P% y3 U' Q% ` {
6 u4 K3 L/ Z- m1 N, Y; g return;
2 y6 x: {1 W! Q9 F8 B( L3 {/ _ }
0 z* [) q# R( l. a
; [: A8 n t( j textAppend("正在卸载微信!\n");. ]% v/ S8 b5 X$ | \2 _) _3 d
result = ShellUtils.execCommand("pm uninstall com.tencent.mm", true);//-k无用' @$ b/ ?$ ^1 `+ |* }9 }
putResult(result);
* N2 b& W6 n2 j& F5 i! e1 t if (!isEnpty(result.errorMsg))
3 k1 m! b* \* v+ {- O( C {
5 Y6 }( k, a1 s9 t1 ^% H# D- G# x) C return;( F3 q0 F) q7 \8 V
}
4 F0 F( _7 j Q7 c% ^
+ b; \" H7 I7 m+ U( x( i textAppend("请安装新版微信,并在登录成功后点击【覆盖安装文件并重启】按钮!\n");1 Z' k0 f4 h' O p* L) B
}
# u I# e7 a7 K" C2 i }.start();, _) V4 W; R& D9 P3 [: Y- Q
& W( G- q9 ?" |7 B+ Q4 H# B }4 V+ k& C* Q) ~% `9 `
});
3 f% D8 `5 m$ g6 T7 l+ Q
1 j8 r5 s; T% M- u) F$ l5 R
/ v9 j! K; b" ]4 ?8 |8 K ((Button) findViewById(R.id.button2)).setOnClickListener(new View.OnClickListener()
" z3 Z& x/ z7 V& M( Y$ Y {" c& C- X2 W9 ?! _3 ^
@Override% w7 b- X4 ] p& Q1 A& u1 v
public void onClick(View v)
; ?# r, W! d2 ]0 S3 u0 }, ? {
]5 t) H# A$ Z0 W+ W4 z4 J- T3 n3 G* b3 E+ b+ k
new Thread()
3 U4 f. g. z8 p+ c; T {; G' R' l8 P0 S3 g
@Override9 e) }0 x: G" l/ n4 x2 R' k) q
public void run()
, X* d. P* A- a5 B {9 k+ `: U2 ~- f2 H) Q7 P: e7 }: u
String path = getPath();
, |- e- v! a; f6 x( y if (path == null)
# a- ~5 s- ^# e% i O+ x" T" B { J& s! U! A* `2 u+ k# d
return;8 k8 X* B+ o+ X, v1 c( V- S+ c
}
0 b G/ c: e( E5 m9 q/ d
: t2 E% c1 _# h4 s( Q textAppend("正在检查是否有备份的安装文件!\n");' N1 m( |3 f: k
if (!new File("/data/local/tmp/com.tencent.mm").exists())
0 ], O7 h9 l* s+ \ {
8 u1 `1 ]0 I9 B2 N; P! z# [& L textAppend("不存在备份的安装文件,请先安装低版本微信后点击【覆盖安装文件并重启】按钮!\n");, E9 u, _( w3 y' J9 T- o5 g
return;
- ]1 E2 F2 o* b7 ~8 Y9 @! y }8 }$ Y& |0 U6 |2 K: Q$ Q
/ s, @% ` p& L$ G z4 z* ?
textAppend("已有备份的安装文件,正在删除当前的安装目录!\n");
9 Z6 D7 A$ D) `! S4 y' R! X ShellUtils.CommandResult result = ShellUtils.execCommand("rm -rf " + path + "/*", true);
. S! Q% g8 a( s9 ~, k# H, U3 O putResult(result);, k4 b) S2 b7 e9 _" P& R
if (!isEnpty(result.errorMsg))/ m7 i; g& G/ { ?. U
{
& H( ]% b) `* z! ? return;
: U5 h( J$ W: ^6 e( Q }* L/ E8 h2 W2 C! T7 y2 i9 ^) l4 {" a
6 e: R, m) L4 b$ k( @) |
/ V3 ~. r# |7 _! U! U* }
textAppend("正在覆盖安装目录!\n");
$ H; k9 D0 ~: u% k D4 B1 i) M result = ShellUtils.execCommand("cp -af /data/local/tmp/com.tencent.mm/* " + path, true);
" E5 P6 K% k, J/ G0 c4 |/ c ShellUtils.execCommand("rm -rf /data/local/tmp/com.tencent.mm", true);; i# Z3 e! A K9 u3 t
putResult(result);6 f" O; q8 q j+ m1 N, ~! d. n. \
if (!isEnpty(result.errorMsg)). r* ?; Z+ b4 p0 W6 N8 a/ M
{
7 F& w5 ?0 w8 V. c$ J+ t2 @ return;2 s1 s( j' O1 W3 u
}
. Y/ A' ]5 T; O Y# J1 S# u2 ^
3 w3 B6 U1 @, t( i6 P- W% ]: s5 u/ W textAppend("系统将在10秒后重启!\n");
8 Y. _! D" }% y/ i/ z
8 ^) n# N5 a" S6 } S7 @$ {* Q' |" d1 r" n1 D/ b% B9 x
try
+ ]- H( X' O* l; o9 J" A7 B' d {- ^; c4 @5 a) R2 u3 G% e/ |
sleep(10 * 1000);+ @8 ?( w: i+ l6 d) ?4 d
handler.post(new Runnable()% m4 f1 l9 C, Z% ^; t1 p
{
2 x: R/ k& K& I3 e7 M% B- ? @Override4 J& i! O6 R* B6 t2 z
public void run()2 ]% |" r: v: _- z& [4 b" U
{3 T3 `8 R% w8 U; e' }
ShellUtils.execCommand("reboot", true);
1 f( M3 C# ]: r0 Z7 b, { }
' Q$ l5 `8 E( ]3 U9 l3 I- L! r! o });
9 e0 i- O6 _+ ?' k+ w( b# v9 y) H } catch (InterruptedException e)
, m* J* I, p& ]5 t5 f5 k, U {, k7 f, q/ R4 N h
e.printStackTrace();; u0 }1 }; B! w
}& A% s" ]7 D6 w. Q' ~
}/ k: i/ r. j& A
}.start();
! ~( U3 H! W3 F
0 Y+ k4 Z- F4 Z: ?. L5 |' h }! t' b o* O: N+ ^0 x$ m. _
});
" b. Y: |' O2 b# r/ i7 w9 A1 n, m s% H: v7 u9 ]3 g# o
textView.append("-----------------------------\n");3 b5 i y3 U/ Y" I+ `
textView.append("该应用需要root权限!\n");
' x1 M0 A+ y: i+ E& m! g. Z) j if (ShellUtils.checkRootPermission())$ t6 h. g, ?. I! {0 [5 t5 @
{; m8 S; X U/ m
textView.append("获取root权限成功!\n");. p. K) m1 f7 y, k* A, J/ M* `
} else
2 U& V* H, g U2 q- p/ i4 ]. \ {% s* X+ u' r. z- j3 q" U3 W) o+ h
textView.append("获取root权限失败,请在设置中授予权限!\n");
: L( `" ?; s2 ]6 r( G }
! |, R* |8 _( H: ?# M4 P6 v( y! @/ R2 B! B
2 _3 A& W+ M3 T' T2 H( W. M
}# a5 j2 W: Z: i1 `6 W' R3 t+ F
}[url=][/url]/ J! o6 b7 P: w" r( ~
5 Y1 R% g9 M& P) _% D2 y/ G
) @# b b9 D8 c* K& Q! |. X8 z运行如下: 先安装旧版本微信, 运行该程序,点击左边的【保存安装文件并卸载】按钮, 安装新版本微信并登录, 点击右边的【覆盖安装文件并重启】按钮, 重启后重新输密码登录即可。 K4 W/ R0 o: W7 f/ a& u1 T2 `. G
安装包在release目录中
- [% I, {0 H9 b$ O* W7 t) N |