All posts by Cocoa

Rust Learning from Zero (21) —— All I need is a dlsym | Exploit macOS application with Rust

It has been a long time since my last reverse engineering on macOS, and that was about Netease Cloud Music. 

But I always write that in Objective-C, perhaps I should try something different, let's say, Rust.

  1. Compile a .dylib that macOS recognises
  2. Build a Constructor Function that Invokes on dylib Loading
  3. All I need is a dlsym
    1. Find the Symbol You Want
    2. Casting to Function Pointer
  4. Compile and Hook

1. Compile a .dylib that macOS recognises

The first step is to tell cargo that what we need is a library

cargo new --lib exploit

And cargo will create these files for us.

exploit
├── Cargo.toml
└── src
    └── lib.rs

However, if we compile this project, the output library has a suffix .rlib. which suggests that this is a Rust library. Yet we need a dylib that macOS recognises.

Therefore the hint for producing a .dylib library should be added in Cargo.toml, which is shown below.

[lib]
crate-type = ["dylib"]

This tells cargo out crate type is a dylib, which satisfies the format requirements of macOS dynamic library.

Continue reading Rust Learning from Zero (21) —— All I need is a dlsym | Exploit macOS application with Rust

最近

離上次寫點東西已經過了一個月有餘,這一個月裡也算是發生了蠻多事情吧

見到了一直以來想見的あやか,還有朵朵和范范~

自己也暫時可以說是在這邊落腳了,一個人找了地方租房子住下了。但一個人住的確是挺寂寞的,沒有人可以說話,自己對很多事情也提不起來興趣⋯

感覺好像世界上就只剩下自己一個人了,唯一可以跟其他人交流的渠道就只剩網絡⋯⋯雖然說是有網絡可以交流,但是實際上並沒有人來找這邊聊天就是了。週六外面在下雨,於是在住的地方待了一整天,也是呆了一整天。

今天週日,出門去逛逛好了,順便買些需要的東西回來。大概需要一個檯燈吧,房間裡自帶的燈還是有點暗的感覺,也許是自己習慣在較高亮度的環境了?總而言之先出門好了w

買了一些會用到的東西,燈還是被我咕咕咕了,要不直接在網上買、快遞過來好了。一回來就已經下午 5 點半了,明天又是週一了呢,又要開始 5 天的上班了><

Rust Learning from Zero (20) —— Sobani Tracker in Rust

A few weeks ago, Ayaka and me started a project named Sobani, a simple cross platform UDP-based P2P audio sharing application for couples, friends, coworkers and etc. Sobani also has a public tracker server for storing all its peers' connection info.

Well, given that the last time that I wrote any Rust code was quite a while ago, I decided to rewrite the Sobani tracker in Rust. The Rust version of Sobani tracker is on BlueCocoa/sobani-tracker-rust.

In brief, this time I'd like to write this Rust project with async-std, and the tracker service will be encapsulated in SobaniTracker class. The SobaniTracker will be initialised with an IP:Port string as the listening endpoint for its UDP socket. Let's also add pretty_env_logger for beautiful logging.

Without further ado, the main.rs is shown below, which should be rather straightforward.

extern crate pretty_env_logger;
#[macro_use] extern crate log;

mod server;
use server::SobaniTracker;

#[async_std::main]
async fn main() -> std::io::Result<()> {
    pretty_env_logger::init();
    let mut server = SobaniTracker::config("0.0.0.0:3000");
    server.start().await?;
    Ok(())
}

As for the SobaniTracker (in server.rs), it should hold its listening endpoint, e.g. 0.0.0.0:3000, the internal UDP socket and all clients' connection info. And because the server will handle multiple clients in parallel, I added Mutex on clients so that the connection data can be consistent.

use async_std::net::UdpSocket;
use serde_json::json;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::sync::Mutex;
use sha2::{Sha256, Digest};

/// Sobani Tracker
///
/// This struct can be created by the [`server::SobaniTracker::config`] function. See its
/// documentation for more.
pub struct SobaniTracker {
    /// listening endpoint, `IP:Port` format
    bind: String,

    /// internal UDP socket
    socket: Option<UdpSocket>,

    /// bi-directional client mapping
    /// - maps from `IP:Port` to `ShareID`
    /// - maps `ShareID` to `IP:Port`
    clients: Mutex<HashMap<String, String>>
}

And the API of SobaniTracker goes below

impl SobaniTracker {
    /// Constructs a new `SobaniTracker` with listening endpoint at `bind`.
    ///
    /// # Example
    ///
    /// ```
    /// use server::SobaniTracker;
    ///
    /// let mut server = SobaniTracker::config("0.0.0.0:3000");
    /// ```
    pub fn config(bind: &str) -> SobaniTracker { /* */ }
    
    /// Starts the `SobaniTracker` server
    /// This function won't return
    ///
    /// # Exmaple
    /// 
    /// ```
    /// use server::SobaniTracker;
    ///
    /// let mut server = SobaniTracker::config("0.0.0.0:3000");
    /// server.start().await?;
    /// ```
    pub async fn start(&mut self) -> std::io::Result<()> { /* */ }
    
    /// Handles announce message from remote client
    /// This function will generate a `shareId` for the remote client
    async fn announce_handler(&self, remote: SocketAddr) -> Option<serde_json::Value> { /* */ }
    
    /// Handles push message from remote client
    /// The push message will be sent if a client (the requestor) wants to connect to
    /// another client (requestee) by requestee's shareId
    /// This function looks up the requested peer by `shareId` that sent in the push message
    /// And if both requestor and requestee have announced themselves on this tracker
    /// A pushed message containing the requestee's connection info will be sent back to the requestor
    /// An income message containing the requestor's connection info will be sent to the requestee
    async fn push_handler(&self, msg: serde_json::Value, remote: SocketAddr) -> Option<serde_json::Value> { /* */ }
}
Continue reading Rust Learning from Zero (20) —— Sobani Tracker in Rust

是否无力改变

很多时候能看到,在提出一些存在的问题之后,特别是关于政治上的,很多人也许先会同意问题存在,但是马上就会接一句“我们又能改变什么呢”

曾经觉得,比如有更强的力量去监督政府的话,还是可以改变的,可是这样的“更强的力量”又从哪里来呢?我们连杨永信都治不了,连遍布全国的这种所谓的“教育”机构都制裁不了,连一个有计划的强奸幼女的人都不能定罪,我们能做什么呢?

感觉到真实的无力,所有的这些残忍的事情不断的发生,发生到好多人都要麻木了甚至已经麻木,都要无所谓了,我们依旧没有什么办法……

回顾自己之前在知道某些事情的时候,要是自己有所行动的话,是不是也会不一样?只觉得自己什么都没有做到。现在去把那个地方拆了又能怎么样,站在当事人的角度,已经没有用了,唯一有用的就是那个时候能去带消息出来,能救她出来。现在再来说这些,无用也无力。

我们要拿什么去对抗这些啊……是不是这些都是自己无力改变的呢……

『側に / Sobani』 —— 在你身旁

Sobani —— Fully self-hostable and customizable media entertaining stream service based on Node.js for couples, friends, collaboration teams, and even quarantined office workers.

在几周前的一个晚上,跟猫猫闲聊时,猫猫有提到说「要是可以一起听音乐 / 看电影就好了」。虽然 Team Viewer 的音频共享可以将被连接的电脑的音频发送过来,同时也会将屏幕共享过来,在网络不太好的情况下音频会断断续续,同时视频的质量也不太高,体验会比较差QwQ 另外 Team Viewer 在开始语音的时候,就不能 forward 电脑输出的音频给对方了,因此还需要别的软件来做语音通话Σ(・□・;)

在一边想象着跟猫猫听音乐 / 看电影的时候,一边就在跟猫猫开始计划着写这个项目。每次开始写项目时,猫猫和 Cocoa 一致认为日常最难问题就是叫什么名字喵,一开始猫猫提到可以用「接続 / Setsuzoku」抽取一个部分和 link 接起来,也就是「setsulink」这样的;又或者是「cosetsu」,即 coop 和 setsuzoku。在我们纠结一段时间名字之后,

猫猫提议:把自己的心愿写进去

Cocoa:Sobani そばに?

猫猫:诶 是什么呀

Cocoa:就是在身边的意思,要说心愿的话,大概就是这个 w 想要在身边,但是又只能暂时用软件来实现啦

在决定好用这样的心愿起名字之后,就开始了这个满怀爱(狗)意(粮)的项目啦/ 头一次用 VS Code 的 Live Share 一起写代码!

那么想到的连接办法当然就是 NAT 穿透 了!在最开始的时候,我们决定使用的语言是 Go,然后本来是想用 libp2p 来完成的,但是后来验证发现,libp2p 默认使用的是 TCP 连接,虽然 TCP 的 NAT 穿透是有方法的,但是似乎实现起来会太复杂了,以及对于 Go 上跨平台的、好看的 UI 还没有什么头绪(>﹏<)

项目的地址在 nekomeowww/sobani,最新的 build 都放在 release 里面啦~同时支持 macOS / Linux / Windows 的说(*^3^)

公开的 tracker 服务器的地址是 34.80.41.119:3000,这个 Sobani 的 tracker 服务器也是可以自己搭建的/

Sobani tracker 的项目地址是 nekomeowww/sobani-tracker,里面的 deploy.sh 脚本在 Debian / Ubuntu 上验证过~

Continue reading 『側に / Sobani』 —— 在你身旁

被 CloudFlare 耽误的 v2ray ws+tls

前几天跟佑奈在闲聊的时候,听佑奈吐槽说她的 v2ray 似乎被制裁了,于是 Cocoa 这边推荐她用 v2ray 的 ws+tls+CDN,不过佑奈说「CDN 之后,除了 Mac 其他的都访问不到了」

于是 Cocoa 就跟着佑奈研究了好久好久,把几乎所有可能的地方都怀疑了一下,从 DNS、VPS 的 IP、acme.sh 申请证书,一路怀疑到 Nginx 版本、SSL Cipher 协商、TLS 版本、三级域名设置……那天晚上折腾到了凌晨 6 点才睡QwQ 最后发现……居然是 CloudFlare 的锅!

详细的过程和最后的总结的话,佑奈执笔跟 Cocoa 在佑奈的博客上一起写了一篇非常非常详细的心累历程记录~ 是佑奈写得炒鸡用心的一篇记录喵~被 CloudFlare 耽误的 v2ray ws+tls

虽然最后在写这个详细的记录的时候,又遇到了一些奇妙的问题(>﹏<) 改前端果然也是一个大坑呀Σ(・□・;)

在 CentOS 7 中编译安装 Aria2

咕噜~昨天猫猫在她的 VPS 上给了我一个账号,然后还花了好长时间帮我配置,zsh 的主题也是猫猫自己写的哦,而且还给了 2TB 的空间~是头一次有人给 Cocoa 配置系统,超级感动的说 (>﹏<)

在拿到之后就跟着猫猫安装了 neofetch~( ^ω^ )

是我之前几乎没用过的 CentOS 7!在一边安装一边聊的时候,猫猫说她在 CentOS 上使用 Aria2 的时候遇到过一些问题,那么~这里就让 Cocoa 来帮猫猫解决吧!

以前 Cocoa 自己折腾的时候主要是 Ubuntu / Debian,自己有记录一份 Ubuntu / Debian 下 Aria2 的编译安装流程~不过 CentOS 的话,因为是 RedHat 系,所以有蛮多不一样的地方,于是就一边摸索一边记录下来了~

Continue reading 在 CentOS 7 中编译安装 Aria2

Go net/http 与 nodejs Koa 的 Σ(・□・;)

最近跟猫猫正在写一个一起听音乐的项目「側に」,然后昨天晚上就在一起通宵写代码的说~我对 stream 和 P2P 也了解得蛮少的,就跟着猫猫一起一边学一边写~是 Pair Programming (/ω\)!

猫猫找到了一个 Go 里面的 P2P 的库 libp2p。于是一番商量之后,打算先写一个可以通过 P2P 方式相互连接的小 demo 出来~猫猫跑去用 nodejs 写服务器端的 tracker,我则是跑去研究这个库里的 example 写 client 出来~

一边开着 TeamViewer 一起听歌,一边开了几个 VSCode 的 Live Share 开始写~其实到这里都还好喵,还是蛮顺利的!然而在测试的时候发现, Go 这边一 POST JSON 数据过去,那边服务器就 throw error 了 Σ(・□・;)

一开始的时候在想为什么 Koa 一直没有收到数据,后来用「手动挡」发现,自己给 Koa addListener 的话,的确数据都是正常的样子><

然后就陷入了沉思,莫非是又遇到了什么神奇的坑了么(゚o゚;; 而且猫猫那边用 nodejs axios 发给 server 却又都是正常的……咕噜咕噜呜(>﹏<)

啊,这实在是不够科学!于是找猫猫要了一下 tracker 部分的代码喵,在本地跑了一下之后发现,这边本地 POST 也炸了!

炸了之后跟猫猫怀疑是不是 Go 里面 net/http POST 的时候默认 gzip 了,但是 Content-Length 又的确是原始的 JSON 字符串的长度的说……

Continue reading Go net/http 与 nodejs Koa 的 Σ(・□・;)

WebP Server in Rust

Generate WebP images for JPG / PNG files on-the-fly with Rust! BlueCocoa/webp_server_rs

Speaking of switching to WebP image, at the first glance, I just did it in a very naive approach.

Then @Nova wrote a Node.JS server that can serve JPG/PNGs as WebP format on-the-fly. You can find that at n0vad3v/webp_server.

A few days ago, @Nova and @Benny rewrite the WebP Server in Golang, webp-sh/webp_server_go

And that looks really promising, the size of the webp server, according to its description, had greatly reduced from 43 MB to 15 MB, and it is a single binary instead of webp_server with node_modules.

I cloned that project and added a tiny feature. However, I just found that although it is absolutely easy to implement the tiny feature, there is a potential design issue with the `fasthttp` module. In order to get everything work, it took me about 4 hours to debug on it.

Finally, it turned out to be a pointer of an internal variable (ctx.Request.uri, or so) was directly returned from ctx.Path(), and if users invoke ctx.SendFile(filepath), the ctx.Request.uri will be set to filepath, which will also propagate to all variables that hold the shared value of ctx.Path(). You may visit my previous blog post for details.

Well, in aforementioned blog post, I said that it would be better if it was written in Rust. Now, let's make it come true and push the WebP server even further.

There are some comparisons among n0vad3v/webp_server, webp-sh/webp_server_go and BlueCocoa/webp_server_rs.

As for size,

  • webp_server(nodejs) with node_modules takes 43 MB
  • webp-server(go) has reduced to 15 MB, and it's single binary
  • webp-server(Rust) pushes that even further, only 3.6 MB on macOS and 6.4 MB on Linux

In terms of convenience, you can just download the binary file and run if you choose either webp-server(go) or webp-server(Rust). However, webp_server(nodejs) requires pm2 to run.

Performance, to be honest, I haven't got time to run some benchmarks on them. But IMHO it (webp-server(Rust)) should be as fast as golang version.

记 Golang 下遇到的一回「毫无由头」的内存更改

前两天看到 @Nova Kwok@BennyThink 做了一个 WebP Server,于是 clone 下来玩了一下,发现貌似没有做“原始图像更新后,重新生成相应的 WebP 图像”的功能。好的,这个说起来简单,做起来也很简单,就是 os.Stat 一下,然后取到图像最后修改时间的 UNIX timstamp,STAT.ModTime().Unix(),最后再跟先前生成好的 WebP 文件名比较一下就好(timestamp 会放在生成的 WebP 文件名里)。

上面为止真的都很简单,在 macOS 上测试了一下,看起来没问题~于是就提交 Pull Request

然而 Nova 告诉我说,

Nice PR, but there seems a little problem that the older converted images are not deleted after the change of the original image, this might cause a possible leakage of the original one's content.

显然我是一头雾水,一开始还以为自己提交 PR 的时候是不是手滑删掉了几行,检查了一下之后发现并没有!然后姑且先把 macOS 上测试的截图 comment 在了 PR 下面。

接着我估计 Nova 应该是在 Linux 下跑的测试,于是就在一台新的 VPS 上安装了 go,把我 fork 且修改过的那份代码 clone 在 VPS 里测试。本来我预估的时候要么是我搞错了文件,要么也许是 Nova 不小心用了以前编译好的文件。然后一测试我就惊了,居然真的没有删除以前生成的 WebP 图像 Σ(・□・;)?!

由于没有 Linux 机器,也懒得安装虚拟机了,只能一头雾水的在 VPS 用 fmt.Println 输出来简易 debug 了。根据 fmt.Println 的输出,发现 ImgName 不知道为什么就突然之间被改了!

[1]ImgName: webp_server.png
[2]ImgName: webp_server.png
[3]ImgName: webp_server.png
[4]ImgName: webp_server.png
[5]ImgName: root/webp_serve

上面是在 5 处不同的地方 fmt.Println("ImgName", ImgName) 的输出,虽然我放了这么多,但是实际上在代码里 ImgName 在其作用域内只有过一次赋值,

ImgPath := c.Path()
// ... 略去 10 行左右判断文件扩展名的代码
ImgName := path.Base(ImgPath)

然后就没写过了,仅有读的操作,没有任何赋值,中间只有一次被用来当作 Sprintf 的一个参数

WebpImgPath := fmt.Sprintf("%s/%s.%d.webp", DirPath, ImgName, ModifiedTime)

但显然这个也不会更改 ImgName 的内存嘛。“这不科学!” 虽然想这么叫出来,但是想想这个肯定还是有原因的!

Continue reading 记 Golang 下遇到的一回「毫无由头」的内存更改