在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会有点/礼貌地留下理由)
答案 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)做到了这一点,希望它将值保持为正常(有限精度)整数。事实证明它并不重要,单个减法也可以正常工作。