Android加固技術(shù)分析與多渠道打包實(shí)踐

摘要

由于android應(yīng)用程序使用java代碼開(kāi)發(fā)魔慷,java編譯生成的.dex文件或.class代碼反編譯之后很容易得到源代碼参咙。雖然已經(jīng)有混淆技術(shù)可以大大提高代碼反編譯之后的可讀性,但反編譯的源碼還是暴露無(wú)遺,所以出現(xiàn)了許多android加固方案,本文分析一種android加固方案和多渠道打包的整合。

class文件和DEX文件

  • Android Java代碼編譯之后產(chǎn)生.class文件评凝,然后dex工具會(huì)把 .class文件處理成 .dex文件,再把資源文件和.dex文件等打包成 .apk文件 (apk就是android package的意思)腺律。
  • .class文件存在很多的冗余信息奕短,dex工具會(huì)去除冗余信息,并把所有的.class文件整合到.dex文件中匀钧。減少了I/O操作翎碑,提高了類的查找速度。
  • Android使用dvm(Dalvik Virtual Machine)作為虛擬機(jī)之斯,dvm執(zhí)行的是.dex格式文件日杈。jvm執(zhí)行的是.class文件。
  • dvm是基于寄存器的虛擬機(jī)佑刷,而jvm執(zhí)行是基于虛擬棧的虛擬機(jī)莉擒。寄存器存取速度比棧快的多瘫絮,dvm可以根據(jù)硬件實(shí)現(xiàn)最大的優(yōu)化涨冀,比較適合移動(dòng)設(shè)備。

DEX文件結(jié)構(gòu)

dex文件結(jié)構(gòu)如下:
image

如圖麦萤,整個(gè)dex文件分為三個(gè)模塊

  • 文件頭鹿鳖,文件頭記錄了dex文件的一些基本信息, 以及大致的數(shù)據(jù)分布. dex文件頭部總長(zhǎng)度是固定的0x70
  • 索引區(qū),索引區(qū)中索引了整個(gè)dex中的字符串壮莹、類型翅帜、方法聲明、字段以及方法的信息, 其結(jié)構(gòu)體的開(kāi)始位置和個(gè)數(shù)均來(lái)自dex文件頭中的記錄(或通過(guò)map_list也可以索引到記錄)
    1. 字符串索引區(qū)垛孔,描述dex文件中所有的字符串信息
    2. 類型索引區(qū)藕甩, 描述dex文件中所有的類型, 如類類型、基本類型周荐、返回值類型等
    3. 方法聲明索引區(qū)狭莱, 描述dex文件中所有的方法聲明
  • 數(shù)據(jù)區(qū)
dex文件頭結(jié)構(gòu)
字段名稱 偏移值 長(zhǎng)度 描述
magic 0x0 0x8 dex魔數(shù)字,文件類型的標(biāo)識(shí)概作。 固定信息: dex\n035腋妙,035是結(jié)構(gòu)的版本
checksum 0x8 0x4 去除了magic和checksum字段之外的所有內(nèi)容的校驗(yàn)碼,(alder32算法)
signature 0xc 0x14 SHA-1簽名, 去除了magic讯榕、checksum和signature字段之外的所有內(nèi)容的簽名
fileSize 0x20 0x4 整個(gè)dex的文件大小
headerSize 0x24 0x4 整個(gè)dex文件頭的大小 (固定大小為0x70)
... ... ... ...

上面了解了dex文件和dex文件的文件頭骤素,接下來(lái)進(jìn)入主題,看一下本文所要介紹的apk的加固過(guò)程:

APK加固過(guò)程總圖解

apk加固過(guò)程解析.png
以上過(guò)程大致可總結(jié)為:

需要準(zhǔn)備的項(xiàng)目有兩個(gè):

  • 需要加固的Android項(xiàng)目愚屁,即需要保護(hù)的源代碼济竹。
  • 解密的項(xiàng)目,即負(fù)責(zé)解密出原apk并使用代理去啟動(dòng)原apk的Android項(xiàng)目霎槐;還包括包含加固工具的java工具類和簽名的java工具類(以Library形式包含在此項(xiàng)目中送浊,不參打包),負(fù)責(zé)加密和合并apk丘跌,并寫(xiě)入渠道信息袭景。

加固工具的加密過(guò)程:

  • 將原項(xiàng)目和解密的項(xiàng)目分別編譯,取原項(xiàng)目生成的apk文件和解密項(xiàng)目生成的dex文件闭树,讀入字節(jié)流耸棒;
  • 使用自己定義的對(duì)稱加密算法加密原apk文件的數(shù)組流,生成新的數(shù)組报辱;
  • 將原apk文件流和解密項(xiàng)目dex的文件流進(jìn)行拼接与殃,生成一個(gè)新的dex文件;
  • 修改新的dex文件的文件頭碍现,使得新的拼接而成的dex文件格式合法幅疼;

代碼:

    public static String forceApk() throws Exception {
        File payloadSrcFile = new File(payloadSrcFilePath); // 需要加殼的程序
        System.out.println("input apk size:" + payloadSrcFile.length());
        File unShellDexFile = new File(unShellDexFilePath); // 解客dex
        byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));// 以二進(jìn)制形式讀出apk,并進(jìn)行加密處理//對(duì)源Apk進(jìn)行加密操作
        byte[] unShellDexArray = readFileBytes(unShellDexFile);// 以二進(jìn)制形式讀出dex
        int payloadLen = payloadArray.length;
        int unShellDexLen = unShellDexArray.length;
        int totalLen = payloadLen + unShellDexLen + 4;// 多出4字節(jié)是存放長(zhǎng)度的鸵赫。
        byte[] newdex = new byte[totalLen]; // 申請(qǐng)了新的長(zhǎng)度
        // 添加解殼代碼
        System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);// 先拷貝dex內(nèi)容
        // 添加加密后的解殼數(shù)據(jù)
        System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);// 再在dex內(nèi)容后面拷貝apk的內(nèi)容
        // 添加解殼數(shù)據(jù)長(zhǎng)度
        System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen - 4, 4);// 最后4為長(zhǎng)度
        // 修改DEX file size文件頭
        fixFileSizeHeader(newdex);
        // 修改DEX SHA1 文件頭
        fixSHA1Header(newdex);
        // 修改DEX CheckSum文件頭
        fixCheckSumHeader(newdex);
        File file = new File(outputDexFileName);
        if (file.delete()||!file.exists()) {
            file.createNewFile();
        }

        FileOutputStream localFileOutputStream = new FileOutputStream(
                outputDexFileName);
        localFileOutputStream.write(newdex);
        localFileOutputStream.flush();
        localFileOutputStream.close();
        return replaceDex(outputDexFileName);
    }

    private static byte[] encrpt(byte[] srcdata) {
        for (int i = 0; i < srcdata.length; i++) {
            srcdata[i] = (byte) (0xFF ^ srcdata[i]);
        }
        return srcdata;
    }

dex文件需要修改的內(nèi)容:

  • 從上面列出的dex文件頭各結(jié)構(gòu)的含義中可以得知衣屏,拼接dex文件之后需要改變dex文件頭的checksum、signature辩棒、fileSize字段以保證dex文件合法狼忱;
  • 新dex文件的大小fileSize,寫(xiě)在0x20 即新dex文件數(shù)組流的第32個(gè)元素一睁,占用占四位的長(zhǎng)度钻弄;
  • 新dex的SHA-1簽名signature,寫(xiě)在0xc處者吁,即0x20往前0x14個(gè)長(zhǎng)度窘俺,所以是12-31位置;
  • 新dex的校驗(yàn)碼checksum寫(xiě)在0x8處复凳,占四位(8-11)瘤泪;

代碼:

/**
* 修改dex頭 sha1值
*/
private static void fixCheckSumHeader(byte[] dexBytes) {
        Adler32 adler = new Adler32();
        adler.update(dexBytes, 12, dexBytes.length - 12);// 從12到文件末尾計(jì)算校驗(yàn)碼
        long value = adler.getValue();
        int va = (int) value;
        byte[] newcs = intToByte(va);
        // 高位在前灶泵,低位在后
        byte[] recs = new byte[4];
        for (int i = 0; i < 4; i++) {
            recs[i] = newcs[newcs.length - 1 - i];
            // System.out.println(Integer.toHexString(newcs[i]));
        }
        System.arraycopy(recs, 0, dexBytes, 8, 4);// 效驗(yàn)碼賦值(8-11)
    }

    /**
    * 修改dex頭 sha1值
    */
    private static void fixSHA1Header(byte[] dexBytes)
            throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(dexBytes, 32, dexBytes.length - 32);// 從32位到結(jié)束計(jì)算sha--1
        byte[] newdt = md.digest();
        System.arraycopy(newdt, 0, dexBytes, 12, 20);// 修改sha-1值(12-31)
    }

    /**
    * 修改dex頭 file_size值
    */
    private static void fixFileSizeHeader(byte[] dexBytes) {
        // 新文件長(zhǎng)度
        byte[] newfs = intToByte(dexBytes.length);
        // System.out.println(Integer.toHexString(dexBytes.length));
        byte[] refs = new byte[4];
        // 高位在前,低位在后
        for (int i = 0; i < 4; i++) {
            refs[i] = newfs[newfs.length - 1 - i];
        }
        System.arraycopy(refs, 0, dexBytes, 32, 4);// 修改(32-35)
    }

以上步驟把原apk和殼文件寫(xiě)成了一個(gè)合法的dex文件对途。接下來(lái)需要簽名殼項(xiàng)目代碼生成合法的apk文件赦邻,用戶才能把它正常安裝到手機(jī)上去。
(所以需要了解apk的構(gòu)建流程实檀,了解apk打包過(guò)程的童鞋可跳過(guò))

APK構(gòu)建流程

APP的構(gòu)建流程涉及許多將項(xiàng)目轉(zhuǎn)換成 Android 應(yīng)用軟件包 (APK) 的工具和流程惶洲。構(gòu)建流程非常靈活,因此了解它的一些底層工作原理會(huì)對(duì)我們很有幫助膳犹。

典型 Android 應(yīng)用模塊的構(gòu)建流程如下:

1

如上圖恬吕,典型 Android 應(yīng)用模塊的構(gòu)建流程通常依循下列步驟:

  • 編譯器將源代碼轉(zhuǎn)換成 DEX(Dalvik Executable) 文件(其中包括運(yùn)行在 Android 設(shè)備上的字節(jié)碼),將所有其他內(nèi)容轉(zhuǎn)換成已編譯資源须床。
  • APK 打包器將 DEX 文件和已編譯資源合并成單個(gè) APK铐料。不過(guò),必須先將APK簽名侨颈,才能將應(yīng)用安裝并部署到 Android 設(shè)備上余赢。
  • APK 打包器使用調(diào)試或發(fā)布密鑰庫(kù)簽署您的 APK:
    1. 如果您構(gòu)建的是調(diào)試版本的應(yīng)用(即專用于測(cè)試和分析的應(yīng)用),打包器會(huì)使用調(diào)試密鑰庫(kù)簽署您的應(yīng)用哈垢。Android Studio 自動(dòng)使用調(diào)試密鑰庫(kù)配置新項(xiàng)目妻柒。
    2. 如果您構(gòu)建的是打算向外發(fā)布的發(fā)布版本應(yīng)用,打包器會(huì)使用發(fā)布密鑰庫(kù)簽署您的應(yīng)用耘分。要?jiǎng)?chuàng)建發(fā)布密鑰庫(kù)举塔,請(qǐng)閱讀在 Android Studio 中簽署您的應(yīng)用。
    3. 在生成最終 APK 之前求泰,打包器會(huì)使用 zipalign 工具對(duì)應(yīng)用進(jìn)行優(yōu)化央渣,減少其在設(shè)備上運(yùn)行時(shí)的內(nèi)存占用。
      構(gòu)建流程結(jié)束時(shí)渴频,將獲得可用來(lái)進(jìn)行部署芽丹、測(cè)試的調(diào)試 APK,或者可用來(lái)發(fā)布給外部用戶的release版本的APK卜朗。
回到加固過(guò)程拔第,把生成的dex文件替換到殼apk中去,需要先簽名

APK簽名

  • 這里使用jarsigner命令簽名:
    public static String sign(String apkPath) throws Exception {
        String nameFlag = apkPath.replace(".apk", "");
        String output = nameFlag + "_singed.apk";
        String shell = "jarsigner -verbose -digestalg SHA1 -sigalg MD5withRSA -keystore "
                + keystorePath + " -signedjar "  + output  + " " + apkPath + " "+ alias + " -storepass " + storepass + " -keypass " + keypass;
        System.out.println(shell);
        callShell(shell);
        return output;
    }

    public static void callShell(String shellString) throws Exception {
        Process process = Runtime.getRuntime().exec(shellString);
        int exitValue = process.waitFor();
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line).append("\n");
        }
        String result = sb.toString();
        System.out.println(result);
        if (0 != exitValue) {
            throw new Exception("call shell failed. error code is :"
                    + exitValue);
        }
    }

Android多渠道打包#

背景

Android應(yīng)用程序會(huì)發(fā)布到各個(gè)平臺(tái)的應(yīng)用市場(chǎng)上去场钉,以便不同品牌的手機(jī)可以方便的在自己的應(yīng)用市場(chǎng)內(nèi)下載到想要的apk蚊俺,但由于Android手機(jī)品牌和應(yīng)用市場(chǎng)非常多,一般大型的app會(huì)發(fā)布到幾十個(gè)甚至更多的應(yīng)用市場(chǎng)中去逛万。為了統(tǒng)計(jì)用戶來(lái)源泳猬,需要分別統(tǒng)計(jì)這些渠道的用戶量或其他屬性,因此需要給apk文件加入特殊標(biāo)識(shí) 以識(shí)別應(yīng)用來(lái)源。
如果按照傳統(tǒng)打打包方式得封,需要修改一次AndroidManifest.xml文件的渠道號(hào)重新打包一次埋心,往往幾十個(gè)包需要幾個(gè)小時(shí)甚至更久,效率及其的低下呛每。
為了解決這個(gè)問(wèn)題踩窖,業(yè)內(nèi)誕生的較早的多渠道快速打包方案有美團(tuán)多渠道打包方案坡氯。

美團(tuán)多渠道打包

美團(tuán)多渠道打包的思路是晨横,先打包并簽名一個(gè)沒(méi)有渠道標(biāo)識(shí)的apk文件,然后每打一個(gè)渠道包復(fù)制一個(gè)apk文件出來(lái)箫柳,這個(gè)apk文件的META-INF目錄中添加一個(gè)使用渠道號(hào)命名的空文件即可(v1.0簽名機(jī)制下 添加一個(gè)空文件不會(huì)影響apk文件的簽名)手形,apk安裝后代碼中讀取空文件文件名就可以得到渠道信息了。這種打包方式速度非趁趸校快库糠,900多個(gè)渠道不到一分鐘就能打完。

增加渠道標(biāo)識(shí)文件:

    public static boolean changeChannel(final String zipFilename,
                                        final String channel) {
        try (FileSystem zipfs = FileUtils.createZipFileSystem(zipFilename, false)) {
            final Path root = zipfs.getPath("/META-INF/");
            ChannelFileVisitor visitor = new ChannelFileVisitor();
            Files.walkFileTree(root, visitor);
            Path existChannel = visitor.getChannelFile();
            Path newChannel = zipfs.getPath(CHANNEL_PREFIX + channel);
            if (existChannel != null) {
                Files.move(existChannel, newChannel, StandardCopyOption.ATOMIC_MOVE);
            } else {
                Files.createFile(newChannel);
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

操作結(jié)果:

image

(后續(xù)在apk中讀取渠道信息的代碼就不展示了涮毫,有興趣的可以查看美團(tuán)多渠道打包方案

packer-ng打包

  • 原理
    packer-ng使用了另一種思路瞬欧。android使用的apk包的壓縮方式是zip,與zip有相同的文件結(jié)構(gòu)罢防,在zip的核心目錄Central directory file header中包含一個(gè)File comment區(qū)域泌绣,可以存放一些數(shù)據(jù)楷怒。File comment是zip文件如果可以正確的修改這個(gè)部分,就可以在不破壞壓縮包、不用重新打包的的前提下快速的給apk文件寫(xiě)入自己想要的數(shù)據(jù)甥角。
    Central directory file header 即zip核心目錄,記錄了壓縮文件的目錄信息腹躁,在這個(gè)數(shù)據(jù)區(qū)中每一條紀(jì)錄對(duì)應(yīng)在壓縮源文件數(shù)據(jù)區(qū)中的一條數(shù)據(jù)纱皆。
    End of central directory record(EOCD) 目錄結(jié)束標(biāo)識(shí)存在于整個(gè)歸檔包的結(jié)尾,用于標(biāo)記壓縮的目錄數(shù)據(jù)的結(jié)束绽昼。每個(gè)壓縮文件必須有且只有一個(gè)EOCD記錄唯鸭。

  • zip目錄結(jié)束標(biāo)識(shí)結(jié)構(gòu):

偏移值 長(zhǎng)度 描述 說(shuō)明
0 4 End of central directory signature = 0x06054b50 核心目錄結(jié)束標(biāo)記(0x06054b50)
4 2 Number of this disk 當(dāng)前磁盤編號(hào)
6 2 number of the disk with the start of the central directory 核心目錄開(kāi)始位置的磁盤編號(hào)
8 2 total number of entries in the central directory on this disk 該磁盤上所記錄的核心目錄數(shù)量
10 2 total number of entries in the central directory 核心目錄結(jié)構(gòu)總數(shù)
12 2 Size of central directory (bytes) 核心目錄的大小
16 4 offset of start of central directory with respect to the starting disk number 核心目錄開(kāi)始位置相對(duì)于archive開(kāi)始的位移
20 2 .ZIP file comment length(n) 注釋長(zhǎng)度
22 n .ZIP Comment 注釋內(nèi)容

目錄結(jié)束標(biāo)識(shí)區(qū)域包含zip comment 區(qū)域可以寫(xiě)入少量信息并不會(huì)印象apk簽名,所以可以將渠道數(shù)據(jù)直接寫(xiě)在這里硅确。

  • 長(zhǎng)度處理
    由于數(shù)據(jù)是不確定的目溉,我們無(wú)法知道comment的長(zhǎng)度,從表中可以看到zip定義comment的長(zhǎng)度的位置在comment之前疏魏,所以無(wú)法從zip中直接獲取comment的長(zhǎng)度停做。這里我們需要自定義comment的長(zhǎng)度,在自定義comment內(nèi)容的后面添加一個(gè)區(qū)域儲(chǔ)存comment的長(zhǎng)度大莫。

  • 將數(shù)據(jù)寫(xiě)入comment

public static void writeApk(File file, String comment) {
    ZipFile zipFile = null;
    ByteArrayOutputStream outputStream = null;
    RandomAccessFile accessFile = null;
    try {
        zipFile = new ZipFile(file);
        String zipComment = zipFile.getComment();
        if (zipComment != null) {
            return;
        }
        byte[] byteComment = comment.getBytes();
        outputStream = new ByteArrayOutputStream();
        outputStream.write(byteComment);
        outputStream.write(short2Stream((short) byteComment.length));
        byte[] data = outputStream.toByteArray();
        accessFile = new RandomAccessFile(file, "rw");
        accessFile.seek(file.length() - 2);
        accessFile.write(short2Stream((short) data.length));
        accessFile.write(data);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (zipFile != null) {
                zipFile.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (accessFile != null) {
                accessFile.close();
            }
        } catch (Exception e) {
        }
    }
}
  • 讀取渠道信息

獲取apk路徑蛉腌,找到comment開(kāi)始位置,找到我們自己寫(xiě)入的渠道信息的長(zhǎng)度。讀出寫(xiě)到comment中的信息烙丛。
(后續(xù)讀取渠道信息的代碼就不展示了舅巷,感興趣的童鞋可以去閱讀PackerNg源碼)

至此,從apk加固到簽名河咽、寫(xiě)入多渠道數(shù)據(jù)的整個(gè)流程就結(jié)束了

后續(xù)工作

  • 替換殼apk文件的icon圖標(biāo)為原apk的icon钠右,修改殼apk的配置文件中的包名;
  • 搭建加固工具的UI忘蟹,使其桌面化飒房。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市媚值,隨后出現(xiàn)的幾起案子狠毯,更是在濱河造成了極大的恐慌,老刑警劉巖褥芒,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚼松,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锰扶,警方通過(guò)查閱死者的電腦和手機(jī)献酗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)坷牛,“玉大人罕偎,你說(shuō)我怎么就攤上這事±焖В” “怎么了锨亏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)忙干。 經(jīng)常有香客問(wèn)我器予,道長(zhǎng),這世上最難降的妖魔是什么捐迫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任乾翔,我火速辦了婚禮,結(jié)果婚禮上施戴,老公的妹妹穿的比我還像新娘反浓。我一直安慰自己,他們只是感情好赞哗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布雷则。 她就那樣靜靜地躺著,像睡著了一般肪笋。 火紅的嫁衣襯著肌膚如雪月劈。 梳的紋絲不亂的頭發(fā)上度迂,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音猜揪,去河邊找鬼惭墓。 笑死,一個(gè)胖子當(dāng)著我的面吹牛而姐,可吹牛的內(nèi)容都是我干的腊凶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼拴念,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钧萍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起丈莺,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤划煮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缔俄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡器躏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年俐载,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片登失。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遏佣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揽浙,到底是詐尸還是另有隱情状婶,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布馅巷,位于F島的核電站膛虫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钓猬。R本人自食惡果不足惜稍刀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敞曹。 院中可真熱鬧账月,春花似錦、人聲如沸澳迫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)橄登。三九已至抓歼,卻和暖如春担平,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锭部。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工暂论, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拌禾。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓取胎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親湃窍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闻蛀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評(píng)論 25 707
  • 關(guān)于作者: 李濤,騰訊Android工程師您市,14年加入騰訊SNG增值產(chǎn)品部觉痛,期間主要負(fù)責(zé)手Q動(dòng)漫、企鵝電競(jìng)等項(xiàng)目的...
    稻草人_3e17閱讀 3,619評(píng)論 0 10
  • Android市場(chǎng)的渠道分散已不是什么新鮮事茵休,但如何高效打包仍是令許多開(kāi)發(fā)者頭疼的問(wèn)題薪棒。本篇文章著重介紹了目前最新...
    _曾胖子閱讀 1,925評(píng)論 1 10
  • 目錄一、Python打包及優(yōu)化(美團(tuán)多渠道打包)二榕莺、Gradle打包三俐芯、其他打包方案:修改Zip文件的commen...
    守望君閱讀 5,694評(píng)論 4 17
  • 我近期的目標(biāo)是提高兒子的學(xué)習(xí)熱情和書(shū)寫(xiě)的認(rèn)真,提升學(xué)習(xí)成績(jī)钉鸯,為小升初打下堅(jiān)實(shí)的基礎(chǔ)吧史,順利升入理想的初中。 一唠雕、繼續(xù)...
    歸韻閱讀 275評(píng)論 0 0