吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9113|回复: 3
收起左侧

[原创] 易语言支持库内的DES算法分析

  [复制链接]
monvvv 发表于 2019-5-7 17:09

介绍

易语言支持库里有个这样的命令:

〈字节集〉 加密数据 (字节集 字节集数据,文本型 密码文本,[整数型 加密算法]) - 数据操作支持库一->数据加解密 

其中加密算法可以选DES以及RC4,使用RC4的话其他软件也可以解密,但是如果使用DES算法,结果却和其他标准算法产生的结果不同。本文详细分析了该不同的原因,以及解决方式。

易语言基础

易语言静态编译出的代码在调用库函数时都遵循一个标准:

  push xxxxx  ;一些magic value
  push arg    ;参数
  mov ebx, function
  call FunctionHelper ;一个usercall,在里面会call ebx

FunctionHelper函数如下

  void* FunctionHelper(void* fun, int a2, ...)
  {
    fun((int)&v3, a2, (int)va); //v3是用来传出返回值的。
  }

而易语言内的字节集结构如下

  struct
  {
     int magic //固定前缀,值为1
     int len   //数据长度
     char data[] //数据,一个变长数组
  }

知道了这些,在编译出的程序里很容易就能定位到我们想要的函数的位置。

加密数据函数实现

随便编译一个测试程序,定位到加密数据这条命令的地址,函数如下

  int __cdecl encode(int pResult, int a2, int a3)
  {
    return encode_iner((int *)pResult, (_DWORD *)a3, a2, 1);// 1 = 加密 0 = 解密
  }

pResult是一个用来传出返回值的指针,a2、a3分别是指向参数列表的指针和参数数量。encode_iner的一部分如下:

  int encode_iner(int *a1, _DWORD *a2, signed int a3, int mode)
  {
    v6 = a3 > 2 && a2[8] == 0x80000301 && a2[6] == 2 //参数数量>2,且加密算法为RC4
    if ( v6 )
    {
      sub_45C090(a2[3], strlen((const char *)a2[3]), &v15); //初始化RC4的S-BOX
      v7 = malloc(v5);
      v13 = v7;
      if ( v7 )
      {
        qmemcpy(v7, v4, v5);
        v12 = v5;
        sub_45C160((int)v7, v5, (int)&v15);       // RC4
      }
      goto LABEL_21;
    }
    //如果不是RC4那就调用des_encrypt并将mode传入,解密时会判断数据是否能8bytes等分
    if(!mode && v5 % 8)
      goto LABEL_21;
    v9 = v8 + v5 + 4;
    v7 = malloc(v9);
    v14 = a2[3];               // v14为key
    v5 = *(_DWORD *)(*a2 + 4); // v5为指向数据的指针
    *v7 = v5;                  // 第一个int为长度
    qmemcpy(v7 + 1, v4, v5);
    des_encrypt((int)v7, v9, mode);  // 注意,在传入数据时会把长度一起传进去。所以加密"123"实际上是加密"\x03\x00\x00\x00123"
  }

des_encrypt内部又会调用调用一个函数,该函数就是具体的DES实现,代码如下:

  int crypto_iner(int data, int len, int key, int mode)
  {
    int result; // eax
    int v5; // esi
    int v6; // edi

    GetNewKey((_BYTE *)key);                     // xor loop, key -> 8 bytes
    SetSubKeys(gKey, mode == 0);     // expand key set subkeys,
    // DES中加密和解密使用同一个算法,只有子秘钥的顺序不同
    result = len / 8;
    if ( len / 8 > 0 )
    {
      v5 = data;
      v6 = len / 8;
      do
      {
        result = (int)EncodeRaw(v5, v5);         // 加密8bytes块
        v5 += 8;
        --v6;
      }
      while ( v6 );
    }
    return result;
  }

在GetNewKey里会对传入的key循环异或,保存在一个全局变量里,在生成子秘钥的时候使用。由于接受的key长度是任意的,该方法确保了可以获得8bytes的key。

  int* GetNewKey(_BYTE* key)
  {
    _BYTE *v1; // ecx
    int *result; // eax
    char v3; // bl

    v1 = a1;
    *(QWORD *)gKey = 0;  //清空
    result = gKey;
    while ( *v1 )
    {
      v3 = *v1++ ^ *(_BYTE *)result;
      *(BYTE *)result = v3;
      result = (int *)((char *)result + 1);
      if ( result == &gKey[8] )              // end
        result = (int *)gKey;
    }
    return result;
  }

SetSubKey是生成子秘钥k1-k16的过程,函数内有很多C语言形式的优化,比如硬编码位与表等。EncodeRaw的实现与标准没什么不同,唯一的区别就是在操作前会将8bytes转为两个DWORD,结束后再转换回去。可能这样运行效率会高一点?

0045D310  /$  8B4C24 04     mov ecx,dword ptr ss:[esp+0x4]
0045D314  |.  33C0          xor eax,eax
0045D316  |.  56            push esi
0045D317  |.  8A01          mov al,byte ptr ds:[ecx]
0045D319  |.  8BD0          mov edx,eax
0045D31B  |.  8B4424 0C     mov eax,dword ptr ss:[esp+0xC]
0045D31F  |.  C1E2 18       shl edx,0x18
0045D322  |.  8910          mov dword ptr ds:[eax],edx
0045D324  |.  8B30          mov esi,dword ptr ds:[eax]
0045D326  |.  41            inc ecx
0045D327  |.  33D2          xor edx,edx
0045D329  |.  83C0 04       add eax,0x4
0045D32C  |.  8A11          mov dl,byte ptr ds:[ecx]
0045D32E  |.  C1E2 10       shl edx,0x10
0045D331  |.  0BF2          or esi,edx
0045D333  |.  41            inc ecx
0045D334  |.  33D2          xor edx,edx
0045D336  |.  8970 FC       mov dword ptr ds:[eax-0x4],esi
0045D339  |.  8A31          mov dh,byte ptr ds:[ecx]
0045D33B  |.  0BF2          or esi,edx
0045D33D  |.  41            inc ecx
0045D33E  |.  33D2          xor edx,edx
0045D340  |.  8970 FC       mov dword ptr ds:[eax-0x4],esi
0045D343  |.  8A11          mov dl,byte ptr ds:[ecx]
0045D345  |.  0BF2          or esi,edx
0045D347  |.  41            inc ecx
0045D348  |.  33D2          xor edx,edx
0045D34A  |.  8970 FC       mov dword ptr ds:[eax-0x4],esi
0045D34D  |.  8A11          mov dl,byte ptr ds:[ecx]
0045D34F  |.  C1E2 18       shl edx,0x18
0045D352  |.  8910          mov dword ptr ds:[eax],edx
0045D354  |.  8B30          mov esi,dword ptr ds:[eax]
0045D356  |.  41            inc ecx
0045D357  |.  33D2          xor edx,edx
0045D359  |.  8A11          mov dl,byte ptr ds:[ecx]
0045D35B  |.  C1E2 10       shl edx,0x10
0045D35E  |.  0BF2          or esi,edx
0045D360  |.  41            inc ecx
0045D361  |.  33D2          xor edx,edx
0045D363  |.  8930          mov dword ptr ds:[eax],esi
0045D365  |.  8A31          mov dh,byte ptr ds:[ecx]
0045D367  |.  0BF2          or esi,edx
0045D369  |.  33D2          xor edx,edx
0045D36B  |.  8930          mov dword ptr ds:[eax],esi
0045D36D  |.  8A51 01       mov dl,byte ptr ds:[ecx+0x1]
0045D370  |.  0BD6          or edx,esi
0045D372  |.  5E            pop esi
0045D373  |.  8910          mov dword ptr ds:[eax],edx
0045D375  \.  C3            retn

除去一些优化的部分外,该实现是一个典型的DES算法的ECB模式,每个加密块之间没有任何联系。

差异

DES算法包含了很多预先定义好的置换Table,想要找出变化后的内容不是很容易。不过对比后发现,这个实现里包含的Table都和标准没什么差异,问题在进行key置换时的一句代码:

  char v23[56];

  v2 = 0;
  do                                            // 读PC1表 密钥置换 去除校验位
  {
    v23[v2] = (*(_BYTE *)((pc1[v2] >> 3) + a1) & bitTable[2 * (pc1[v2] & 7)]) != 0;
    ++v2;
  }
  while ( v2 < 56 );

这段代码从pc1表里获得第n位对应的置换位,在key里找到并保存到v23里,方式是首先获得int数组的开始位置,再通过掩码表bitTable获得该int具体的某一位。可是,bitTable的定义如下:

  unsigned short bitTable[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

在这个表里,如果要获取第0个bit,获得的掩码将是0b00000001,最后一个bit的掩码是0b10000000,顺序颠倒了过来。也就是说获得的1位却会得到最后一位,相当于将key每8个bit都倒转了一次。正确的定义应该是

  unsigned short bitTable[8] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x04, 0x02, 0x01};

总结

关于这个问题到底是一个feature还是BUG,我不能确定,也不知道去哪提交。

不过避免这个问题的方法也很简单,在调用"加/解密数据"或者其他DES函数时将key同样按照上面的方式倒转一次就可以了。比如一个key为01 01 01 01 01 01 01 01,在普通DES里等价于00 00 00 00 00 00 00 00,在加密数据里等价于:80 80 80 80 80 80 80 80。另外,在加密解密后同样需要和易语言里一样将data的长度同样作为数据,如"\x31"变为"\x01\x00\x00\x00\x00\x31"。

下面是一个简单的python实现,其他语言同理

from Crypto.Cipher import DES
import struct

def reverse_bytes(b):
    assert type(b) == bytes
    ba = bytearray(b)
    for i in range(0, len(b)):
        ba[i] = int(format(b[i], '0>8b')[::-1], 2)
    return bytes(ba)

def get_new_key(key):
    ba = bytearray(8)
    i = 0
    for b in key:
        ba[i] = b ^ ba[i]
        i = i + 1 if i < 8 else 0
    return bytes(ba)

# zero padding
def padding(d):
    ba = bytearray(d)
    while len(ba) % 8 != 0:
        ba.append(0)

    return bytes(ba)

def append_len(d):
    assert type(d) == bytes
    length = struct.pack('<L', len(d))

    return bytes(length + d)

def remove_len(d):
    assert type(d) == bytes
    return d[4:]

def e_des_encrypt(plain, key):
    des = DES.new(reverse_bytes(get_new_key(key)), DES.MODE_ECB)
    return des.encrypt(padding(append_len(plain)))

def e_des_decrypt(raw, key):
    des = DES.new(reverse_bytes(get_new_key(key)), DES.MODE_ECB)
    t = des.decrypt(raw)
    return remove_len(t)

经测试与易语言的加密数据命令输出结果相同。

  # 易语言:
  # 输出调试文本(字节集_字节集到十六进制 (加密数据 (到字节集 (“123456789”), “123456789”, #DES算法)))
  # 输出:
  # 53DEE70DD231541839EB99553B8B056D
  # --------------------------------
  # python:
  plain = b'123456789'
  key = b'123456789'
  ciph = e_des_encrypt(plain, key)

  print(ciph.hex().upper())
  print(e_des_decrypt(ciph, key).decode())
  # 输出:
  # 53DEE70DD231541839EB99553B8B056D123456789
  # 123456789

参考资料

  1. 一篇关于DES算法的简单介绍

免费评分

参与人数 8威望 +1 吾爱币 +15 热心值 +8 收起 理由
zlufeng + 1 + 1 热心回复!
Light紫星 + 1 + 1 用心讨论,共获提升!
Hmily + 1 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
黑的思想 + 1 用心讨论,共获提升!
爱飞的猫 + 3 + 1 用心讨论,共获提升!
cxp521 + 1 + 1 谢谢@Thanks!
姚小宝 + 1 + 1 热心回复!
wahx1314 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

hongjian 发表于 2019-11-28 17:25
如果我想用易的这个支持库的“加密解密”生成标准des加密数据,应该怎么处理?需要提前转换哪些数据?
wangyujie96 发表于 2020-4-9 13:39
易语言,luajit,老哥是在研究某中文的游戏引擎吧?
pengtusheng 发表于 2020-10-18 19:45
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-27 02:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表