昨天在一個 PHP 的羣裏看到一個圖片,圖片如下:

        看到這個圖片,我覺得這應該是某個收費項目的源碼,收費的項目爲什麼還要提供源碼,這就是 PHP 的問題之一吧。

很多人也許想要修改這樣的源碼,但是無奈源碼又是這樣的讓人看不懂。 拿到這樣的源碼,我估計很多想要修改源碼的一部分人就被卡住了。在這種情況下,我想說的是,作者既然這麼做了,就是不希望被別人修改。如果真的覺得項目好的話,其實可以去付費的,畢竟軟件是每個軟件工程師的汗水。

雖然話是這麼說,但是如果只是單純的想要學習,也不產生什麼利益的話,遇到這樣的問題有什麼辦法呢?雖然我對 PHP 不怎麼懂,但是我知道對於 PHP 這種源代碼層面的處理想要還原問題不大(我自己的臆想,畢竟各種的處理方法可能很多,只是我不知罷了),關鍵在於還原一下值不值。當然了,PHP 的加密還存在引擎級別的,我覺得引擎級別還是有難度的。但是如果只是源碼級別的處理,我覺得發揮想象其實每個人都是可以去嘗試還原的,因爲還原的可能性還是很大的。

這類代碼我沒怎麼見過,針對上面那個圖片,我沒有拿到源文件,只有這個圖片。針對這個圖片,我給出一個處理的思路,和大家進行交流。

說說我的思路

說說如果是我處理的話,我處理的思路吧。

  • 首先將代碼格式化,用很多工具都可以進行格式化,比如 PHPStorm;

  • 這樣的代碼格式化後顯然是沒有太大的用處的,格式化的目的在於要把整個源碼規範一下,然後嘗試把整個代碼中的 goto 語句去掉;因爲代碼的執行是順序的,也就是從文件的開頭到結尾這麼進行執行,如果能把 goto 去掉的話,你就得到了一份真正的執行順序的代碼,其實 goto 就是無條件的跳轉,我們將離散的用 goto 連接的代碼,變成線性的就可以了;

  • 除了滿屏的 goto 語句以外,剩下的就是一些看不懂的字符了,可能有人會說是加密了之類的,其實是看的不夠仔細。仔細觀察的話,其實只是被編碼了。我們可以從代碼中有明顯特徵的位置開始還原,什麼是明顯特徵?看下圖。

        圖中是一個 date() 函數,這個函數我們每個人都會用,它的參數是字符串。PHP 中用來限定字符串的符號分兩種,分別是 單引號 雙引號 ,在平時爲了代碼的運行速度,我們寫代碼通常會使用單引號,而字符串當中有轉義字符時,我們就要去使用雙引號。而這裏 date() 函數中的字符串其實就是轉義字符。這些看起來被加密的東西其實 就是一些 ASCII 碼,說白了就是考驗大家的基礎。 “\”開頭的是 8 進制,“x”開頭的是 16 進制,"\131" 是大寫字母“Y”,“\55”是字符“-”,剩下的還要我說嗎?還不會?找一份 ASCII 碼錶對着看看不行麼?

        有了第三步的基礎,還原剩下的部分難嗎?

嘗試

我去網上找了類似的一個文件,然後自己嘗試用代碼去還原它的結構,也就是我上面思路的第二步。畢竟文件有點大,還是寫代碼還原靠譜。代碼寫了不到 200 行,還原差不多 20 多行的代碼。可以說是有進展的,爲什麼沒有全部還原呢?其實是有原因的,因爲在格式化以後,我用代碼進行處理的時候,沒有逐個的去處理各種可能(因爲這部分花時間比較多),我只是處理了部分的情況。有些格式化後的代碼,和我想要的預期也不太相同,比如多行連續標籤,標籤後面接 goto 之類的情況,我沒有去一一處理,因爲我爲的不是還原源碼,而是驗證我的思路。給出關鍵代碼的結構,完整的源碼就不提供了 (具體的處理我刪掉了 ,我自己都沒有寫完,而且也不算複雜。

<?php


class decode

{

private $f;

private $arr = [];

private $lineNo = 0;


public function openfile()

{

$this->f = fopen('./test.php', 'r');

}


public function closefile()

{

fclose($this->f);

}


public function de()

{

$this->openfile();

while (!feof($this->f)) {

$line = fgets($this->f);

$this->lineNo ++;

$this->parse($line);

}


$this->closefile();

$this->output();

$this->outputcode();

}


public function delrn($str)

{

}


public function parse($line)

{

// 處理 goto 的

if (strpos($line, 'goto') !== false) {


}


// 處理標號後的代碼

if (strpos($line, ':') !== false) {


// 標號後面是單行代碼

if (strpos($line, ';') !== false) {


}


if (strpos($line, ':') !== false) {


}


if (strpos($line, 'goto') !== false) {

}


// 標號後面是多行代碼

if (strpos($line, '{') !== false) {

$code = $this->delrn($line);

$i = 1; // 記錄左括號{的數量

while ($line = fgets($this->f)) {

$this->lineNo ++;

if (strpos($line, '{') !== false) {

}


if (strpos($line, '}') !== false) {


}


$code = $code . ' ' . $this->delrn($line);

}

}


// 標號後面是個function

if (strpos($line, 'function') !== false) {

$code = $line;

$i = 0; // 記錄左括號{的數量

while ($line = fgets($this->f)) {

$this->lineNo ++;

if (strpos($line, '{') !== false) {


}


if (strpos($line, '}') !== false) {


}


$code = $code . ' ' . $line;

}

}


return ;

}

}


// 中間文件

public function output()

{

$f = fopen('./output.php', 'w');


foreach ($this->arr as $k => $v) {

}


fclose($f);

}


// 最終文件

public function outputcode()

{

$f = fopen('./code.php', 'w');


while (true) {

}


fclose($f);

}


public function next($key, $arr)

{

$bret = false;


foreach($arr as $k => $v) {

if ($bret == true) {

return $k;

}

if ($k === $key) {

$bret = true;

}

}


return false;

}

}


$decode = new decode();

$decode->de();

總結

這種對代碼的處理一般應該被稱爲“ 代碼混淆 ”,而這種代碼混淆的方式算是簡單的。這種工具其實可以自己實現一個,按行讀取每一行的 PHP 代碼,然後給每行代碼隨機生成一個行號,然後用 goto 連接,最後進行亂序。然後可以把“字符串”處理成“轉義字符”。當然了,其實還有很多可以處理的方法,只要把能想到的處理方法定義成規則,你的代碼混淆工具處理後的 PHP 代碼會比這個要複雜。

知道了混淆的思路,那麼反混淆的話,其實也是這種思路,可以人肉進行處理,如果量大就不合適人肉了。量大就需要寫工具去自動化完成了。

最後,我想再次和大家說,我們都是做軟件開發的,請珍惜每一位軟件工程師的汗水。盜取別人的成果,其實是在破壞這個行業,也是在違法。我們面對各種問題時,還是抱着學習和提高自身能力出發。

喜歡就點在看哦~

相關文章