最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

當談到如何打印日志?很多人會想這不是很簡單,直接使用android.util.Log這個類不就行了?然而,日志屬于非常敏感的信息;逆向工程師在逆向你的程序的時候,本來需要捕捉你程序的各種輸出,然后進行推測,順藤摸瓜然后得到需要的信息;一旦你的日志泄漏,無異于門戶洞開,破解你的程序如入無人之境。

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

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

日志開關(guān)+日志類

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

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

 // 其他debug方法
}

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

日志輸出圖片

我們打開這個叫做x的類,雖然被混淆過了,但是意思很明白,跟我們上面的思路一樣:

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);
  }
 }
}

這是一個真實的例子,而且這個app的用戶還不少;接下來我們看看這種方式有什么問題。

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

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

我們使用apktool反編譯得到這個apk的smali代碼;然后上面的反編譯告訴我們,這個日志類的位置是:com.lionmobi.util.x我們打開這個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

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


泄漏的日志信息

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

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

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

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

if (true) {
 // statement;
}

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

正確的做法

最終,我們所有打印日志的語句應(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)化代碼即可。

看起來簡單,好像也與最初的“日志開關(guān)”沒有什么區(qū)別,仔細分析一下:

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

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

拋棄日志類

假設(shè)我們使用了靜態(tài)常量代碼塊以及proguard優(yōu)化代碼的技術(shù);但是依然采用上面的日志類的技術(shù),會發(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);
 }
}

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

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)沒有了,確實不會輸出任何日志;但是,我們看看調(diào)用這個類的地方!


掩耳盜鈴的日志

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

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

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

解放雙手的補充

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

IDEA/Android Studio

可以使用live template的功能;比如我的做法是,寫一個ifd的template,每次我輸入ifd然后自動展開成if語句,光標停在最中間:

使用live template簡化輸入

vim/emacs

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

總結(jié)

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

相關(guān)文章

  • Android中Activity生命周期和啟動模式詳解

    Android中Activity生命周期和啟動模式詳解

    這篇文章主要介紹了Activity生命周期和啟動模式詳解的相關(guān)資料,需要的朋友可以參考下
    2016-07-07
  • 詳解Android安全防護之加密算法

    詳解Android安全防護之加密算法

    Android應(yīng)用對安全防范這方面要求越來越高了。特別是金融行業(yè),如果app沒有沒有做好安全處理,那些很容易被一些Hacker(黑客)所攻擊。并不是說做了這些安全防范,這個應(yīng)用就百分之百的安全的。只是說能夠盡可能加大破解難度。本文將詳細介紹Android安全防護之加密算法。
    2021-06-06
  • Android 獲取手機信息實例詳解

    Android 獲取手機信息實例詳解

    這篇文章主要介紹了Android 獲取手機信息實例詳解的相關(guān)資料,這里附有實例代碼及實現(xiàn)效果圖,需要的朋友可以參考下
    2017-01-01
  • Android如何自定義修改打包apk名稱

    Android如何自定義修改打包apk名稱

    當我們進行apk打包時,默認會生成app-debug.apk或者app-release.apk,每次都需要手動去修改apk的名稱,用于區(qū)分各個版本的名稱,這篇文章主要介紹了Android自定義修改打包apk名稱,需要的朋友可以參考下
    2024-03-03
  • Android編程中關(guān)于單線程模型的理解與分析

    Android編程中關(guān)于單線程模型的理解與分析

    這篇文章主要介紹了Android編程中關(guān)于單線程模型的理解與分析,較為詳細的分析了Android編程中關(guān)于單線程的原理與相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-10-10
  • Android 自定義Dialog去除title導(dǎo)航欄的解決方法

    Android 自定義Dialog去除title導(dǎo)航欄的解決方法

    今天小編就為大家分享一篇Android 自定義Dialog去除title導(dǎo)航欄的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • 深入理解Android之接口回調(diào)機制

    深入理解Android之接口回調(diào)機制

    本篇文章主要介紹了Android之接口回調(diào)機制,在開發(fā)中經(jīng)常會用到,具有一定的學(xué)習(xí)價值,有需要的可以來了解一下。
    2016-10-10
  • Android中ProgressDialog的dismiss()與cancel()方法的區(qū)別

    Android中ProgressDialog的dismiss()與cancel()方法的區(qū)別

    本文主要介紹了Android中ProgressDialog的dismiss()與cancel()方法的區(qū)別,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-04-04
  • Android 通過Intent使用Bundle傳遞對象詳細介紹

    Android 通過Intent使用Bundle傳遞對象詳細介紹

    這篇文章主要介紹了Android 通過Intent使用Bundle傳遞對象詳細介紹的相關(guān)資料,并附實例代碼講解,具有一定的參考價值,需要的朋友可以參考下
    2016-11-11
  • Android10.0實現(xiàn)本地音樂播放(附源碼下載)

    Android10.0實現(xiàn)本地音樂播放(附源碼下載)

    這篇文章主要介紹了Android10.0實現(xiàn)本地音樂播放(附源碼下載),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06

最新評論

高淳县| 南召县| 衡水市| 茌平县| 隆尧县| 集贤县| 堆龙德庆县| 南木林县| 抚松县| 鄢陵县| 临沂市| 无棣县| 三亚市| 从江县| 紫金县| 漳州市| 五家渠市| 湄潭县| 文昌市| 博乐市| 同仁县| 大埔县| 抚松县| 全州县| 宁夏| 滨海县| 乐东| 辽中县| 布拖县| 庆阳市| SHOW| 临漳县| 三河市| 山阴县| 永靖县| 隆林| 惠来县| 乌鲁木齐市| 洞头县| 富蕴县| 伊春市|