2016年4月14日星期四

php_screw在未知密钥情况下的破解

php_screw模块是一个依赖与zlib与流式加密的php代码加密模块。
主要构成为:
密钥模块:php_screw-1.5/my_screw.h
加密模块:php_screw-1.5/tools/screw.c
解密模块:php_screw-1.5/php_screw.c

具体的加密细节可以参考文件中的实现。

大体加解密的过程如下:
加密过程:
PHP文件(明文) -> zencode ->流式加密(my_screw.h秘钥)-> PHP文件(密文)
解密过程(可逆):
PHP文件(密文)-> 流式解密(my_screw.h秘钥)-> zdecode -> PHP文件(明文)

整个加密模块的弱点在于,在每次加密的过程中都使用相同的密钥,且没有用到IV。因此基于xor运算的特性,在已知一段未知密文,一段明文与密文的对照之后。可以破解未知密文多对照的明文。

破解只需要关注加密部分,解密部分完全可以忽略,因为解密部分依赖的密钥又不在我们手上= =!

下面来分解一下加密的过程:
输入:(PHP文件(明文)-> zencode)->
加密:流式加密(my_screw.h秘钥)->
输出:PHP文件(密文)

撇开加解密过程中zlib的参与(既zencode方法),仅关注中间的流式加密部分。
可以把经过zencode处理的php文件理解为流式加密过程中的明文。
经过流式加密处理的内容既为密文。

因此要破解一段密文a,我们还缺少一分明文与密文的对照。
密文b很简单,直接用对方的screw模块处理一下就能获得。
明文b也和简单,将一个PHP文件中的内容交给zencode处理一下就行了。
接下来将
(密文a ) xor (密文b) xor (明文b) 既可得到明文a
只不过这里得到的明文a是经过zencode处理的。
得到明文a之后调用zdecode一下即可得到最终解密的php代码。


以上情况基于密钥每次都相同的情况。
但实际情况下密钥并不是每次都相同的,但是密钥是有一定规律的,其规律与输入的明文长度有关。来看screw.c中的加密代码。可以知道密钥基于 pm9screw_mycryptkey短整型数组,在实际使用的时,并不是从0元素开始到循环尾元素的,而是基于明文的长度来选择一个起始位置,然后向 前迭代。如果两段明文的长度之间遵守一定规律,是可以构造出相同密钥加密后的密文的。

规律很简单,因为是流式加密,且没有带入任何影响密文长度的内容,因此密文的长度就是明文的长度。
假如密钥的长度为len(key), 对于一段长度为n的密文,其明文的长度也为n。
密钥的起始位置应该为(n-i) % len(key),又因为i默认为0,因此起始位置为:
n % len(key)。
为了构造起始位置相同的密钥,其明文的长度k必须大于n并且满足
k % len(key) == n % len(key)。
构造出此明文,经过screw加密一下得到对应的密文。在加上未知的密文。
这三者拿去xor一下,把xor得到的结果丢给zdecode处理,最终可获得未知密文对应的明文信息。

本场景适用于仅对密钥内容进行了修改, 未对 php_screw的原始加密算法进行过修改。
如果密钥长度修改过的话,就自己按
“明文的长度k必须大于n并且满足k % len(key) == n % len(key)”的规则去创建明文,然后一个个匹配结果吧!

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "../php_screw.h"
#include "../my_screw.h"

char *CryptBinaryData(char *pData, int nDataLen, int *pNewDataLen)
{
    char *pRet=NULL;
    *pNewDataLen = 0;
    pRet = zencode(pData, nDataLen, pNewDataLen);
  
    int    cryptkey_len = sizeof pm9screw_mycryptkey / 2;
    printf("cryptkey_len: %d\r\nfirst_key_elemnt:%d\r\n",cryptkey_len,pm9screw_mycryptkey[(*pNewDataLen - 0) % cryptkey_len]);
    int i=0;
    for(i=0; i<*pNewDataLen; i++) {
        pRet[i] = (char)pm9screw_mycryptkey[(*pNewDataLen - i) % cryptkey_len] ^ (~(pRet[i]));
    }
    return pRet;
  
}

void printf_string_on_element(char *pString, int nLen)
{
    int i=0;
    for(i=0; i<nLen; ++i)
    {
        printf("%d ",pString[i]);
    }
    printf("\r\n");
}

int main(int argc, char **argv)
{
    /*
  
     默认key长度为5
     "<? echo '123'?>";//zencode后的长度为23
     "<? echo '12345678'?>";//zencode后的长度为28
   
     满足
     23 % 5 == 28 % 5 && 28 > 25
    */
    char *pSourceText1="<? echo '123'?>";
    int nSourceText1Len = strlen(pSourceText1);
    char *pSourceText2="<? echo '12345678'?>";
    int nSourceText2Len = strlen(pSourceText2);
  
    //构造pSourceText2对应的“明文”
    int nPlainText2Len = 0;
    char *pPlainText2=zencode(pSourceText2,nSourceText2Len, &nPlainText2Len);
    printf("%s encoded by zencode:%d\r\n%sr\n",pSourceText2, nPlainText2Len, pPlainText2);
    printf_string_on_element(pPlainText2, nPlainText2Len);

    //构造pSourceText1的密文
    int nCipherText1Len = 0;
    char *pCipherText1 = CryptBinaryData(pSourceText1,nSourceText1Len,&nCipherText1Len);
    printf("%s Cryptyed %d:\r\n%s\r\n",pSourceText1, nCipherText1Len, pCipherText1);
    printf_string_on_element(pCipherText1, nCipherText1Len);

    //构造pSourceText2的密文
    int nCipherText2Len = 0;
    char *pCipherText2 = CryptBinaryData(pSourceText2,nSourceText2Len,&nCipherText2Len);
    printf("%s Cryptyed %d:\r\n%s\r\n",pSourceText2, nCipherText2Len, pCipherText2);
    printf_string_on_element(pCipherText2, nCipherText2Len);
  
    //xor
    int i =0;
    char plaintextBuffer[1024]={0};
    for(i=0; i<nCipherText1Len; ++i)
    {
        //plaintextBuffer[i] =pCipherText2[i] ^ pCipherText1[i] ^ (~pPlainText2[i]);
        //plaintextBuffer[i] = ~plaintextBuffer[i];
        //上面两句可以简化为下面这一句,因为两个非运算后,还是得到原始结果
        plaintextBuffer[i] =pCipherText2[i] ^ pCipherText1[i] ^ (pPlainText2[i]);
    }
    printf("%s\r\n",plaintextBuffer);
    printf_string_on_element(plaintextBuffer, nCipherText1Len);
    int nPlainTextResultLen = 0;
    char *pPlainTextResult = zdecode(plaintextBuffer, nCipherText1Len, &nPlainTextResultLen);
    printf("(%d)%s\r\n",nPlainTextResultLen, pPlainTextResult);
  
    return 0;
}

没有评论:

发表评论