如何安全地打印日志

如何打印日志管怠?這不是很簡(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)即可扬卷,大致思路如下:

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)似日志輸出:


image

我們打開(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è)圖,感受下:


image

讓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)該如下:

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

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)常量宾濒;如果是常量的話(huà)腿短,那么就是永遠(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)了秉继,一字之差祈噪,失之千里。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尚辑,一起剝皮案震驚了整個(gè)濱河市辑鲤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杠茬,老刑警劉巖月褥,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弛随,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宁赤,警方通過(guò)查閱死者的電腦和手機(jī)舀透,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)决左,“玉大人愕够,你說(shuō)我怎么就攤上這事》鹈停” “怎么了惑芭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)继找。 經(jīng)常有香客問(wèn)我遂跟,道長(zhǎng),這世上最難降的妖魔是什么婴渡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任幻锁,我火速辦了婚禮,結(jié)果婚禮上边臼,老公的妹妹穿的比我還像新娘越败。我一直安慰自己,他們只是感情好硼瓣,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布究飞。 她就那樣靜靜地躺著,像睡著了一般堂鲤。 火紅的嫁衣襯著肌膚如雪亿傅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天瘟栖,我揣著相機(jī)與錄音葵擎,去河邊找鬼。 笑死半哟,一個(gè)胖子當(dāng)著我的面吹牛酬滤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寓涨,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盯串,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了戒良?” 一聲冷哼從身側(cè)響起体捏,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后几缭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體河泳,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年年栓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拆挥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡某抓,死狀恐怖纸兔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搪缨,我是刑警寧澤食拜,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布鸵熟,位于F島的核電站副编,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏流强。R本人自食惡果不足惜痹届,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望打月。 院中可真熱鬧队腐,春花似錦、人聲如沸奏篙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)秘通。三九已至为严,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肺稀,已是汗流浹背第股。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留话原,地道東北人夕吻。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像繁仁,于是被迫代替她去往敵國(guó)和親涉馅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說(shuō)明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí)黄虱,會(huì)觸發(fā)此異常控漠。 O...
    我想起個(gè)好名字閱讀 5,333評(píng)論 0 9
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,109評(píng)論 1 32
  • “手心手背都是肉,但是手掌肉比手背厚”,我笑著對(duì)媽媽說(shuō)盐捷。曾經(jīng)看過(guò)這樣一句話(huà)偶翅,所有的玩笑都是不敢言語(yǔ)的真話(huà)。其實(shí)我看...
    磚縫的小草閱讀 6,718評(píng)論 3 23
  • 坐車(chē)回單位的路上,快到站的時(shí)候從窗口看到隔壁小黑狗順著環(huán)山路往下溜達(dá)滞诺,我忽然覺(jué)得小黑是真的不會(huì)回來(lái)了形导。之前它一直留...
    靈湮涼閱讀 298評(píng)論 0 0
  • 前言 之前在平臺(tái)發(fā)布過(guò)一篇關(guān)于AS導(dǎo)入二次開(kāi)發(fā)系統(tǒng)包的文章,得到很多開(kāi)發(fā)者的回饋和討論习霹,有興趣的可以回看AS中導(dǎo)入...
    詭異的葉子閱讀 1,205評(píng)論 0 0