欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android中如何安全地打印日志詳解

 更新時(shí)間:2017年12月03日 08:52:02   作者:Weishu  
日志是我們?cè)谌粘i_(kāi)發(fā)中必不可少的一部分,下面這篇文章主要給大家介紹了關(guān)于Android中如何安全地打印日志的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們一起來(lái)看看吧。

前言

在Android開(kāi)發(fā)過(guò)程中,不管是寫(xiě)Demo還是實(shí)戰(zhàn)項(xiàng)目中,都會(huì)打印一些日志用于記錄數(shù)據(jù),調(diào)試來(lái)著,Android中的日志工具類(lèi)是Log,這個(gè)類(lèi)提供了一些方法來(lái)打印日志。五個(gè)級(jí)別,v、d、i、w、e,各有不同的重載。

當(dāng)談到如何打印日志?很多人會(huì)想這不是很簡(jiǎn)單,直接使用android.util.Log這個(gè)類(lèi)不就行了?然而,日志屬于非常敏感的信息;逆向工程師在逆向你的程序的時(shí)候,本來(lái)需要捕捉你程序的各種輸出,然后進(jìn)行推測(cè),順藤摸瓜然后得到需要的信息;一旦你的日志泄漏,無(wú)異于門(mén)戶(hù)洞開(kāi),破解你的程序如入無(wú)人之境。

安全的概念本來(lái)就是相對(duì)的,如果破解你程序的代價(jià)遠(yuǎn)遠(yuǎn)大于破解得到的價(jià)值,那么就可以認(rèn)為程序是“安全的”;這里就分析一下,為了提高程序的安全性,在打印日志的時(shí)候應(yīng)該注意什么。

首先看看絕大部分公司以及開(kāi)發(fā)者的做法:

日志開(kāi)關(guān)+日志類(lèi)

為了在release版本里面沒(méi)有日志輸出,一個(gè)最簡(jiǎn)單的想法是:把所有打印日志的語(yǔ)句放在一個(gè)if(DEBUG)的語(yǔ)句里面;在日常開(kāi)發(fā)的時(shí)候,DEBUG開(kāi)關(guān)打開(kāi),發(fā)布正式版本的時(shí)候關(guān)閉這個(gè)開(kāi)關(guān)即可,大致思路如下:

// LogUtil.java
public class LogUtil {
 private static boolean DEBUG = true;// 發(fā)布的時(shí)候修改為false
 
 public static void d(String tag, String msg) {
  if (DEBUG) android.util.Log.d(TAG, msg);
 }

 // 其他debug方法
}

接下來(lái)看一個(gè)真實(shí)的例子,國(guó)外的一個(gè)apk,名字叫做powerclean;包名:com.lionmobi.powerclean;我們安裝這個(gè)包;發(fā)現(xiàn)很正常,沒(méi)有任何日志輸出;然后我們逆向這個(gè)apk;隨便翻看幾個(gè)類(lèi),發(fā)現(xiàn)很多地方有類(lèi)似日志輸出:

日志輸出圖片

我們打開(kāi)這個(gè)叫做x的類(lèi),雖然被混淆過(guò)了,但是意思很明白,跟我們上面的思路一樣:

package com.lionmobi.util;

import android.util.Log;

public class x {
 private static boolean a;

 static {
  x.a = false;
 }

 public static void d(String arg1, String arg2) {
  if(x.a) {
   Log.d(arg1, arg2);
  }
 }

 public static void e(String arg1, String arg2) {
  if(x.a) {
   Log.e(arg1, arg2);
  }
 }

 public static void i(String arg1, String arg2) {
  if(x.a) {
   Log.i(arg1, arg2);
  }
 }
}

這是一個(gè)真實(shí)的例子,而且這個(gè)app的用戶(hù)還不少;接下來(lái)我們看看這種方式有什么問(wèn)題。

靜態(tài)反編譯打開(kāi)日志開(kāi)關(guān)

上面的那種方式有一個(gè)問(wèn)題:雖然在release版本里面,確實(shí)沒(méi)有日志輸出;但是輸出日志的代碼依然存在,只是沒(méi)有執(zhí)行到!(if條件不成立)所以,有沒(méi)有辦法讓這些代碼執(zhí)行到呢?簡(jiǎn)單來(lái)說(shuō),就是能不能在release版本里面把這個(gè)DEBUG變量弄成true呢?當(dāng)然可以!而且做法還非常簡(jiǎn)單。

我們使用apktool反編譯得到這個(gè)apk的smali代碼;然后上面的反編譯告訴我們,這個(gè)日志類(lèi)的位置是:com.lionmobi.util.x我們打開(kāi)這個(gè)x.smali文件,內(nèi)容如下:

.class public Lcom/lionmobi/util/x;
.super Ljava/lang/Object;


# static fields
.field private static a:Z


# direct methods
.method static constructor <clinit>()V
 .locals 1

 const/4 v0, 0x0 # 修改為0x1 (True)

 sput-boolean v0, Lcom/lionmobi/util/x;->a:Z #初始化位置

 return-void
.end method

.method public static d(Ljava/lang/String;Ljava/lang/String;)V
 .locals 1

 sget-boolean v0, Lcom/lionmobi/util/x;->a:Z

 if-eqz v0, :cond_0

 invoke-static {p0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

 :cond_0
 return-void
.end method

.method public static e(Ljava/lang/String;Ljava/lang/String;)V
 .locals 1

 sget-boolean v0, Lcom/lionmobi/util/x;->a:Z

 if-eqz v0, :cond_0

 invoke-static {p0, p1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

 :cond_0
 return-void
.end method

.method public static i(Ljava/lang/String;Ljava/lang/String;)V
 .locals 1

 sget-boolean v0, Lcom/lionmobi/util/x;->a:Z

 if-eqz v0, :cond_0

 invoke-static {p0, p1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

 :cond_0
 return-void
.end method

很明白,那個(gè)叫做a的靜態(tài)變量就是我們的開(kāi)關(guān), 它的初始化在哪個(gè)靜態(tài)代碼塊里面;新建了一個(gè)局部變量0x0然后賦值給了a;因此,我們把這個(gè)0x0修改為0x1就打開(kāi)了這個(gè)開(kāi)關(guān)。很簡(jiǎn)單吧,接下來(lái)我們把修改好的smali打包回去,然后簽名得到一個(gè)新的可以運(yùn)行的apk;運(yùn)行一下看看結(jié)果。果然,一大堆的日志輸出了出來(lái),你的程序每一步在干什么都自己告訴別人了,都不需要去猜;我就隨便截個(gè)圖,感受下:


泄漏的日志信息

讓release版本里面不包含日志代碼

從上面的分析我們得到一個(gè)結(jié)論:如果需要程序是“日志安全的”,那么release版本里面不應(yīng)該存在輸出日志的代碼。

如何做到這一點(diǎn)呢?我們可以做一個(gè)工具,開(kāi)發(fā)的時(shí)候,正常打印日志;一旦需要發(fā)布版本,把所有打印日志的語(yǔ)句代碼,全部刪除掉。代碼很簡(jiǎn)單,用一些正則表達(dá)式就可以做到。

事實(shí)上,我們也可以使用一些別的工具,來(lái)實(shí)現(xiàn)這個(gè)類(lèi)似的功能;那就是proguard;提到這個(gè)工具,很多認(rèn)只是覺(jué)得他是一個(gè)代碼混淆的工具,實(shí)際上,它還可以幫你剔除無(wú)用代碼!什么樣的代碼是無(wú)用代碼呢?

if (true) {
 // statement;
}

類(lèi)似于這樣,靜態(tài)編譯的時(shí)候被認(rèn)為“永遠(yuǎn)不會(huì)執(zhí)行的代碼”,就被認(rèn)為是無(wú)用代碼,會(huì)被這個(gè)工具直接優(yōu)化掉,生成的class文件里面,這個(gè)if語(yǔ)句直接就沒(méi)有了。這個(gè)功能,完美符合我們的需求;我們只需要把輸出日志的代碼用這樣的if語(yǔ)句包圍起來(lái),然后release的時(shí)候肯定會(huì)用這個(gè)工具混淆;然后,在release版本里面,所有的輸出日志的代碼全部都沒(méi)有了!不會(huì)像以前一樣,留下一個(gè)影子,只是不做事。

正確的做法

最終,我們所有打印日志的語(yǔ)句應(yīng)該如下:

private static final boolean DEBUG = true; // 必須是static final 也就是常量,這樣才能在編譯器優(yōu)化;刪除if塊

if (DEBUG) {
 android.util.Log.d(TAG, "msg to print");
}

然后,使用proguard優(yōu)化代碼即可。

看起來(lái)簡(jiǎn)單,好像也與最初的“日志開(kāi)關(guān)”沒(méi)有什么區(qū)別,仔細(xì)分析一下:

日志開(kāi)關(guān)必須是靜態(tài)常量

對(duì)比一下正確的做法與最開(kāi)始的日志開(kāi)關(guān),一個(gè)是一個(gè)靜態(tài)變量,一個(gè)是靜態(tài)常量;如果是常量的話,那么就是永遠(yuǎn)不變的,那么當(dāng)DEBUG變量為False的時(shí)候proguard可以理所當(dāng)然地認(rèn)為,這一部分代碼時(shí)絕對(duì)不會(huì)被執(zhí)行的,這樣,打印日志的語(yǔ)句就會(huì)被優(yōu)化(刪除)掉;如果是一個(gè)變量,那么在運(yùn)行期間就有可能改變它的值(private僅僅是對(duì)于程序員的改變,對(duì)于編譯器以及運(yùn)行時(shí),沒(méi)有什么改不了),這樣proguard就會(huì)置之不理,這樣你的日志代碼就暴露出來(lái)了,一字之差,失之千里。

拋棄日志類(lèi)

假設(shè)我們使用了靜態(tài)常量代碼塊以及proguard優(yōu)化代碼的技術(shù);但是依然采用上面的日志類(lèi)的技術(shù),會(huì)發(fā)生什么呢?

public class LogUtil {
 private static final boolean DEBUG = false;

 public static void d(String tag, String msg) {
  if (DEBUG) android.util.Log.d(tag, msg);
 }
}

我寫(xiě)了一個(gè)demo,自己打包然后反編譯,得到這個(gè)日志類(lèi)如下(為了方便看,沒(méi)有混淆):

package com.example.test.app;

public class LogUtil {
 private static final boolean DEBUG;

 public LogUtil() {
  super();
 }

 public static void d(String tag, String msg) {
 }
}

我們看到,if代碼塊已經(jīng)沒(méi)有了,確實(shí)不會(huì)輸出任何日志;但是,我們看看調(diào)用這個(gè)類(lèi)的地方!


掩耳盜鈴的日志

這個(gè)LogUtil.d的調(diào)用,無(wú)異于掩耳盜鈴;雖然破解者沒(méi)辦法讓android.util.Log這個(gè)類(lèi)輸出任何日志,但是你這里的這個(gè)調(diào)用還是告訴了別人你在干什么;所以,要屏蔽日志的輸出,必須使用if代碼塊直接包含要被剔除的日志。上面的那個(gè)日志類(lèi),要被優(yōu)化掉,那就是:

if (DEBUG) {
 LogUtil.d(TAG, "msg");
}

這里,不是多此一舉嗎,寫(xiě)一個(gè)日志類(lèi)就是想不想重復(fù)地寫(xiě)if (DEBUG) ,這里為了使這一句隱藏,還是逃不掉;但是很抱歉,逃得了和尚逃不了廟,這種方法沒(méi)辦法做到完全隱藏信息;必須拋棄日志類(lèi)包裹日志代碼的做法!

解放雙手的補(bǔ)充

也許有人說(shuō),為了這個(gè)所謂的日志安全,每次輸出日志都的寫(xiě)一個(gè)if語(yǔ)句,那不麻煩死;簡(jiǎn)直反人類(lèi),我懶!實(shí)際上,要少寫(xiě)幾行代碼,我們可以選擇復(fù)用(代碼級(jí)別,比如上面的日志類(lèi)),也可以選擇生成(直接生成代碼);在支持元編程的語(yǔ)言里面,生成代碼是很常見(jiàn)的事情,比如C++的模版元編程以及ruby吹噓的DSL能力;這里沒(méi)有那么高大上,用代碼生成代碼,我們直接借助編輯器幫助我們少寫(xiě)幾行代碼萬(wàn)事。

IDEA/Android Studio

可以使用live template的功能;比如我的做法是,寫(xiě)一個(gè)ifd的template,每次我輸入ifd然后自動(dòng)展開(kāi)成if語(yǔ)句,光標(biāo)停在最中間:

使用live template簡(jiǎn)化輸入

vim/emacs

可以使用宏錄制的功能,實(shí)現(xiàn)上面的live template。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Android 跨進(jìn)程SharedPreferences異常詳解

    Android 跨進(jìn)程SharedPreferences異常詳解

    這篇文章主要介紹了Android 跨進(jìn)程SharedPreferences異常詳解的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Android項(xiàng)目開(kāi)發(fā)常用工具類(lèi)LightTaskUtils源碼介紹

    Android項(xiàng)目開(kāi)發(fā)常用工具類(lèi)LightTaskUtils源碼介紹

    LightTaskUtils是一個(gè)輕量級(jí)的線程管理工具,本文通過(guò)實(shí)例代碼給大家詳細(xì)介紹Android項(xiàng)目開(kāi)發(fā)常用工具類(lèi)LightTaskUtils的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2022-06-06
  • Android開(kāi)發(fā)手冊(cè)TextView控件及陰影效果實(shí)現(xiàn)

    Android開(kāi)發(fā)手冊(cè)TextView控件及陰影效果實(shí)現(xiàn)

    這篇文章主要為大家介紹了Android開(kāi)發(fā)手冊(cè)TextView控件及陰影效果的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Android使用GridLayout繪制自定義日歷控件

    Android使用GridLayout繪制自定義日歷控件

    這篇文章主要為大家詳細(xì)介紹了Android使用GridLayout繪制自定義日歷控件的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Android開(kāi)發(fā)中數(shù)據(jù)庫(kù)升級(jí)且表添加新列的方法

    Android開(kāi)發(fā)中數(shù)據(jù)庫(kù)升級(jí)且表添加新列的方法

    這篇文章主要介紹了Android開(kāi)發(fā)中數(shù)據(jù)庫(kù)升級(jí)且表添加新列的方法,結(jié)合具體實(shí)例形式分析了Android數(shù)據(jù)庫(kù)升級(jí)開(kāi)發(fā)過(guò)程中常見(jiàn)問(wèn)題與相關(guān)操作技巧,需要的朋友可以參考下
    2017-09-09
  • Android自定義View過(guò)程解析

    Android自定義View過(guò)程解析

    這篇文章主要針對(duì)Android自定義View過(guò)程進(jìn)行解析,Android創(chuàng)建自定義的view,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android編程之軟件的安裝和卸載方法

    Android編程之軟件的安裝和卸載方法

    這篇文章主要介紹了Android編程之軟件的安裝和卸載方法,涉及Android編程實(shí)現(xiàn)軟件的安裝、權(quán)限修改及卸載的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-12-12
  • Android編程獲取手機(jī)屏幕分辨率大小的方法

    Android編程獲取手機(jī)屏幕分辨率大小的方法

    這篇文章主要介紹了Android編程獲取手機(jī)屏幕分辨率大小的方法,涉及Android基于getWindowManager()方法獲取手機(jī)屏幕相關(guān)信息的使用技巧,需要的朋友可以參考下
    2016-02-02
  • Android Glide常見(jiàn)使用方式講解

    Android Glide常見(jiàn)使用方式講解

    對(duì)于Glide這個(gè)加載圖片的框架,很多人都在用,我之前使用的是ImageLoader,最近查資料時(shí),發(fā)現(xiàn)Glide才是Google推薦的加載圖片框架,功能非常強(qiáng)大,而且還有Google專(zhuān)人維護(hù),要知道,ImageLoader已經(jīng)沒(méi)人維護(hù)了,除了問(wèn)題可沒(méi)人解答。所以有必要整理一下Glide的使用
    2023-01-01
  • android檢測(cè)網(wǎng)絡(luò)連接狀態(tài)示例講解

    android檢測(cè)網(wǎng)絡(luò)連接狀態(tài)示例講解

    網(wǎng)絡(luò)的時(shí)候,并不是每次都能連接到網(wǎng)絡(luò),因此在程序啟動(dòng)中需要對(duì)網(wǎng)絡(luò)的狀態(tài)進(jìn)行判斷,如果沒(méi)有網(wǎng)絡(luò)則提醒用戶(hù)進(jìn)行設(shè)置
    2014-02-02

最新評(píng)論