<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>cp.codeplay</title>
        <link>https://paragraph.com/@cp-codeplay</link>
        <description>Software Engineer, Machine Learning</description>
        <lastBuildDate>Sat, 05 Sep 2026 07:05:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>cp.codeplay</title>
            <url>https://storage.googleapis.com/papyrus_images/21422bcb78276c7a3b7d7c12461cbe2253bb9b17ca31d097297da3e0b610f747.png</url>
            <link>https://paragraph.com/@cp-codeplay</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[iOS逆向工程-0x02－Hacking on B612]]></title>
            <link>https://paragraph.com/@cp-codeplay/ios-0x02-hacking-on-b612</link>
            <guid>GDFKil14uuJQRNqhVVy1</guid>
            <pubDate>Sat, 26 Mar 2022 12:03:22 GMT</pubDate>
            <description><![CDATA[前面两篇iOS逆向的文章（iOS逆向工程-0x00－用途以及准备工作,iOS逆向工程-0x01－工具篇-Cycript）主要是介绍iOS逆向的相关基础环境的搭建，工具的使用。有了这些知识之后，我们可以进行简单的逆向工作了。 B612是Line公司出品的一款非常棒的拍照软件。它的界面，交互，用起来非常顺手。app中的滤镜效果很赞，用户简单几步操作就可以生成一张很有质感的照片。本文我将会演示如何通过逆向来得知B612单个的滤镜的具体实现。 从AppStore上面下载B612的IPA文件，把文件的后缀名从 .IPA 修改成 .zip。解压zip文件之后，在Payload中有一个b612.app的文件，这里我们把.app的后缀去掉，让它变成一个文件夹，方便后面查阅。经过以上几步操作我们就拿到了 B612 app的 bundle 文件信息。 在使用B612的过程中，app会展列一个滤镜列表给用户进行选择（如下图）：B612_filter_list使用的过程发现一个叫 China 的滤镜，China，中国？还是瓷器？叫China的滤镜会呈现出什么样子的效果，根据效果我们是否能推测出这里的Ch...]]></description>
            <content:encoded><![CDATA[<p>前面两篇iOS逆向的文章（<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://cp0000.github.io/2015/11/21/iOS-reverse-engineering-01/">iOS逆向工程-0x00－用途以及准备工作</a>,<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://cp0000.github.io/2015/11/30/iOS-reverse-engineering-02/">iOS逆向工程-0x01－工具篇-Cycript</a>）主要是介绍iOS逆向的相关基础环境的搭建，工具的使用。有了这些知识之后，我们可以进行简单的逆向工作了。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://itunes.apple.com/us/app/b612-selfie-from-the-heart/id904209370?mt=8">B612</a>是Line公司出品的一款非常棒的拍照软件。它的界面，交互，用起来非常顺手。app中的滤镜效果很赞，用户简单几步操作就可以生成一张很有质感的照片。本文我将会演示如何通过逆向来得知B612单个的滤镜的具体实现。</p><p>从AppStore上面下载B612的IPA文件，把文件的后缀名从 .IPA 修改成 .zip。解压zip文件之后，在Payload中有一个b612.app的文件，这里我们把.app的后缀去掉，让它变成一个文件夹，方便后面查阅。经过以上几步操作我们就拿到了 B612 app的 bundle 文件信息。</p><p>在使用B612的过程中，app会展列一个滤镜列表给用户进行选择（如下图）：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cf1b7b5c518b9e86b75630bcd0fb4981379677b65e611abdb2dace83fe399165.jpg" alt="B612_filter_list" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">B612_filter_list</figcaption></figure><p>使用的过程发现一个叫 China 的滤镜，China，中国？还是瓷器？叫China的滤镜会呈现出什么样子的效果，根据效果我们是否能推测出这里的China是要翻译成中国，还是瓷器咧？很有意思的样子。我们就拿这个滤镜当做目标吧。</p><p>一般情况下，我的常用做法是在先前解压的文件夹中搜索一下相关的信息，碰碰运气。</p><pre data-type="codeBlock" text="find . -name &quot;*china*&quot;                 
./FilterThumb.bundle/filterthumb_china.jpg
./ObfuscateImages.bundle/lut/china.dat
"><code>find . -name <span class="hljs-string">"*china*"</span>                 
./FilterThumb.bundle/filterthumb_china.jpg
./ObfuscateImages.bundle/lut<span class="hljs-operator">/</span>china.dat
</code></pre><p>运气不错。在ObfuscateImages.bundle发现了一个叫china.dat的文件。不过尝试用各种编辑器都没法打开，使用修改后缀名等之类的方法也没能成功打开。看来要获取这个文件的内容没有那么简单，开发者对这个文件做了相关加密工作。要想解密该文件，我们可能需要到汇编代码中寻找答案了。</p><blockquote><p>这里考虑到程序在使用这个滤镜的时候，会加载该文件，并进行相关解密工作。所以下一步是要找到解密dat文件的地方，不过在此之前，我们还有一些工作要做。</p></blockquote><h2 id="h-clutch" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">解密可执行文件 - clutch 演示</h2><p>接下来我们要去可执行文件中查询相关信息了。但是从苹果商店上面下载下来的IPA里面的可执行文件是被苹果加密过的,我们解谜它。我常用的工具是 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/KJCracks/Clutch">clutch</a>,你可以clone一份repo到本地，然后编译得到一个clutch的程序，也可以直接下载release版本。然后把Clutch scp 到你的越狱机器上面就可以使用了。</p><ul><li><p>首先我们查找一下b612的bundle</p><p>iPhone:/usr root# ./Clutch -i|grep b61228)</p></li><li><p>然后进行dump工作</p><p>iPhone:/usr root# ./Clutch -d com.linecorp.b612….//程序输出相关log….DONE: /private/var/mobile/Documents/Dumped/com.linecorp.b612-iOS7.0-(Clutch-2.0 RC2).ipa</p></li></ul><p>最后会生成一个解密了的ipa文件，我们把它scp到电脑上来，进行分析。</p><blockquote><p>还有另外一个解密苹果商店加密IPA的工具叫<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/conradev/dumpdecrypted">dumpdecrypted</a>。由于在使用Clutch解密app的时候，消耗内存比较大，导致运行时会出现解密失败的情况，这个时候可以尝试使用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/conradev/dumpdecrypted">dumpdecrypted</a>。</p></blockquote><h2 id="h-class-dump-ida" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">分析可执行文件 - class-dump 以及 IDA</h2><p>要分析解密之后的文件，我们可以使用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/nygard/class-dump">class-dump</a>来导出程序的头文件。有了头文件列表之后，我们可以利用它来了解app的架构，使用的类库，以及探索需要逆向的那一块功能所属的类名。</p><pre data-type="codeBlock" text="class-dump-z/mac_x86/class-dump-z b612 -H -o headers
cd headers
total 10728
drwxr-xr-x  1295 cp  wheel  44030 Apr  4 12:46 .
drwxr-xr-x   455 cp  wheel  15470 Apr  4 12:46 ..
-rw-r--r--     1 cp  wheel    785 Apr  4 12:46 ACTApplication.h
-rw-r--r--     1 cp  wheel    370 Apr  4 12:46 ACTAutomatedUsageReporter.h
-rw-r--r--     1 cp  wheel    391 Apr  4 12:46 ACTAutomatedUsageReporterPrivate.h
-rw-r--r--     1 cp  wheel    264 Apr  4 12:46 ACTAutomatedUsageReporting.h
-rw-r--r--     1 cp  wheel   1830 Apr  4 12:46 ACTAutomatedUsageTracker.h
"><code>class<span class="hljs-operator">-</span>dump<span class="hljs-operator">-</span>z<span class="hljs-operator">/</span>mac_x86<span class="hljs-operator">/</span>class<span class="hljs-operator">-</span>dump<span class="hljs-operator">-</span>z b612 <span class="hljs-operator">-</span>H <span class="hljs-operator">-</span>o headers
cd headers
total <span class="hljs-number">10728</span>
drwxr<span class="hljs-operator">-</span>xr<span class="hljs-operator">-</span>x  <span class="hljs-number">1295</span> cp  wheel  <span class="hljs-number">44030</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> .
drwxr-xr<span class="hljs-operator">-</span>x   <span class="hljs-number">455</span> cp  wheel  <span class="hljs-number">15470</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> ..
<span class="hljs-operator">-</span>rw<span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>     <span class="hljs-number">1</span> cp  wheel    <span class="hljs-number">785</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> ACTApplication.h
<span class="hljs-operator">-</span>rw<span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>     <span class="hljs-number">1</span> cp  wheel    <span class="hljs-number">370</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> ACTAutomatedUsageReporter.h
<span class="hljs-operator">-</span>rw<span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>     <span class="hljs-number">1</span> cp  wheel    <span class="hljs-number">391</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> ACTAutomatedUsageReporterPrivate.h
<span class="hljs-operator">-</span>rw<span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>     <span class="hljs-number">1</span> cp  wheel    <span class="hljs-number">264</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> ACTAutomatedUsageReporting.h
<span class="hljs-operator">-</span>rw<span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>r<span class="hljs-operator">-</span><span class="hljs-operator">-</span>     <span class="hljs-number">1</span> cp  wheel   <span class="hljs-number">1830</span> Apr  <span class="hljs-number">4</span> <span class="hljs-number">12</span>:<span class="hljs-number">46</span> ACTAutomatedUsageTracker.h
</code></pre><p>要找到加密 .dat 文件的相关代码，我们需要使用另外一个非常强大的工具<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.hex-rays.com/products/ida/index.shtml">IDA</a>。IDA可以非常整洁的输出可执行文件的汇编代码，并且相互连接起来。不过这个软件非常之贵，但是它的免费版本已经够用了。</p><p>把B612的可执行文件加载到IDA之后，我们在IDA中执行一个搜索，关键字是dat;幸运的是，搜索结果指向了一下的代码。虽然是汇编代码，但是不难看出两点信息，（1）dat后缀的文件是一张Image （2）程序中需要把文件的数据字节进行反向操作，达到混淆的目的。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7a44de96c085f99cda1ca0ddf44d669e878ca61d6c262976111eceadf1862c24.jpg" alt="reversebyte" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">reversebyte</figcaption></figure><p>接下来验证一下线索结果：</p><pre data-type="codeBlock" text="- (void)viewDidLoad {
    [super viewDidLoad];
    NSData * data = [[NSData alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@&quot;china&quot; ofType:@&quot;dat&quot;]];
    data = [self reverseData: data];
    UIImage * image  = [UIImage imageWithData: data];
    UIImageView * imageView = [[UIImageView alloc] initWithImage: image];
    imageView.frame = self.view.bounds;
    [self.view addSubview: imageView];
}
- (NSData *)reverseData:(NSData *)data {
    const char *bytes = [data bytes];
    int idx = [data length] - 1;
    char *reversedBytes = calloc(sizeof(char),[data length]);
    for (int i = 0; i &lt; [data length]; i++) {
        reversedBytes[idx--] = bytes[i];
    }
    NSData *reversedData = [NSData dataWithBytes:reversedBytes length:[data length]];
    free(reversedBytes);
    return reversedData;
}
"><code><span class="hljs-operator">-</span> (void)viewDidLoad {
    [<span class="hljs-built_in">super</span> viewDidLoad];
    NSData <span class="hljs-operator">*</span> data <span class="hljs-operator">=</span> [[NSData alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@<span class="hljs-string">"china"</span> ofType:@<span class="hljs-string">"dat"</span>]];
    data <span class="hljs-operator">=</span> [<span class="hljs-built_in">self</span> reverseData: data];
    UIImage <span class="hljs-operator">*</span> image  <span class="hljs-operator">=</span> [UIImage imageWithData: data];
    UIImageView <span class="hljs-operator">*</span> imageView <span class="hljs-operator">=</span> [[UIImageView alloc] initWithImage: image];
    imageView.frame <span class="hljs-operator">=</span> <span class="hljs-built_in">self</span>.view.bounds;
    [<span class="hljs-built_in">self</span>.view addSubview: imageView];
}
<span class="hljs-operator">-</span> (NSData <span class="hljs-operator">*</span>)reverseData:(NSData <span class="hljs-operator">*</span>)data {
    const char <span class="hljs-operator">*</span><span class="hljs-keyword">bytes</span> <span class="hljs-operator">=</span> [data <span class="hljs-keyword">bytes</span>];
    <span class="hljs-keyword">int</span> idx <span class="hljs-operator">=</span> [data length] <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
    char <span class="hljs-operator">*</span>reversedBytes <span class="hljs-operator">=</span> calloc(sizeof(char),[data length]);
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> [data length]; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        reversedBytes[idx<span class="hljs-operator">-</span><span class="hljs-operator">-</span>] <span class="hljs-operator">=</span> <span class="hljs-keyword">bytes</span>[i];
    }
    NSData <span class="hljs-operator">*</span>reversedData <span class="hljs-operator">=</span> [NSData dataWithBytes:reversedBytes length:[data length]];
    free(reversedBytes);
    <span class="hljs-keyword">return</span> reversedData;
}
</code></pre><p>上述代码让我们获得一张china的图片：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c58f9b4a7c08707634d6fdefadf9cfe560d7cb74c1b949e20b014bc2e5eedaa8.png" alt="china" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">china</figcaption></figure><p>上述代码，请查阅<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/cp0000/HackB612">HackB612Image</a></p><p>OK，到此为止，我们解密了china.dat 这种类型的文件，它跟我们最终要挖掘的china滤镜是如何实现的之间肯定有着联系，但我们现在还不得知。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">关于滤镜</h2><p>在iOS app 中给图片加滤镜是要通过OpenGL ES 去实现的，而现在app中普遍使用的是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.khronos.org/opengles/2_X/">OpenGL ES 2.0</a>。这种情况下，滤镜一般都是通过OpenGL的shader来实现的。此时，前面dump出来的header就有用了，分析headers之后，我们发现了两个相关类 GPUImageFilter以及GLProgram。又由于shader在使用之前一定要进行link，compile操作。所以我们可以通过尝试hook这两个类的相关函数，来获得滤镜的shader。</p><blockquote><p>根据头文件我们能发现B612是通过使用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/BradLarson/GPUImage">GPUImage</a>框架来实现滤镜功能的。</p></blockquote><h2 id="h-theos" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Theos</h2><p>关于如何hook app 在运行时的函数，我们需要使用工具<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://iphonedevwiki.net/index.php/Theos">Theos</a>.</p><ul><li><p>关于如何安装Theos，请阅读<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://iphonedevwiki.net/index.php/Theos/Setup">Theos Setup</a>。</p></li><li><p>Logos相关语法，请参考<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://iphonedevwiki.net/index.php/Logos">Theos Logos</a></p></li></ul><p>安装好Theos之后，我们为B612创建一个tweak;</p><pre data-type="codeBlock" text="$THEOS/bin/nic.pl 
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
Choose a Template (required): 5
Project Name (required): bsix
Package Name [com.yourcompany.bsix]: com.cp.bsix
Author/Maintainer Name [cp]: 
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.linecorp.b612            
[iphone/tweak] List of applications to terminate upon installation (space-separated, &apos;-&apos; for none) [SpringBoard]: b612
Instantiating iphone/tweak in bsix/...
Done.
"><code>$THEOS<span class="hljs-operator">/</span>bin<span class="hljs-operator">/</span>nic.pl 
NIC <span class="hljs-number">2.0</span> <span class="hljs-operator">-</span> New Instance Creator
<span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">-</span>
  [<span class="hljs-number">1.</span>] iphone<span class="hljs-operator">/</span>application
  [<span class="hljs-number">2.</span>] iphone<span class="hljs-operator">/</span><span class="hljs-class"><span class="hljs-keyword">library</span>
  [3.] <span class="hljs-title">iphone</span>/<span class="hljs-title">preference_bundle</span>
  [4.] <span class="hljs-title">iphone</span>/<span class="hljs-title">tool</span>
  [5.] <span class="hljs-title">iphone</span>/<span class="hljs-title">tweak</span>
<span class="hljs-title">Choose</span> <span class="hljs-title">a</span> <span class="hljs-title">Template</span> (<span class="hljs-params">required</span>): 5
<span class="hljs-title">Project</span> <span class="hljs-title">Name</span> (<span class="hljs-params">required</span>): <span class="hljs-title">bsix</span>
<span class="hljs-title">Package</span> <span class="hljs-title">Name</span> [<span class="hljs-title">com</span>.<span class="hljs-title">yourcompany</span>.<span class="hljs-title">bsix</span>]: <span class="hljs-title">com</span>.<span class="hljs-title">cp</span>.<span class="hljs-title">bsix</span>
<span class="hljs-title">Author</span>/<span class="hljs-title">Maintainer</span> <span class="hljs-title">Name</span> [<span class="hljs-title">cp</span>]: 
[<span class="hljs-title">iphone</span>/<span class="hljs-title">tweak</span>] <span class="hljs-title">MobileSubstrate</span> <span class="hljs-title">Bundle</span> <span class="hljs-title">filter</span> [<span class="hljs-title">com</span>.<span class="hljs-title">apple</span>.<span class="hljs-title">springboard</span>]: <span class="hljs-title">com</span>.<span class="hljs-title">linecorp</span>.<span class="hljs-title">b612</span>            
[<span class="hljs-title">iphone</span>/<span class="hljs-title">tweak</span>] <span class="hljs-title">List</span> <span class="hljs-title">of</span> <span class="hljs-title">applications</span> <span class="hljs-title">to</span> <span class="hljs-title">terminate</span> <span class="hljs-title">upon</span> <span class="hljs-title">installation</span> (<span class="hljs-params">space-separated, <span class="hljs-string">'-'</span> <span class="hljs-keyword">for</span> none</span>) [<span class="hljs-title">SpringBoard</span>]: <span class="hljs-title">b612</span>
<span class="hljs-title">Instantiating</span> <span class="hljs-title">iphone</span>/<span class="hljs-title">tweak</span> <span class="hljs-title">in</span> <span class="hljs-title">bsix</span>/...
<span class="hljs-title">Done</span>.
</span></code></pre><p>然后在Tweak.xm文件中编辑需要hook的函数：</p><pre data-type="codeBlock" text="%hook GLProgram
-(id)initWithVertexShaderString:(id)vertexShaderString fragmentShaderString:(id)string
{
    [Logger log: [NSString stringWithFormat:@&quot;%@ %@&quot;, NSStringFromClass([self class]), NSStringFromSelector(_cmd)]];
    NSString * msg = [NSString stringWithFormat: @&quot;vertexShaderString: %@ fragmentShaderString: %@ &quot;, vertexShaderString, string];
    [Logger log: msg];
    return %orig;
}
%end
"><code><span class="hljs-operator">%</span>hook GLProgram
<span class="hljs-operator">-</span>(id)initWithVertexShaderString:(id)vertexShaderString fragmentShaderString:(id)<span class="hljs-keyword">string</span>
{
    [Logger log: [NSString stringWithFormat:@<span class="hljs-string">"%@ %@"</span>, NSStringFromClass([<span class="hljs-built_in">self</span> class]), NSStringFromSelector(_cmd)]];
    NSString <span class="hljs-operator">*</span> <span class="hljs-built_in">msg</span> <span class="hljs-operator">=</span> [NSString stringWithFormat: @<span class="hljs-string">"vertexShaderString: %@ fragmentShaderString: %@ "</span>, vertexShaderString, <span class="hljs-keyword">string</span>];
    [Logger log: <span class="hljs-built_in">msg</span>];
    <span class="hljs-keyword">return</span> <span class="hljs-operator">%</span>orig;
}
<span class="hljs-operator">%</span>end
</code></pre><p>B612 tweak 的项目地址<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/cp0000/B612Tweak">B612Tweak</a>.成功编译之后，把生成的.deb文件scp到越狱机器上，利用dpkg安装tweak；杀掉B612进程重启app之后，tweak就可以hook相关函数了。以下在点击China滤镜的时候，tweak打印出来的log信息：</p><pre data-type="codeBlock" text="GLProgram initWithVertexShaderString:fragmentShaderString:
vertexShaderString: 
attribute vec4 position; 
attribute vec4 inputTextureCoordinate; 
attribute vec4 inputTextureCoordinate2; 
varying vec2 textureCoordinate; 
varying vec2 textureCoordinate2; 
void main() { 
    gl_Position = position; 
    textureCoordinate = inputTextureCoordinate.xy; 
    textureCoordinate2 = inputTextureCoordinate2.xy; 
} 
fragmentShaderString: 
varying highp vec2 textureCoordinate; 
varying highp vec2 textureCoordinate2; 
uniform sampler2D inputImageTexture; 
uniform sampler2D inputImageTexture2; 
void main() { 
    highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate); 
    highp float blueColor = textureColor.b * 63.0; 
    highp vec2 quad1; 
    quad1.y = floor(floor(blueColor) / 8.0); 
    quad1.x = floor(blueColor) - (quad1.y * 8.0); 
    highp vec2 quad2; 
    quad2.y = floor(ceil(blueColor) / 8.0); 
    quad2.x = ceil(blueColor) - (quad2.y * 8.0); 
    highp vec2 texPos1; 
    texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); 
    texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); 
    highp vec2 texPos2; 
    texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r); 
    texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g); 
    lowp vec4 newColor1 = texture2D(inputImageTexture2, texPos1); 
    lowp vec4 newColor2 = texture2D(inputImageTexture2, texPos2); 
    lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor)); 
    gl_FragColor = vec4(newColor.rgb, textureColor.w); 
}
"><code>GLProgram initWithVertexShaderString:fragmentShaderString:
vertexShaderString: 
attribute vec4 position; 
attribute vec4 inputTextureCoordinate; 
attribute vec4 inputTextureCoordinate2; 
varying vec2 textureCoordinate; 
varying vec2 textureCoordinate2; 
void main() { 
    gl_Position <span class="hljs-operator">=</span> position; 
    textureCoordinate <span class="hljs-operator">=</span> inputTextureCoordinate.xy; 
    textureCoordinate2 <span class="hljs-operator">=</span> inputTextureCoordinate2.xy; 
} 
fragmentShaderString: 
varying highp vec2 textureCoordinate; 
varying highp vec2 textureCoordinate2; 
uniform sampler2D inputImageTexture; 
uniform sampler2D inputImageTexture2; 
void main() { 
    highp vec4 textureColor <span class="hljs-operator">=</span> texture2D(inputImageTexture, textureCoordinate); 
    highp float blueColor <span class="hljs-operator">=</span> textureColor.b <span class="hljs-operator">*</span> <span class="hljs-number">63.0</span>; 
    highp vec2 quad1; 
    quad1.y <span class="hljs-operator">=</span> floor(floor(blueColor) <span class="hljs-operator">/</span> <span class="hljs-number">8.0</span>); 
    quad1.x <span class="hljs-operator">=</span> floor(blueColor) <span class="hljs-operator">-</span> (quad1.y <span class="hljs-operator">*</span> <span class="hljs-number">8.0</span>); 
    highp vec2 quad2; 
    quad2.y <span class="hljs-operator">=</span> floor(ceil(blueColor) <span class="hljs-operator">/</span> <span class="hljs-number">8.0</span>); 
    quad2.x <span class="hljs-operator">=</span> ceil(blueColor) <span class="hljs-operator">-</span> (quad2.y <span class="hljs-operator">*</span> <span class="hljs-number">8.0</span>); 
    highp vec2 texPos1; 
    texPos1.x <span class="hljs-operator">=</span> (quad1.x <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.125</span>) <span class="hljs-operator">+</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span> <span class="hljs-operator">+</span> ((<span class="hljs-number">0</span><span class="hljs-number">.125</span> <span class="hljs-operator">-</span> <span class="hljs-number">1.0</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span>) <span class="hljs-operator">*</span> textureColor.r); 
    texPos1.y <span class="hljs-operator">=</span> (quad1.y <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.125</span>) <span class="hljs-operator">+</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span> <span class="hljs-operator">+</span> ((<span class="hljs-number">0</span><span class="hljs-number">.125</span> <span class="hljs-operator">-</span> <span class="hljs-number">1.0</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span>) <span class="hljs-operator">*</span> textureColor.g); 
    highp vec2 texPos2; 
    texPos2.x <span class="hljs-operator">=</span> (quad2.x <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.125</span>) <span class="hljs-operator">+</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span> <span class="hljs-operator">+</span> ((<span class="hljs-number">0</span><span class="hljs-number">.125</span> <span class="hljs-operator">-</span> <span class="hljs-number">1.0</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span>) <span class="hljs-operator">*</span> textureColor.r); 
    texPos2.y <span class="hljs-operator">=</span> (quad2.y <span class="hljs-operator">*</span> <span class="hljs-number">0</span><span class="hljs-number">.125</span>) <span class="hljs-operator">+</span> <span class="hljs-number">0</span><span class="hljs-number">.5</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span> <span class="hljs-operator">+</span> ((<span class="hljs-number">0</span><span class="hljs-number">.125</span> <span class="hljs-operator">-</span> <span class="hljs-number">1.0</span><span class="hljs-operator">/</span><span class="hljs-number">512.0</span>) <span class="hljs-operator">*</span> textureColor.g); 
    lowp vec4 newColor1 <span class="hljs-operator">=</span> texture2D(inputImageTexture2, texPos1); 
    lowp vec4 newColor2 <span class="hljs-operator">=</span> texture2D(inputImageTexture2, texPos2); 
    lowp vec4 newColor <span class="hljs-operator">=</span> mix(newColor1, newColor2, fract(blueColor)); 
    gl_FragColor <span class="hljs-operator">=</span> vec4(newColor.rgb, textureColor.w); 
}
</code></pre><h2 id="h-opengl" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">关于使用OpenGL实现滤镜</h2><p>前文提到过，根据class-dump出来的头文件，得知B612使用了<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/BradLarson/GPUImage">GPUImage</a>。理论上我们下一步研究下GPUImage项目，应该就可以实现china滤镜的效果了，但GPUImage项目还是挺庞大，挺复杂的。</p><blockquote><p>GPUImage 提供了<code>GPUImageLookupFilter</code>类来实现颜色查找类的滤镜</p></blockquote><p>这里我们直接使用OpenGL来实现China滤镜。阅读前面的shader代码，得知需要传递给shader两个texture，可以想象出一个是需要原图片，一个是china.dat转换出来的图片，经过顶点着色器和片段着色器，最终给目标图片加上滤镜。下面第一张图是原图，第二张是我用OpenGL实现的Demo效果图片，第三张是B612加china滤镜出来的结果。对比第二张和第三章，效果非常相似。利用OpenGL实现滤镜的代码，请查阅<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/cp0000/HackB612">HackB612</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4d971fbe79f892d2909550cb037599b7d3b0cce01ed67c8d8d36771e8ccb1719.jpg" alt="原图" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">原图</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a5efd496ea41fc64c73997c52fa9e8a76dccae6a18c3b91102659d1e1702de24.png" alt="Demo效果图" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Demo效果图</figcaption></figure>]]></content:encoded>
            <author>cp-codeplay@newsletter.paragraph.com (cp.codeplay)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/39227efc0e4c396b66df315e0f41d0b5db53258012c13666917f0269d0ae143c.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>