藝術鬼才,Unicode 字符還能這麼玩?

上週的時候,朋友圈的直升飛機不知道爲何就火了,不少朋友開着各類花式飛機帶着起飛。git

圖片來自網絡

還沒來得及瞭解咋回事來着,這個直升飛機就🔥到的微博熱搜。程序員

圖片來自網絡

後面愈來愈多人開來他們的直升飛機,盤旋在朋友圈上方。因而不少朋友開來他們的坦克,專打直升飛機,一轟一個準。github

圖片來自網絡

好了,說回正題!web

程序員朋友應該都很熟悉 Unicode (萬國碼),它幾乎包含世界上全部符號,好比組成直升飛機這幾個特殊符號對應的 Unicode 碼分別爲:算法

ps:推薦一個網站,能夠根據符號搜對應的 Unicode 碼: https://unicode.yunser.com/un...

除了這些正常字符之外,Unicode 還包含着各類各樣的奇葩字符。json

奇葩字符

除了正常的咱們熟知的文字之外,Unicode 中還有一些奇怪的文字,好比下面這些文字後端

這咋讀?某少?

世代?

恩?超出認知範圍

除了這些奇怪文字之外,Unicode 還有一些奇葩的的符號。瀏覽器

例以下面一整套麻將牌:網絡

一整套的撲克牌:ide

一整套國際象棋:

image-20200725215319183

除了這些,經過組合符合,咱們還能夠造出各類各樣的顏文字(๑•̀ㅂ•́)و✧、

另外 Unicode 還收錄着咱們經常使用的 Emoji

除了這些以外,Unicode 中還有一些特殊字符的,利用這些字符,咱們還能夠玩出不少有趣的騷操做。

組合字符

Unicode 有一類字符稱爲組合字符,它能夠附加在前一個非組合字符上,從而使總體看起來像是一個字符。

組合字符原來目的是爲了解決一些地區語言、文字特殊的須要,好比說泰文聲調符號與母音符號。

正常使用的狀況下,這些組合字符數量都會有一些限制。可是在 Unicode 組合字符設計上,並無加這種限制,這樣使咱們能夠無限加這類組合字符。

利用這個特性,能夠達到一些惡搞效果,好比「擊穿天花板」與「鑿穿地板」的效果。

上面實現原理其是利用如下兩個組合字符:

上翻字符

下翻字符

只要複製這兩個字符相應的 HTML 代碼,跟在正常的字符後面,就可使這兩個字符附加在普通字符上,好比下面實現效果爲

黑̮̑

Unicode 碼值一般使用 U+N(16 進制N 表明碼值),好比 A 的碼值爲 U+0041。

在 HTML 中 Unicode 可使用 &#N;(十進制,N 表明碼值)表示

在 JS 中 Unicode 中須要使用] \uN(16 進制N 表明碼值)表示

只要咱們在普通字符多複製幾個這類附加字符,就能夠造成上述「擊穿」效果。

還記得上面說的泰文嗎,曾經有一段時間貼吧,很流行一種噴射文,好比下面的效果。

向左噴

向右噴

左右互噴

這種噴射文實際原理就是利用泰文中聲調符號附加在其餘正常符號上。

不過如今這個效果貌似已經沒辦法再復現了,如今咱們只能看到這樣的效果:

在一些老版本的系統/瀏覽器可能還能看到這種效果,知道的小夥伴留言區能夠告知一下。

零寬字符

Unicode 中還有一類格式字符,不可見,不可打印,主要做用於調整字符的顯示格式,因此咱們將其稱爲零寬字符。

零寬字符主要有如下幾類:

零寬度空格符 (zero-width space) U+200B : 用於較長單詞的換行分隔

零寬度非斷空格符 (zero width no-break space) U+FEFF : 用於阻止特定位置的換行分隔

零寬度連字符 (zero-width joiner) U+200D : 用於阿拉伯文與印度語系等文字中,使不會發生連字的字符間產生連字效果

零寬度斷字符 (zero-width non-joiner) U+200C : 用於阿拉伯文,德文,印度語系等文字中,阻止會發生連字的字符間的連字效果

左至右符 (left-to-right mark) U+200E : 用於在混合文字方向的多種語言文本中(例:混合左至右書寫的英語與右至左書寫的希伯來語),規定排版文字書寫方向爲左至右

右至左符 (right-to-left mark) U+200F : 用於在混合文字方向的多種語言文本中,規定排版文字書寫方向爲右至左

利用零寬字符不不可見的特性,咱們也能夠玩出一些騷效果。

空白微博

發佈微博的時候,若是內容都是空格,將沒辦法發佈。

可是若是咱們將零寬字符,好比說「零寬度空格符 U+200B」複製到微博,這樣咱們就能夠發佈空白微博。

咱們能夠利用 Chrome 瀏覽器的控制檯複製零寬字符,操做方式以下:

發佈效果以下:

真的沒有改 HTML 致使的.jpg

隱形水印

對於一些內部論壇或者說小說網站來講,能夠經過零寬字符在帖子或小說內容嵌入隱形水印。

當這些內容被一些爬蟲複製到其餘網站時,咱們就能夠經過隱形水印,輕鬆查找時那位用戶泄漏內容。

隱形水印主要原理就是將用戶信息好比用戶名,經過必定算法轉成零寬字符,這樣普通用戶瀏覽時徹底看不到這個水印。

若是內容被複制到其餘網站,隱形誰贏也被複制,只要找到這個水印,將這些零寬字符反轉成用戶名便可。

下面展現一種轉換方法,JS 代碼主要參考如下 Github 項目:

https://github.com/umpox/zero...

隱形水印生成方法

第一步咱們須要將明文字符串每一個字符都轉成二進制串。

// 每一個字符轉爲二進制,用空格分隔
    const textToBinary = username => (
      username
      .split('')
      // charCodeAt 將字符轉成相應的 Unicode 碼值
      .map(char => char.charCodeAt(0).toString(2))
      .join(' ')
    );

示例以下:

第二步,將二進制串轉爲零度字符串,轉換規則以下:

  • 1 轉換爲 u200b 零寬度字符(zero-width space)
  • 0 轉換爲 u200c 零寬度斷字符(zero-width non-joiner)
  • 其餘(剩餘就是空格) 轉換爲 u200d 零寬度連字符 (zero-width joiner)
  • 最後使用 ufeff 零寬度非斷空格符 (zero width no-break space) 做爲分隔符
const binaryToZeroWidth = binary => (
  binary.split('').map((binaryNum) => {
    const num = parseInt(binaryNum, 10);
    if (num === 1) {
      return '\u200b'; // \u200b 零寬度字符(zero-width space)
    } else if(num===0) {
      return '\u200c'; // \u200c 零寬度斷字符(zero-width non-joiner)
    }
    return '\u200d'; // \u200d 零寬度連字符 (zero-width joiner)

  }).join('\ufeff') // \ufeff 零寬度非斷空格符 (zero width no-break space)
);

最終加密方法以下:

const encode = username => {
  const binaryUsername = textToBinary(username);
  const zeroWidthUsername = binaryToZeroWidth(binaryUsername);
  return zeroWidthUsername;
};

使用加密方法將明文字符串加密以後,加密字符串肉眼是看不到了,可是實際仍是存在的。

實際上,若是咱們將加密以後字符串複製到 BEJSON 網站,就能夠看到字符。

image-20200722083507869

另外你還能夠把加密字符串複製到 IDEA 中,能夠看到相應的 Unicode 編碼值。

解密隱形水印

知道了加密的方式,解密其實就很簡單,咱們只要按照相反步驟的來就能夠了。

第一步,將隱形水印按照如下規則轉換爲二進制串。轉換規則以下:

  • 使用 ufeff 分隔字符串
  • u200b 轉爲 1
  • u200c 轉爲 0
  • 其餘字符使用空格
const zeroWidthToBinary = string => (
  string.split('\ufeff').map((char) => { // \ufeff 零寬度非斷空格符 (zero width no-break space)
    if (char === '\u200b') { // \u200b 零寬度字符(zero-width space)
      return '1';
    } else if(char === '\u200c') { // \u200c 零寬度斷字符(zero-width non-joiner)
      return '0';
    }
    return ' ';
  }).join('')
);

調用該方法,隱形水印轉成二進制串。

第二步,將二進制再轉爲相應的字符。

const binaryToText = string => (
  // fromCharCode 二進制轉化
  string.split(' ').map(num => String.fromCharCode(parseInt(num, 2))).join('')
);

最終解密方法以下:

const decode = zeroWidthUsername => {
  const binaryUsername = zeroWidthToBinary(zeroWidthUsername);
  const textUsername = binaryToText(binaryUsername);
  return textUsername;
};

解密示例以下:

短網址

咱們經常使用的短網址,域名後面會跟上一串隨機串,從而實現短網址到長網址的映射。好比如下網址:

https://sourl.cn/iLyn9S

然而咱們能夠利用零寬字符也能夠實現短網址的效果,,好比下面這個網站,就能夠生成這類短網址。

https://zws.im/

能夠看到這個短網址後面看不到任何字符,實際上這後面跟着一串零寬字符。當瀏覽器訪問該短網址時,後端程序只要反解密的後面零寬字符,拿到相應的網址,而後在作跳轉就能夠到指定的網站。

反解密的原理能夠參考上面隱形水印的代碼

當心零寬字符

平常開發過程當中,咱們有時須要從一些文件中讀取文本內容,而後作相應的處理。

有時候咱們可能會碰到一些詭異的現象,好比咱們以前碰到的例子。

後臺程序從 Excel 讀取文本內容,而後程序中判斷是讀取的文本內容是否與指定的字符串相等。

而後當咱們讀取一份 Excel 內容後,返現這段比較邏輯怎麼也經過不了。原本覺得是 Excel 內容存在空格什麼的,可是打開 Excel 仔細一看,跟指定字符串如出一轍,並無什麼其餘字符。

第一次碰到這種例子,沒有什麼經驗,真的排查了好久,到最後都有點懷疑人生了。最後無心間將文本內容複製到了 IDEA 中,才發現整理混雜着零寬字符!

若是各位小夥伴也碰到這類問題,不妨將複製文本內容,而後到 IDEA 中查看是否存在某些看不見字符~

最後(點個讚唄!)

這兩個星期一直很忙,一直都在 9106 的節奏,真的是累,因此斷更了一週!

所幸最近項目提測,稍微輕鬆了一點,能有點划水時間來寫寫文章。不過再提起筆來寫文章,就有點斷節奏了!

這篇文章墨跡了好久才水出來,下週開始再次恢復周更的節奏,再忙再累,每週都來一篇。

歡迎各位小夥伴,每週來這裏蹲我,Gank 我!!!

好了,我是樓下小黑哥,下週見!!!

參考連接

  1. https://juejin.im/post/5d3f01...
  2. http://zero.rovelast.com/
  3. https://zws.im/
  4. https://imweb.io/topic/5a08a5...
歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客: studyidea.cn

相關文章
相關標籤/搜索