转换为C代码的python

时间:2018-02-23 15:43:16

标签: python c django

在web后端(django)项目中使用时,我获得了以下C代码片段:

/* architecture is x86_64, strings are ASCII */
/* example output:
 * 7KL753WG => 680403628
 * 043NM2B6 => 517135930
 * 7B6ISP72 => 058511020
 */

char buf[33];
char*  unlock(char *s) {
  char  token[9];
  char  scrambled[9];
  long long *ltoken;

  strncpy(token,s,9);

  ltoken=(long long *)token;

  *ltoken *= 610;
  *ltoken ^= 0x5A5A5A5A ;

  buf[32]='\0';

  int i = snprintf(buf,32,"%lld",*ltoken);
  return buf+i-9;
}

在单词中:取一个8 char ascii字符串,将其位模式用作long,应用几个计算,取结果的最后9位数。

由于显而易见的原因,我讨厌调用二进制文件,并希望将其转换为python 2.7。另一方面,我尝试了明显的(struct.pack,struct.unpack)和异国情调(ctypes),没有任何乐趣。我认为ctypes是要走的路,但我从未使用它,所以我的尝试基本上用棍子戳它。 的修改: 这是我试过的。 打包/解包(只是错误):

from sys import stderr
import struct

expected= {
    '7KL753WG' : "680403628",
    '043NM2B6': "517135930",
    '7B6ISP72' : "058511020",
    }

def trans(ary):
    ret=[]
    for v in ary: ret.append((v*610)^0x5A5A5A5A)
    return ret

def func(token):
    Q=struct.unpack('Q',token)
    I=struct.unpack('II',token)
    S=struct.unpack('hhhh',token)

    print("(Q/L) %s => %s"%(token,','.join(map(str,trans(Q)))))
    print("(I)   %s => %s"%(token,','.join(map(str,trans(I)))))
    print("(S)   %s => %s"%(token,','.join(map(str,trans(S)))))
    print("==== expected last 9 digits as %s ====="%(expected.get(token,"NOT FOUND"),))


for s in ('7KL753WG','043NM2B6', '7B6ISP72'):  func(s)

ctypes:很有希望,但我找不到在ctypes中进行数学运算的方法:

from ctypes import *

aa="043NM2B6"
pb=create_string_buffer(aa)
#pl.contents becomes c_long(3909742734116860976) (which is right)
pl=cast(pb,POINTER(c_longlong))
# this is an error
result=(pl.contents * c_longlong(610)) ^ c_longlong(0x5A5A5A5A)

编辑由于有澄清要求:

我想编写一个python函数,当在同一个输入上调用时,它给出了与上面C代码相同的结果。因为C代码本身依赖于字符串和长整数的内部数据表示(根据ltoken =(long long *)令牌),到目前为止我还没有找到办法,而且我的几次尝试都没有结果。

我不想在我的python代码中调用system()或道德等价物,因为它会使整个事物变得更脆弱并增加依赖性。

因此问题。

(是的,我知道算法本身并不漂亮 - 幸运的是不是我写的东西)

TIA

编辑:(downvoters会有点/礼貌地留下理由)

1 个答案:

答案 0 :(得分:2)

如果python 2.7 int类型是64位,那么

import sys
from struct import pack, unpack

def unlock(s):
    val = unpack('<q', ('%s\0\0\0\0\0\0\0\0' % s)[0:8])[0]
    val = val * 610
    val = val ^ 0x5a5a5a5a
    val = val & 18446744073709551615
    if val > 9223372036854775807:
        val = val - 18446744073709551616
    return ('%d' % val)[-9:]

但是哦,男孩,这是一个可怕的黑客......

如果有人问,我没有写上面的代码,好吗?

在上面的代码中,(%s\0\0\0\0\0\0\0\0' % s)[0:8]最多从s获取8个第一个字符,并用零填充到8个字符。 unpack('<q', ...)将其转换为8字节(64位带符号)整数。结果是只有一个元素的元组,所以最终的&#39; [0]&#39;只需抓取64位结果。

其余的代码很难理解,除非你知道Python支持正常整数(至少32位)和"long integers"具有无限精度(大小)。

基本上,乘以610可能会将val变为无限精度&#34;长整数&#34;。为了限制为64位精度,我们使用2 64 -1进行二进制AND。

原始代码依赖于64位签名的整数,但LLONG_MAX == 9223372036854775807。这意味着如果我们的值大于9223372036854775807,我们需要从中减去2 64 。最初,我在两个部分(由2 63 = 9223372036854775808)做到了这一点,希望它将值保持为正常(有限精度)整数。事实证明它并不重要,单个减法也可以正常工作。