介绍

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

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

其中加密算法可以选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

转载自:https://www.52pojie.cn/thread-950178-1-1.html

[易语言][转载]易语言支持库内的DES算法分析相关推荐

  1. incon函数图像c语言,[转载]c语言经典题目

    1.局部变量能否和全局变量重名? 答:局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量.对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如 ...

  2. c语言写易语言支持库6,易语言编写支持库

    ========== 1.易支持库机制 ========== !!!易支持库的库信息.命令信息等等均使用某个结构储存 !!!易源码中的自定义数据类型.类模块.组件即为支持库中的库定义数据类型 !!!易 ...

  3. c开发 易语言支持库,易写易库-用易语言开发易语言支持库.pdf

    易写易库-用易语言开发易语言支持库 易写易库(EXEK)用户手册 版本:0.2 作者:liigo,/liigo 时间:2008.10 概述 EXEK EXEK "易写易库(EEXXEEKK) ...

  4. “易写易库(EXEK)”项目启动,用易语言开发易语言支持库

    "易写易库"(EXEK,E Xie E Ku)项目已经启动,用易语言开发易语言支持库.我(liigo)准备用一个月左右的业余时间,完成本项目的一期工程. 用易语言开发易语言支持库, ...

  5. 易语言 大数计算 大数支持库的使用实例

    部分数据过大需要使用到大数计算,网上暂时没看到简单的例子实例,所以记录下.希望能帮上需要的人. 以下代码直接复制到易语言即可使用.支持库里需要勾选上    数值计算支持库 EC模块下载地址:易语言大数 ...

  6. java支持库 易语言_易语言支持库简单安装加载添加

    1,首先,准备好欲安装的易语言支持库文件(fne.fnr或lib为后缀名等文件) 2,接下来,有必要对易语言支持库的文件做重要讲解.要安装支持库之前,先要弄明白易语言安装目录下的两个文件夹:lib和s ...

  7. m3u8云切片程序PHP视频切片转码系统易语言源码(支持添加视频水印)

    文章目录 前言 一.m3u8云切片程序PHP视频切片转码系统易语言源码(支持添加视频水印) 二.程序演示与下载 1.程序演示 2.程序下载 前言 这里就不过多赘述了,上篇文章对m3u8也做过介绍了,这 ...

  8. 为什么说易语言不如c语言_不说语言? 如何使用Google翻译

    为什么说易语言不如c语言 The Google Translate app and Google Assistant can translate text or images in dozens of ...

  9. c语言调易语言dll,易语言DLL来返回文本供其他语言调用

    部分简介 在使用易语言与其他语言配合编程中,有时需要使用易语言的DLL来返回文本型的值,但是由于易语言本身文本型数据不能直接返回让其他语言使用(直接使用多次会崩溃).所以这里要使用一种转换的方式,是其 ...

最新文章

  1. 个人所得税计算器2016 by Jacksile
  2. R:关系型数据库管理
  3. UVA679 小球下落 Dropping Balls(二叉树的编号)
  4. 8G+256G固态笔记本,结合这款IDEA插件,写代码飞起!
  5. nyist-508(余数求和)
  6. 记录部署hue在k8s上
  7. Mybatis学习笔记13 - 动态sql之set标签
  8. Java ==和Equals方法的比较
  9. 何凯明最新一作MAE解读系列2之代码实践
  10. Youtube——如何将视频中的英文字幕转换成中文字幕
  11. 创业找市场——从逛街开始(转)
  12. JAVA基础(43)彻底理解cookie、session、token
  13. 今天你够“敏捷”吗?
  14. 【对流氓软件说ByeBye】—— 恶意软件删除工具
  15. 如果要用运营的姿势,发支付宝红包
  16. 【机器学习】一文详解GBDT、Xgboost、Boosting与Bagging之间的区别
  17. 0基础入门Linux运维,有哪些学习技巧?
  18. 接触vsto,开发word插件的利器
  19. Java项目:基于ssm框架个人博客系统多用户(计算机毕业设计)
  20. 数据库存储过程简单介绍

热门文章

  1. 超验骇客1280高清科幻大片
  2. 操作无法完成(0x000006ba)。本地后台打印程序服务没有运行。请重新启动后台打印程序或重新启动计算机。
  3. Foxmail 账号无法登陆、无法收取邮件等问题
  4. 风险提醒之Oracle RAC高可用失效
  5. owa for android 下载,Mobile Access for Outlook OWA
  6. HijackThis日志细解【简明教程增强版】(三)
  7. errdisabled 障碍处理
  8. 解惑:心智模式决定你的一生
  9. 全部开放基金接口调用代码
  10. 怎么查看计算机显卡类型,如何查看电脑显卡支持类型?查看显卡支持类型方法...