|
[原创]Python和《加密与解密》
VB P-code 分析工具:x64dbg + WKTVBDE P-code 分析遇到的问题: 40E416 DivVar var_BC 伪指令除法运算一开始未能理解逻辑,导致浪费大量时间,除法指令分析如下: x32dbg 下断点: bph 40E416, rw ;breakpoint hardware F9 运行,来到断点处,跟踪分析 DivVar var_BC 除法运算 741BDEE5 | mov al,byte ptr ds:[esi] | 0xFB 741BDEE7 | inc esi | 741BDEE8 | jmp dword ptr ds:[eax*4+741BED94] | 741BEC87 | xor eax,eax | 0xBC 741BEC89 | mov al,byte ptr ds:[esi] | 0x74 741BEC8B | inc esi | 741BEC8C | jmp dword ptr ds:[eax*4+741BF194] | 741BE0C3 | lea ebx,dword ptr ds:[<__vbaVarDiv>] | 741BE0C9 | jmp msvbvm50.741BE0E1 | 741BE0E1 | movsx edi,word ptr ds:[esi] | 741BE0E4 | add edi,ebp | 741BE0E6 | push edi | 0x12->len(S1) ; stack ;; 0019F1E4 03 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 ................ ;len(S1) ;; 0019F1F4 05 00 00 00 00 00 00 00 50 45 52 54 FB 21 09 40 ........PERTû!.@ ;pi = 3.141592654 ;; 0019F20C 08 00 00 00 00 00 00 00 04 22 48 00 00 00 00 00 ........."H..... ; ;;; pointer -> u"105112101100105121" 741BE0E7 | call ebx | ebx:__vbaVarDiv ; 0019F1E4 05 00 00 00 00 00 00 00 8E 1B 63 C7 84 B7 5D 43 ..........cÇ.·]C eax ;; 3.3458220933344824e+16 == 105112101100105121 / pi 741BE0E9 | push edi | 741BE0EA | xor eax,eax | #!/usr/bin/env python3 # coding: utf-8 # 2016-10-23 23:39:10 P217 ch8.3.4 VB P-code 攻击实战 import binascii import struct # f('50 45 52 54 FB 21 09 40') -> (3.141592654,) 用来翻译内存中的 double 值,'<' 表示 little-endian f = lambda data, fmt='<d': struct.unpack(fmt, binascii.unhexlify(data.replace(' ', ''))) def verify(name, serial): ''' VB P-code 逻辑再现 name = cyclotron => S1 = 9912199108111116114111110 ''' name = name.strip() serial = serial.strip() assert name != '' and serial != '', 'Both name and serial need!' S1 = int(''.join([str(ord(i)) for i in name])) pi = 3.141592654 while len(str(S1)) > 9: S1 = int(S1 / pi) S2 = (S1 ^ 0x30f85678) - 0xd8b3 if len(name) == int(serial) - S2: print('The register is right!') else: print('Try again!') def genSerial(name): name = name.strip() assert len(name) != 0 S1 = int(''.join([str(ord(i)) for i in name])) while len(str(S1)) > 9: S1 = int(S1 / 3.141592654) S2 = (S1 ^ 0x30f85678) - 0xd8b3 return len(name) + S2 if __name__ == '__main__': verify('cyclotron', '667574641') print(genSerial('cyclotron')) |
|
[注意]《加密与解密(第三版)》勘误(2009.9.9更新)
P214 L35 D13 原文:40E32E EqStr ;序列号是否大于5位 更正:40E32E EqStr ;序列号是否为空 说明:目标程序无法触发 “小于5位”序列号的条件,逆向调试也未发现5位的判断 P215 L20 D13 原文: 40E3A6 VCallHresult 取用户名长度 更正:40E3A6 VCallHresult 取用户名 |
|
[讨论]关于《加密与解密》第四版进展(2016.1.1更新)
《加密与解密》第三版 p206 8.3.1 VB P-code 虚拟机与伪代码章节,感觉章节内容安排不合理,存在以下问题: 1.win10 环境下 使用OD2.01 跟着书上做,很难从 P206 底部代码 rtcInputBox 断点处,使用 ctrl + F9跟踪到书上相同的代码处,只有相似代码(使用程序:\chap08\8.3 伪编译(Pcode)\8.3.1 虚拟机与伪代码\2.虚拟机实例分析\VBP-code-Ex.exe) 2.p207 页底部代码也能跟踪到,但从上一步到这一步,VB 汇编代码跳转多次,不明白如何跟踪到此处的?如果不理解,换一个程序,也就不会了,这样书上的文章不是没用了? 3. 加密与解密光盘资料 “加密与解密_光盘\chap08\资料\VB结构文档\VB结构文档\P-code资料” 提供了P-code说明资源,建议书中加入说明,如何使用这些资料; |
|
[原创]Python和《加密与解密》
国庆结束,继续逆向学习,开始“第4篇 语言和平台篇”学习了,本来犹豫要不要学下 Delphi 再开始,加密与解密没有提到,暂且不学,以下是一点学习方法。 Delphi 反汇编方法梳理: 1.使用DeDe 定位需要逆向的代码 2.使用 IDA + DeDe 进行静态分析 3.使用 OD 进行动态分析 4.逆向分析时,使用 py 代码实时梳理代码逻辑 #!/usr/bin/env python3 # coding: utf-8 # 2016-10-08 17:17:02 # p196 ch07 DE_Delphi_6.exe def register(name, serial): assert name != '' assert len(name) >= 4 hex_name = '' for i in name: hex_name += hex(ord(i))[2:] if serial == hex_name: print('Success!') else: print('Error!') def gen_serial(name): return ''.join([hex(ord(i))[2:] for i in name]) if __name__ == '__main__': name = input('name: ').strip() serial = input('serial: ').strip() register('ipediy', '1234') print(gen_serial('ipediy')) |
|
[原创]Python和《加密与解密》
学习《C语言程序设计》 花约 200小时,为进一步逆向学习打基础 |
|
[讨论]关于《加密与解密》第四版进展(2016.1.1更新)
看到结构调整更合理了,很好的书,赞!另外,在《加密与解密 第三篇 解密篇》 书写错误较多,组织结构不大适合无密码学基础的人学习,比如说 RSA 中 欧拉函数,只需要稍微补充说明一下,phi(n) 实际意义是什么,读者会更容易理解或回忆加密基础,还有ECDSA 算法,P184 说明了 NIST 推荐参数 Curve P-192, 但ECC 椭圆曲线描述都已经很模糊了,这些从天而降的参数,更是不明就里,而实际中的ECC 算法,涉及到点压缩、二次剩余相关概念,完整描述确实很费书页,不过简单完整的介绍一下,让读都自己找方向深入还是可以的,在此推荐两本书《密码学原理与实践 第2版 道格拉斯 R. 斯廷森,冯登国 》《密码编码学与网络安全》,第一本书,组织,举例,都很详实。 还有一点,想问下 Linux 平台逆向和汇编相关知识,可否在新书中介绍下呢? Android 逆向呢? 不一定出新章节,简单介绍和指引下方向和趋势也是极好的,谢谢! |
|
[原创]Python和《加密与解密》
注册机python3.5 代码 #!/usr/bin/env python3 # coding: utf-8 # 2016.6.30 ''' blowfish setup from pypi https://pypi.python.org/pypi/blowfish/0.6.1#downloads ''' from ctypes import * from os import urandom from binascii import unhexlify, hexlify import blowfish def endian32(uint32): return [uint32 >> (8 * (3 - i&3)) & 0xff for i in range(3, -1, -1)] def vol(path='c:\\'): path_name = c_char_p(path.encode('ascii')) vol_name_buf =c_char_p(b'the-volume-name-buffer') vol_name_size=c_int(20) serial_num = c_long(0) max_comp_len = c_long(0) file_sys_flag = c_long(0) file_sys_name_buf= c_void_p(0) file_sys_name_size = c_long(0) windll.kernel32.GetVolumeInformationA( path_name, # _In_opt_ LPCTSTR lpRootPathName vol_name_buf, # _Out_opt_ LPTSTR lpVolumeNameBuffer vol_name_size, # _In_ DWORD nVolumeNameSize byref(serial_num), # _Out_opt_ LPDWORD lpVolumeSerialNumber byref(max_comp_len), # _Out_opt_ LPDWORD lpMaximumComponentLength byref(file_sys_flag), # _Out_opt_ LPDWORD lpFileSystemFlags file_sys_name_buf, # _Out_opt_ LPTSTR lpFileSystemNameBuffer file_sys_name_size) # _In_ DWORD nFileSystemNameSize return endian32(serial_num.value) def byte_xor(x, y): # x,y -> bytes list assert type(x) == type(y) == list assert len(x) == len(y) z = [] for i in range(len(x)): z.append(x[i] ^ y[i]) return z def test(): serial = '251C2FC294A937D8F573A136A0CE910E' key = unhexlify(serial[0: 16]) # key -> big-endian cipher = blowfish.Cipher(key, byte_order="little") x = unhexlify(serial[16: 32]) # big-endian plaintext = cipher.decrypt_block(x) XL = list(plaintext[0: 4]) XR = list(plaintext[4: 8]) k1 = byte_xor(XL, XR) assert vol() == k1 def get_serial(): XL = list(urandom(4)) XR = byte_xor(XL, vol()) x = bytes(XL) + bytes(XR) key = urandom(8) # key size can range(4, 56) bytes cipher = blowfish.Cipher(key, byte_order="little") ciphertext = cipher.encrypt_block(x) serial = hexlify(key) + hexlify(ciphertext) return serial.decode('ascii').upper() if __name__ == '__main__': # import ipdb # ipdb.set_trace() while True: print(get_serial()) input() ---------------------------------------------------- python辅助 逆向分析过程 # ollydbg 内存数据分析函数,注意,OD 中的数据以 little-endian 形式显示 s = '''20 DE 87 7D AB 1F 7A 0E''' def rev(s): s = s.split() s = ['0x' + i for i in s] s = str(s).replace("'", '') print(s) ################################ serial = '0123456789abcdef0123456789abcdef' assert len(serial) == 32 from binascii import unhexlify xSeri = list(unhexlify(serial)) # 01 23 45 67 89 AB CD EF 01 23 45 67 89 AB CD EF key = xSeri[0: 8] ctx = [] # ctx = BlowFish_init(key) # ctx[0: 72] -> [P1, P18]; ctx[72: 4168] -> S-box ctx = [0x03, 0x8D, 0x55, 0x02, 0x5B, 0xE0, 0xD2, 0x8B, 0xEA, 0x00, 0x0C, 0xE6, 0xEF, 0x70, 0xC2, 0x95, 0x55, 0x75, 0x3C, 0xF0, 0xC4, 0x57, 0xBA, 0x58, 0x41, 0x1A, 0x21, 0xA6, 0x6E, 0x51, 0x31, 0xE7, 0xC7, .... 0x18, 0x8A, 0x50, 0x53, 0x79, 0xA1, 0x36, 0x31, 0x4F, 0x06, 0x3A, 0x6E, 0xE4, 0xB3] XL = xSeri[8: 12] XR = xSeri[12: 16] XL, XR = BlowFish_decrypt(ctx, XL, XR) assert XL == [0x20, 0xDE, 0x87, 0x7D] assert XR == [0xAB, 0x1F, 0x7A, 0x0E] k1 = xor(XL, XR) assert k1 == list(reversed([0x73, 0xFD, 0xC1, 0x8B])) volC = GetVolumeInformationA(r"c:\\") assert volC == [0x0C, 0x23, 0xED, 0xBC] # 验证 assert k1 == volC |
|
[原创]Python和《加密与解密》
# 注册机实现遇到的问题:key扩展算法中, 16位数据 BE LE 转换问题 s2 = 0x16FD s1 = 0xEC1D assert ((s1 << 9) ^ (s2 >> 7)) & 0xFFFF == 0x3A2D # 0019F260 2D 3A -: # 实际值应为 s2 = 0xFD16 s1 = 0x1DEC assert ((s1 << 9) ^ (s2 >> 7)) & 0xFFFF == 0xD9FA 解决办法是引入 endian16函数 注册机源代码 #!/usr/bin/env python3 # coding: utf-8 # 2016-06-28 18:33:23 from hashlib import sha1 from ctypes import * import cryptocommon from ideacipher import * def endian32(uint32): return [uint32 >> (8 * (3 - i&3)) & 0xff for i in range(3, -1, -1)] def endian16(x): # x -> bytes list lst = [] for i in range(0, len(x), 2): lst.extend((x[i+1], x[i])) return lst def vol(path='c:\\'): path_name = c_char_p(path.encode('ascii')) vol_name_buf =c_char_p(b'the-volume-name-buffer') vol_name_size=c_int(20) serial_num = c_long(0) max_comp_len = c_long(0) file_sys_flag = c_long(0) file_sys_name_buf= c_void_p(0) file_sys_name_size = c_long(0) windll.kernel32.GetVolumeInformationA( path_name, # _In_opt_ LPCTSTR lpRootPathName vol_name_buf, # _Out_opt_ LPTSTR lpVolumeNameBuffer vol_name_size, # _In_ DWORD nVolumeNameSize byref(serial_num), # _Out_opt_ LPDWORD lpVolumeSerialNumber byref(max_comp_len), # _Out_opt_ LPDWORD lpMaximumComponentLength byref(file_sys_flag), # _Out_opt_ LPDWORD lpFileSystemFlags file_sys_name_buf, # _Out_opt_ LPTSTR lpFileSystemNameBuffer file_sys_name_size) # _In_ DWORD nFileSystemNameSize return endian32(serial_num.value) def get_serial(name): m = sha1() m.update(name.encode('ascii')) h = list(m.digest()) volC = vol() c = h[-4:] + volC c = endian16(c) key = endian16(h[0: 16]) serial = decrypt(c, key) return ''.join([hex(i)[2:].zfill(2) for i in serial]).upper() def test(): ''' name = 'ipediy' serial = 'E3892679EAE3D708' hash = '9d631decfd16b5238b83be269bd65fe5649367c5' ''' h = [0x9D, 0x63, 0x1D, 0xEC, 0xFD, 0x16, 0xB5, 0x23, 0x8B, 0x83, 0xBE, 0x26, 0x9B, 0xD6, 0x5F, 0xE5, 0x64, 0x93, 0x67, 0xC5] volC = vol() c2 = h[-4:] + volC # key 需要 16位 BE2LE 转换 key = endian16(h[0: 16]) # only for my pc assert c2 == [0x64, 0x93, 0x67, 0xC5, 0x0C, 0x23, 0xED, 0xBC] # encrypt function test, 序列号不用转换BE2LE test = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF] c = encrypt(test, key, printdebug=False) # function bug # 加密后的数据,需要 BE2LE 转换 assert c == endian16([0xDC, 0x70, 0xB9, 0x9D, 0xAA, 0xD9, 0x92, 0x5E]) # decrypt function test wSN = [0xE3, 0x89, 0x26, 0x79, 0xEA, 0xE3, 0xD7, 0x08] c2 = endian16(c2) cc = decrypt(c2, key) assert cc == wSN if __name__ == '__main__': print(get_serial('ipediy')) |
|
[原创]Python和《加密与解密》
IDEAKeyGenMe.c 密钥扩展算法存在算法不正确的问题,导致使用标准算法实现注册机的时候加密解密结果不一致 key算法基本原理: 子密钥的生成 IDEA共使用52个16位子密钥,由输入的128位密钥生成,算法如下: • key(128bit)分成8个16位分组,并作为前8个子密钥;(unsigned) • key(128bit)循环左移25位,生成新的8个子密钥; python: hex(((n << 25) | (n >> 103)) & (2**128 -1)) IDEAKeyGenMe.c key function void key(unsigned short uskey[9], unsigned short Z[7][10]) { unsigned short S[54]; int i, j, r; for(i = 1; i<9; i++) S[i-1] = uskey[i]; /* shifts S[i] 内存中128bit 数据全体循环左右25位*/ for(i=8; i<54; i++) { if((i+2)%8 == 0) /* 14,22,30,38,46 mod 8 = 6 剩余类[6] */ S[i] = ((S[i-7]<<9) ^ (S[i-14]>>7)) & one; // 剩余类[7] ^ 剩余[0] else if((i+1)%8==0) /* 15,23,31,39,47*/ S[i] = ((S[i-15]<<9) ^ (S[i-14]>>7)) & one; //剩余类[0] ^ 剩余类[1] else S[i] = ((S[i-7]<<9) ^ (S[i-6]>>7)) & one; } /* get subkeys */ for(r = 1; r <= round + 1 ; r++) for(j = 1; j < 7; j++) Z[j][r]=S[6 * (r - 1) + j - 1]; } 逆向分析结果 '''wiDealKey 18B 00 00 9D 63 1D EC FD 16 B5 23 8B 83 BE 26 9B D6 5F E5 ''' '''wSubkey 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 9D 63 9B D6 AD 7D FB 9A 35 59 1D E9 D2 6F DF A2 45 07 00 00 1D EC 5F E5 CA 37 6F 5A B4 F6 1C AC 58 3B 76 A4 48 BF 00 00 FD 16 2D 3A C7 BE 7D 95 2A DF BE 69 B2 38 71 B0 60 ED 00 00 B5 23 47 FA D8 3B 77 8E 1C FB F6 55 ED 6B D7 64 C9 E2 00 00 8B 83 07 6B D6 8E 74 B0 60 EF DE 39 73 EC D3 DA B5 AF 00 00 BE 26 4D 16 2C 0E F4 5B B7 E8 D1 C1 83 BD AB 7C F9 A6 ''' # python code 对内存数据格式化处理 '''wSubkey 每列表示 [Z1, Z2...Z6] ['9D63', '9BD6', 'AD7D', 'FB9A', '3559', '1DE9', 'D26F', 'DFA2', '4507', '0000'] ['1DEC', '5FE5', 'CA37', '6F5A', 'B4F6', '1CAC', '583B', '76A4', '48BF', '0000'] ['FD16', '2D3A', 'C7BE', '7D95', '2ADF', 'BE69', 'B238', '71B0', '60ED', '0000'] ['B523', '47FA', 'D83B', '778E', '1CFB', 'F655', 'ED6B', 'D764', 'C9E2', '0000'] ['8B83', '076B', 'D68E', '74B0', '60EF', 'DE39', '73EC', 'D3DA', 'B5AF', '0000'] ['BE26', '4D16', '2C0E', 'F45B', 'B7E8', 'D1C1', '83BD', 'AB7C', 'F9A6'] ''' 正确key 值 # 正确生成 wSubkey,每行数据由前一行 ROL25 生成 ''' ['9D63', '1DEC', 'FD16', 'B523', '8B83', 'BE26', '9BD6', '5FE5'] ['D9FA', '2D6A', '4717', '77C', '4D37', 'ACBF', 'CB3A', 'C63B'] ['D48E', '2E0E', 'F89A', '6F59', '7F96', '758C', '77B3', 'F45A'] ['1DF1', '34DE', 'B2FF', '2CEB', '18EF', '67E8', 'B5A9', '1C5C'] ['BD65', 'FE59', 'D631', 'DECF', 'D16B', '5238', 'B83B', 'E269'] ['B3AC', '63BD', '9FA2', 'D6A4', '7170', '77C4', 'D37A', 'CBFC'] ['7B3F', '45AD', '48E2', 'E0EF'] # 正确 wSubkey, 每行表示[Z1, Z2....Z6] ''' ['0x9d63', '0x1dec', '0xfd16', '0xb523', '0x8b83', '0xbe26'] ['0x9bd6', '0x5fe5', '0xd9fa', '0x2d6a', '0x4717', '0x77c'] ['0x4d37', '0xacbf', '0xcb3a', '0xc63b', '0xd48e', '0x2e0e'] ['0xf89a', '0x6f59', '0x7f96', '0x758c', '0x77b3', '0xf45a'] ['0x1df1', '0x34de', '0xb2ff', '0x2ceb', '0x18ef', '0x67e8'] ['0xb5a9', '0x1c5c', '0xbd65', '0xfe59', '0xd631', '0xdecf'] ['0xd16b', '0x5238', '0xb83b', '0xe269', '0xb3ac', '0x63bd'] ['0x9fa2', '0xd6a4', '0x7170', '0x77c4', '0xd37a', '0xcbfc'] ['0x7b3f', '0x45ad', '0x48e2', '0xe0ef'] python 代码验证 sha1_hash = '9d631decfd16b5238b83be269bd65fe5649367c5' # hash for 'ipediy' n = int(m.hexdigest()[0: 32], 16) #取hash值前16字节,共128bit 来做ROL25循环移位操作 assert hex(((n << 25) | (n >> 103)) & (2**128 -1)) == '0xd9fa2d6a4717077c4d37acbfcb3ac63b' # 验证结果 key 算法不正确 |
|
[原创]Python和《加密与解密》
P191 页涉及到一个大数字串到内存 0x7fffffff 进制大数转换,这里用 python实现转换 #!/usr/bin/env python # coding: utf-8 # 2016.6.17 10:00 BE2LE = lambda x:\ ((x >> 24) & 0xff) |\ ((x >> 16) & 0xff) << 8 |\ ((x >> 8) & 0xff) << 16 |\ (x&0xff) << 24 def fgint(data): assert type(data) is int _ord = 0x80000000 # 2^31 (x, r) = divmod(data, _ord) rLst = [r] while x != 0: (x, r) = divmod(x, _ord) rLst.append(r) rLst.insert(0, len(rLst)) rLst = [BE2LE(r) for r in rLst] rLst = [hex(r)[2:].zfill(8).upper() for r in rLst] return ' '.join(rLst) if __name__ == '__main__': s = "322334816268407046326335149654703840583" print(fgint(int(s))) 输出结果 05000000 479D4F54 21A9670A 84481207 796BFB13 0F000000 与书中数据相同 Address Hex dump ASCII 02395254 05 00 00 00|47 9D 4F 54|21 A9 67 0A|84 48 12 07| GOT!©g „H 02395264 79 6B FB 13|0F 00 00 00|16 00 00 00|84 41 39 02| ykû „A9 |
|
[原创]Python和《加密与解密》
#!/usr/bin/env python # coding: utf-8 # 2016.5.27 19:31 from random import randint, getrandbits from xmath import mulinv from isprime import genprime def modular_sqrt(a, p): """ Find a quadratic residue (mod p) of 'a'. p must be an odd prime. Solve the congruence of the form: x^2 = a (mod p) And returns x. Note that p - x is also a root. 0 is returned is no square root exists for these a and p. The Tonelli-Shanks algorithm is used (except for some simple cases in which the solution is known from an identity). This algorithm runs in polynomial time (unless the generalized Riemann hypothesis is false). """ # Simple cases # if legendre_symbol(a, p) != 1: return 0 elif a == 0: return 0 elif p == 2: return p elif p % 4 == 3: return pow(a, (p + 1) // 4, p) # Partition p-1 to s * 2^e for an odd s (i.e. # reduce all the powers of 2 from p-1) # s = p - 1 e = 0 while s % 2 == 0: s //= 2 e += 1 # Find some 'n' with a legendre symbol n|p = -1. # Shouldn't take long. # n = 2 while legendre_symbol(n, p) != -1: n += 1 # Here be dragons! # Read the paper "Square roots from 1; 24, 51, # 10 to Dan Shanks" by Ezra Brown for more # information # # x is a guess of the square root that gets better # with each iteration. # b is the "fudge factor" - by how much we're off # with the guess. The invariant x^2 = ab (mod p) # is maintained throughout the loop. # g is used for successive powers of n to update # both a and b # r is the exponent - decreases with each update # x = pow(a, (s + 1) // 2, p) b = pow(a, s, p) g = pow(n, s, p) r = e while True: t = b m = 0 for m in range(r): if t == 1: break t = pow(t, 2, p) if m == 0: return x gs = pow(g, 2 ** (r - m - 1), p) # wiki var(b) g = (gs * gs) % p # wiki var(c`) x = (x * gs) % p # x^2 = a*t mod p, if t == 1 then x is square root b = (b * g) % p # wiki var(t`) r = m # x -> wiki var(r`), r -> wiki var(j) def legendre_symbol(a, p): """ Compute the Legendre symbol a|p using Euler's criterion. p is a prime, a is relatively prime to p (if p divides a, then a|p = 0) Returns 1 if a has a square root modulo p, -1 otherwise. """ ls = pow(a, (p - 1) // 2, p) return -1 if ls == p - 1 else ls def nfa(n): size = n.bit_length() o = [(n >> i) & 1 for i in range(size - 1, -1, -1)] o.insert(0, 0) j = 0 for i in range(size - 1, -1, -1): if o[i] == o[i + 1] == 1: j += 1 continue if j != 0: o[i: i + j + 2] = [1] + [0] * j + [-1] j = 0 return o class ECC(object): ''' ECC(size [, p, B, A]) ECC(0, p [, B, A]) p.bit_length() == 256, (p, B) will be randomly generated >>> c = ECC(size = 256) # p.bit_length() == 256, (p, B) will be randomly generated A = -3 by default, also you can specify another value >>> c = ECC(p=23, B=17, A=9) # y^2 = x^3 + Ax + B (mod p) >>> P = (16, 5) Caution: Not all points(x, y) on elliptic curves, x belogs to Zp >>> Q = (20, c.gety(20)[0]) # gety(x) -> (y1, y2) or False >>> Q == (20, 3) >>> (16, 18) == c.eccplus(P, Q) >>> (7, 3) == c.eccmul(6, P) ''' def __init__(self, size=0, p=0, B=0, A=-3): assert (size == p == 0) is False, 'ECC(size, p, B, A) -> size must give' self.size = size # size bits self.p = p self.B = B if self.p == 0: while True: p = genprime(size) if p % 4 == 3: self.p = p break self.A = A % p if self.B == 0: self.getab() else: self.checkab() if self.size == 0: self.size = p.bit_length() def getab(self): A = self.A p = self.p while True: # a, b belong to Z_p B = randint(0, p) # be confirm as a abelian group if (4 * A ** 3 + 27 * B ** 2) % p != 0: self.B = B break def checkab(self): if (4 * self.A ** 3 + 27 * self.B ** 2) % self.p == 0: raise ValueError('A, B is invalid, try another!') def getdistlaw(self): ''' get quadratic residue distribution law of mod(p)''' p = self.p if p == 2: return 0 return (p - 1) // 2 def gety(self, x): A = self.A B = self.B p = self.p a = (x ** 3 + A * x + B) % p y1 = modular_sqrt(a, p) if y1 == 0: return False y2 = -y1 % p return y1, y2 def eccplus(self, P, Q): try: assert type(P) is type(Q) is tuple and len(P) == len(Q) == 2 except AssertionError: print('eccplus(P, Q) -> P or Q must be (x, y) tuple') return False p = self.p x1, y1 = P x2, y2 = Q if P == (0, 0) or Q == (0, 0): return (x1 + x2, y1 + y2) if x1 == x2 and y1 == -y2 % p: return 0, 0 if P != Q: _lambda = ((y2 - y1) * mulinv(x2 - x1, p)) % p else: _lambda = (3 * x1 * x1 + self.A) * mulinv(2 * y1, p) % p x3 = (_lambda * _lambda - x1 - x2) % p y3 = (_lambda * (x1 - x3) - y1) % p return (x3, y3) def eccmul2(self, n, P): # n * P try: assert type(n) is int and type(P) is tuple and len(P) == 2 except AssertionError: print('eccmul(n, P), n must be int, P must be (x, y) tuple') return False if n <= 0: return 0, 0 x3, y3 = P for i in range(n - 1): x3, y3 = self.eccplus(P , (x3, y3)) return x3, y3 # Q = nP def eccmul(self, n, P): # double_and algorithm o = nfa(n) Q = (0, 0) for c in o: Q = self.eccplus(Q, Q) if c == 1: Q = self.eccplus(Q, P) elif c == -1: Q = self.eccplus(Q, (P[0], -P[1])) return Q class ECIES(ECC): def __init__(self, args=None, A=0, B=0, p=0): ''' o = ECIES() # ECIES(size=160) o = ECIES(224) # ECIES(size=224) init for enrypt o = ECIES(prikey, pubkey) # init for decrypt o = ECIES(pubkey) # init for encrypt o = ECIES(prikey) # init for decrypt ''' if A & B & p != 0: super(ECIES, self).__init__(A=A, B=B, p=p) self.init() elif args == None: super(ECIES, self).__init__(size=160) self.init() elif type(args) == int: super(ECIES, self).__init__(size=args) elif len(args) == 2 and type(args[1]) == int: prikey = args p, A, B, P = prikey[0] self.d = prikey[1] super(ECIES, self).__init__(p=p, A=A, B=B) elif len(args) == 2 and len(args[1]) == 2 and type(args[1][1]) == int: pubkey = args p, A, B, P = pubkey[0] super(ECIES, self).__init__(p=p, A=A, B=B) self.Q = pubkey[1] elif len(args) == 2 and len(args[1]) == 2 and \ type(args[1][1]) == tuple and \ len(args[1][1]) == 2: prikey, pubkey = args p, A, B, P = pubkey[0] super(ECIES, self).__init__(p=p, A=A, B=B) self.Q = pubkey[1] self.d = prikey[1] else: raise ValueError('ECIES({}'.format(args)) def init(self, size=160): # size = 112, 160, 224, 256, 384, 512 # RSA 512,1024,2048,3072,7680,15360 self.P = self.getP() self.prikey, self.pubkey = self.getkey(self.P) self.d = self.prikey[1] self.Q = self.pubkey[1] def getP(self): while True: x = randint(0, self.p) y = self.gety(x) if y: return x, y[0] def getkey(self, P): d = getrandbits(self.size) % self.p if d == 0: return self.getkey(P) Q = self.eccmul(d, P) common = (self.p, self.A, self.B, P) return (common, d), (common, Q) # prikey, pubkey def pointCompress(self, P): return (P[0], P[1] % 2) def pointDecompress(self, P): x, r = P y1, y2 = self.gety(x) return (x, y1) if y1 % 2 == r else (x, y2) def crypto(self, x): ''' ECIES Algorithm @x: int, 0 < x < self.p, plaintext ''' assert 0 < x < self.p, 'Encryption scope is too large!(x, p) = \ ({}, {})'.format(x, self.p) k = getrandbits(self.size) % self.p if k == 0: return self.crypto(x) kP = self.eccmul(k, self.P) kQ = self.eccmul(k, self.Q) y1 = self.pointCompress(kP) y2 = x * kQ[0] % self.p return (y1, y2) def decrypt(self, y): assert type(y) == tuple and len(y) == 2 and \ type(y[0]) == tuple and len(y[0]) == 2 and \ type(y[1]) == int y1, y2 = y kP = self.pointDecompress(y1) kQ = self.eccmul(self.d, kP) x0 = kQ[0] x = y2 * mulinv(x0, self.p) % self.p return x class ECDSA(ECC): def __init__(self,args=None ,q=0, a=-3, b=0, P=None, n=0, size=192): self.P = P self.d = 0 self.n = n if args == None: b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 q = 0xfffffffffffffffffffffffffffffffeffffffffffffffff self.n = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831 super(ECDSA, self).__init__(p=q, B=b, size=size) elif len(args) == 2 and type(args[1]) == int: p, A, B, self.P, self.n = args[0] self.d = args[1] super(ECDSA, self).__init__(p=p, A=A, B=B) elif len(args) == 2 and len(args[1]) == 2: p, A, B, self.P, self.n = args[0] self.Q = args[1] super(ECDSA, self).__init__(p=p, A=A, B=B) if self.P == None: self.P = self.getP() # be careful def getP(self): while True: x = randint(0, self.p) y = self.gety(x) if y: return x, y[0] def getkey(self): while self.d == 0: self.d = getrandbits(self.size) % self.p self.Q = self.eccmul(self.d, self.P) self.common = (self.p, self.A, self.B, self.P, self.n) self.prikey = (self.common, self.d) # prikey def sha(self, x): from hashlib import sha1 sha = sha1() if type(x) == str: x = x.encode('ascii') sha.update(x) e = int(sha.hexdigest(), 16) % self.n return e def sign(self, x): self.getkey() k = getrandbits(self.size) % self.n kP = self.eccmul(k, self.P) r = kP[0] % self.n if (k & r) == 0: return self.sign(x) e = self.sha(x) s = (mulinv(k, self.n) * (e + self.d * r)) % self.n if s == 0: return self.sign(x) pubkey = (self.common, self.Q) return pubkey, (r, s) def verify(self, x, r, s): if not (1 <= r <= self.n -1 and 1 <= s <= self.n -1): return False e = self.sha(x) inv_s = mulinv(s, self.n) u1 = (e * inv_s) % self.n u2 = (r * inv_s) % self.n u1P = self.eccmul(u1, self.P) # bug: (r * inv_s % n) * Q != (r * inv_s * d % n) * P # debug: self.n must be carefully choose u2Q = self.eccmul(u2, self.Q) X = self.eccplus(u1P, u2Q) if X == (0, 0): return False v = X[0] % self.n return v == r if __name__ == '__main__': import ipdb x = b'I love you' ss = ECDSA() ipdb.set_trace() common = ss.sign(x) o = ECDSA(common[0]) r, s = common[1] print(o.verify(x, r, s)) o = ECIES() o.init() y = o.crypto(100) print(y) print(o.decrypt(y)) c = ECC(p=23, B=17, A=9) P = (16, 5) print('y^2 = x^3 + 9x + 17 mod 23, P = {}'.format(str(P))) for i in range(1, 10): print('{}P = {}'.format(i, str(c.eccmul(i, P)))) ''' y^2 = x^3 + 9x + 17 mod 23, P = (16, 5) 1P = (16, 5) 2P = (20, 20) 3P = (14, 14) 4P = (19, 20) 5P = (13, 10) 6P = (7, 3) 7P = (8, 7) 8P = (12, 17) 9P = (4, 5) ''' |
|
[原创]Python和《加密与解密》
加密与解密_光盘\chap06\6.3 公开密钥加密算法\6.3.2 ElGamal公钥算法 #!/usr/bin/env python # coding: utf-8 # 2016-05-10 15:15:22 P177 ch6.3.2 Elgamal KeyGenMe from sys import version from random import randint from hashlib import md5 from binascii import hexlify from math import gcd from xmath import mulinv if 'v2.7' in version: input = raw_input def keygen(name): assert type(name) is str p = 0xCE892335578D3F g = 0x473FE7D24CB6A6 y = 0xA3CCD85BBD896 x = 0x264D8D82C7AAB8 k = randint(2, p -2) m = md5() m.update(b'ped') m.update(name.encode('ascii')) szHash =m.hexdigest() M = int(szHash, 16) while gcd(k, p - 1) != 1: k = randint(2, p - 2) s1 = pow(g, k, p) k_inv = mulinv(k, p - 1) s2 = k_inv * (M - x * s1) % (p - 1) serial = hex(s1)[2:].upper() + '-' + hex(s2)[2:].upper() return serial def check(name, serial): # type(name) == type(serial) == str assert True if name and serial else False, 'Wrong Serial !' if '-' not in serial: return False m = md5() m.update(b'ped') m.update(name.encode('ascii')) szHash =m.hexdigest() M = int(szHash, 16) # 4DB4A765FD4F0809A881A78966456945 szSerial = serial.split('-') try: a, b= [int(i, 16) for i in szSerial] except: print('Wrong Serial !') return False p = 0xCE892335578D3F g = 0x473FE7D24CB6A6 y = 0xA3CCD85BBD896 result1 = pow(y, a, p) result2 = pow(a, b, p) result3 = result1 * result2 % p result4 = pow(g, M, p) if result4 == result3: print('success') return True else: print('Wrong Serial !') return False if __name__ == '__main__': name = 'ipediy' serial = 'ACD47530E2ED99-295158D2AAA31B' check(name, serial) # True # RDLP Tools v1.12 y = g^x mod p --> x = 0x264D8D82C7AAB8 614s(Try 486711703 cnts) while True: s = keygen(input('name > ')) print('serial > {}'.format(s)) '''output name > pediy serial > 9390FD5DA9F5F-7F98F18EEDEE21 name > pediy serial > 9494C8510B670D-C440133310FD6B name > pediy serial > 45C7449F72112D-4B0ED5382A89D5 name > pediy serial > B1B6C76E5EF311-13CCA372E41A3B ''' |
|
[原创]Python和《加密与解密》
代码变量命名,按照ElgamalKGM.c 的源代码命名方式,方便阅读,注册机后续奉上 #!/usr/bin/env python # coding: utf-8 # 2016-05-09 22:34:39 P177 ch6.3.2 Elgamal KeyGenMe 逻辑代码 python 等价实现 from hashlib import md5 def check(name, serial): # type(name) == type(serial) == str assert True if name and serial else False, 'Wrong Serial !' if '-' not in serial: return False m = md5() m.update(b'ped') m.update(name.encode('ascii')) szHash =m.hexdigest() M = int(szHash, 16) # 4DB4A765FD4F0809A881A78966456945 szSerial = serial.split('-') try: a, b= [int(i, 16) for i in szSerial] except: print('Wrong Serial !') return False p = 0xCE892335578D3F g = 0x473FE7D24CB6A6 y = 0xA3CCD85BBD896 result1 = pow(y, a, p) result2 = pow(a, b, p) result3 = result1 * result2 % p result4 = pow(g, M, p) if result4 == result3: print('success') return True else: print('Wrong Serial !') return False if __name__ == '__main__': name = 'ipediy' serial = 'ACD47530E2ED99-295158D2AAA31B' check(name, serial) # True |
|
[原创]Python和《加密与解密》
继续完善自己的加密函数库,为以后CTF 做准备,就是不知道方面偏没有,上代码 #!/usr/bin/env python # coding: utf-8 # 2016-05-04 23:40:39 加密解密 P173 密码编码学与网络安全 P287 from random import randint, getrandbits from math import gcd from xmath import mulinv, xgcd, xpow from isprime import genprime def elgamal_sig_init(size): ''' @size: int, size > msg y = a^x mod q it's difficult to find random prime root 'a' ''' x = q = genprime(size) a = randint(2, q - 2) size = q.bit_length() while x >= q: x = getrandbits(size) y = pow(a, x, q) return (q, a, x, y) def elgamal_sig(m): ''' @m: int, msg < q s1 = a^k mod q M = (x*s1 + k * s2) mod (q - 1) -> s2 pub_key -> (a, q, y) pri_key -> x signature -> (s1, s2) ''' q, a, x, y = elgamal_sig_init() k = randint(2, q -2) while gcd(k, q - 1) != 1: k = randint(2, q - 2) s1 = pow(a, k, q) k_inv = mulinv(k, q - 1) s2 = k_inv * (m - x * s1) % (q - 1) return (m, a, q, y, s1, s2) def elgamal_sig_verify(args): ''' (y^s1) * (s1^s2) = a^m mod p ''' m, a, q, y, s1, s2 = args v1 = pow(a, m, q) v2 = pow(y, s1, q) * pow(s1, s2, q) % q return True if v1 == v2 and 1<= a < p else False def ready(size): ''' y = g^x mod p a = g^k mod p b = y^k mod p 1 < g, k, x < p - 1 pub_key -> (a, b, p) pri_key -> (a, b, p, x) ''' x = p = genprime(size) k = randint(2, p - 2) while gcd(k, p - 1) != 1: k = randint(2, p - 2) g = randint(2, p - 2) while x >= p: x = getrandbits(size) y = pow(g, x, p) a = pow(g, k, p) b = pow(y, k, p) return (a, b, p, x) # args = ready(n) def crypto(m, a, b, p): b = m * b % p return (a, b) def decrypt(a, b, p, x): ax = pow(a, x, p) inv = mulinv(ax, p) m = b * inv % p return m if __name__ == '__main__': # m < 0xCE892335578D3F m = 1124 sig = elgamal_sig(m) print(sig) print(elgamal_sig_verify(sig)) # genprime from random import randint from random import getrandbits def find_kq(n): q = n - 1 k = 1 while not (q & 1): q >>= 1 k += 1 return k , q # n > 2 def isprime(n): assert n > 2 if n & 1 == 0: return False k, q = find_kq(n) a = randint(1, n - 1) if pow(a, q, n) == 1: # 优化 a ** q % n return True for j in range(k): if pow(a, pow(2, j) * q, n) == n - 1: # a **((2 ** j) * q) % n return True return False # RSA 推荐 bit_length = 1024, 约6.84s 生成一个素数, 2048 约 15s, 平均测试 log(2**1024,e)//2 = 70次 def genprime(bit_length): while True: x = getrandbits(bit_length) # 比 randint() 快3.9倍以上 if isprime(x): return x # mulinv # return (g, x, y) a*x + b*y = gcd(x, y) def xgcd(b, n): x0, x1, y0, y1 = 1, 0, 0, 1 while n != 0: q, b, n = b // n, n, b % n x0, x1 = x1, x0 - q * x1 y0, y1 = y1, y0 - q * y1 return (b, x0, y0) # x = mulinv(b) mod n def mulinv(b, n): g, x, _ = xgcd(b, n) if g == 1: return x % n |
|
[原创]Python和《加密与解密》
在对 p171页的RSA keyGenMe 反汇编的过程中,遇到很多Miracl 标准加密库函数,自己分析很费力,对照源代码看,也发现源代码理解起来也比较费力,去理解加密库源代码,貌似偏离了学逆向的初衷,幸好大神 Nil 相助,让我使用库签名,自己google 找到fireeye的技术文章 https://www.fireeye.com/blog/threat-research/2015/01/flare_ida_pro_script.html 上面提到FLARE 程序,发现IDA_Pro_v6.8 目录下有这个程序,flair68.zip,然后认真阅读readme.txt,将\6.3 公开密钥加密算法\6.3.1 RSA算法\RSAKeyGenMe\RSAKeyGenMe\src\ms32.lib 库制作成*.sig 然后使用IDA pro载入,未知函数就可以识别了,具体细节就不说了,读者自己找 flair68.zip 的readme看吧 |
|
[原创]Python和《加密与解密》
#!/usr/bin/env python3 # coding: utf-8 # 2016-05-04 21:24:18 from xmath import mulinv from cryptocommon import ascii_to_int def rsa_keygen(name, p, q, e): c = ascii_to_int(name) n = p * q phi = (p - 1) * (q - 1) d = mulinv(e, phi) m = pow(c, d, n) return hex(m)[2:].upper() if __name__ == '__main__': name = input('name : ') e = 0x10001 n = 0x80C07AFC9D25404D6555B9ACF3567CF1 # p, q is decomposition by rsa-tool2 by TE p = 0xA554665CC62120D3 q = 0xC75CB54BEDFA30AB print('serial: {}'.format(rsa_keygen(name, p, q, e))) # xmath 是自己实现的加密库,最近学习RSA算法实现的,涉及大数运算的实现 # xmath.py #!/usr/bin/env python # coding: utf-8 # 2016-04-19 10:34:12 # pip install gmpy2 (need visual studio vc compiler) # return (g, x, y) a*x + b*y = gcd(x, y) def xgcd(b, n): x0, x1, y0, y1 = 1, 0, 0, 1 while n != 0: q, b, n = b // n, n, b % n x0, x1 = x1, x0 - q * x1 y0, y1 = y1, y0 - q * y1 return (b, x0, y0) # gcd for python2, the same with py3's math.gcd def gcd(a, b): return xgcd(a, b)[0] # x = mulinv(b) mod n def mulinv(b, n): g, x, _ = xgcd(b, n) if g == 1: return x % n def xpow(x, y, n): if y < 0: x, y = mulinv(x, n), -y return pow(x, y, n) # (x / y) % n, x.bit_length() > 2048 def xdiv(x, y, n): g = xgcd(x, y)[0] x, y = x // g, y // g y = mulinv(y, n) return (x * y) % n if __name__ == '__main__': N = 23927411014020695772934916764953661641310148480977056645255098192491740356525240675906285700516357578929940114553700976167969964364149615226568689224228028461686617293534115788779955597877965044570493457567420874741357186596425753667455266870402154552439899664446413632716747644854897551940777512522044907132864905644212655387223302410896871080751768224091760934209917984213585513510597619708797688705876805464880105797829380326559399723048092175492203894468752718008631464599810632513162129223356467602508095356584405555329096159917957389834381018137378015593755767450675441331998683799788355179363368220408879117131 c1 = 14548997380897265239778884825381301109965518989661808090688952232381091726761464959572943383024428028270717629953894592890859128818839328499002950828491521254480795364789013196240119403187073307558598496713832435709741997056117831860370227155633169019665564392649528306986826960829410120348913586592199732730933259880469229724149887380005627321752843489564984358708013300524640545437703771424168108213045567568595093421366224818609501318783680497763353618110184078118456368631056649526433730408976988014678391205055298782061128568056163894010397245301425676232126267874656710256838457728944370612289985071385621160886 c2 = 12793942795110038319724531875568693507469327176085954164034728727511164833335101755153514030256152878364664079056565385331901196541015393609751624971554016671160730478932343949538202167508319292084519621768851878526657022981883304260886841513342396524869530063372782511380879783246034751883691295368172069170967975561364277514063320691930900258017293871754252209727301719207692321798229276732198521711602080244950295889575423383308099786298184477668302842952215665734671829249323604032320696267130330613134368640401070775927197554082071807605399448960911234829590548855031180158567578928333030631307816223152118126597 m1 = 8246074182642091125578311828374843698994233243811347691229334829218700728624047916518503687366611595562099039411430662968666847086659721231623198995017758424796091810259884653332576136128144958751327844746991264667007359518181363522934430676655236880489550093852524801304612322373542296281962196795304499711006801211783005857297362930338978872451934860435597545642219213551685973208209873623909629278321181485010964460652298690058747090298312365230671723790850998541956664376820820570709272500330966205578898690396706695024001970727864091436518202414166919020415892764617055978488996164642229582717493375419993187360 m2 = 15575051453858521753108462063723750986386093067763948316612157946190835527332641201837062951012227815568418309166473080588354562426066694924364886916408150576082667797274000661726279871971377438362829402529682825471299861814829463510659258586020732228351258291527965822977048954720558973840956731377322516168809373640494227129998871167089589689796024458501705704779109152762373660542684880052489213039920383757930855300338529058000330103359636123251274293258 r1 = 12900676191620430360427117641859547516838813596331616166760756921115466932766990479475373384324634210232168544745677888398849094363202992662466063289599443 r2 = 7718975159402389617924543100113967512280131630286624078102368166185443466262861344357647019797762407935675150925250503475336639811981984126529557679881059 _, a, b = xgcd(r1, r2) k = xpow(xdiv(c1, m1, N), a, N) * xpow(xdiv(c2, m2, N), b, N) % N print(k) # 175971776542095822590595405274258668271271366360140578776612582276966567082080372980811310146217399585938214712928761559525614866113821551467842221588432676885027725038849513527080849158072296957428701767142294778752742980766436072183367444762212399986777124093501619273513421803177347181063254421492621011961 # ascii_to_int 函数实现 from binascii import hexlify, unhexlify def ascii_to_int(s): n = s.encode('ascii') return int(hexlify(n), 16) |
|
[原创]Python和《加密与解密》
在逆向分析过程中,总是要梳理函数逻辑,以理解程序做了什么,之前没有找到好的方法,导致逆向速度低下,现在算不上效率高,但好在用python 语言来描述逻辑过程,可以简化过程。下面是我的一些笔记,新手朋友可以参考下 import string from binascii import unhexlify from hashlib import md5 # ollydbg 内存数据分析函数,注意,OD 中的数据以 little-endian 形式显示 s = '''20 DE 87 7D AB 1F 7A 0E''' def rev(s): s = s.split() s = ['0x' + i for i in s] s = str(s).replace("'", '') print(s) def endian32(lst): assert len(lst) & 3 == 0 for idx in range(0, len(lst), 4): lst[idx: idx + 4] = reversed(lst[idx: idx + 4]) return lst def ishex(s): assert type(s) is str hex_digits = set(string.hexdigits) return all(c in hex_digits for c in s) ############################################### name = 'ipediy' serial = '00112233445566778899AABBCCDDEEFF' key = [0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C] # 2B 7E 15 16 28 AE D2 A6 AB F7 15 88 09 CF 4F 3C assert len(serial) == 0x20 assert ishex(serial) xSeri = list(unhexlify(serial)) assert xSeri == [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF] m = md5() m.update(name.encode('ascii')) szHash = list(m.digest()) assert szHash == [0x84, 0x81, 0xE5, 0x7B, 0x16, 0x7B, 0xA7, 0x84, 0xA6, 0xB5, 0x78, 0x8B, 0x01, 0x89, 0xCE, 0x4C] # .text:00401274 aes_init(ctx, 0, 0x10, key) ctx = [0x04, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C, 0xA0, 0xFA, 0xFE, 0x17, 0x88, ... 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] # pip install pycrypto from Crypto.Cipher import AES a = AES.new(bytes(key), AES.MODE_ECB) ciphertext = a.encrypt(bytes(xSeri)) assert list(ciphertext) == [0x8D, 0xF4, 0xE9, 0xAA, 0xC5, 0xC7, 0x57, 0x3A, 0x27, 0xD8, 0xD0, 0x55, 0xD6, 0xE4, 0xD6, 0x4B] assert ciphertext == szHash, 'Success!' |
|
[原创]Python和《加密与解密》
<加密与解密> 这本书加密算法一章,最虐心的,除了这前的IDEA算法(Debian专利,找不到合适加密库)比较虐心以外,就是AES算法了,复习密码学数论,看源代码,分析IDA伪代码,汇编代码关系。。。 下面上python 注册机主程序 # !/usr/bin/env python3 # coding: utf-8 # 2016-04-01 00:55:58 import string from hashlib import md5 from binascii import unhexlify, hexlify from Crypto.Cipher import AES # pip install pycrypto KEY = [0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C] def ishex(s): assert type(s) is str hex_digits = set(string.hexdigits) return all(c in hex_digits for c in s) # 验证序列号是否正确 def aes_verify(name, serial): assert type(name) is str assert type(serial) is str assert len(serial) == 0x20 assert ishex(serial), 'serial must be hex digits' global KEY aes = AES.new(bytes(KEY), AES.MODE_ECB) # AES.new(key, AES.MODE_CBC, IV) ciphertext = aes.encrypt(unhexlify(serial)) m = md5() m.update(name.encode('ascii')) if ciphertext == m.digest(): return 'Verifyed successful!' else: return 'Verifyed Wrong!' # 由用户名生成序列号 def aes_keygen(name): assert type(name) is str global KEY m = md5() m.update(name.encode('ascii')) aes = AES.new(bytes(KEY), AES.MODE_ECB) szSerial = aes.decrypt(m.digest()) return hexlify(szSerial) if __name__ == '__main__': while True: name = input('name >').strip() print(aes_keygen(name).upper().decode('ascii')) |
|
[原创]Python和《加密与解密》
因为《加密与解密》SHA密码学章节中,未使用IDA 进行分析,本着扎实打基础的原则,没太关注IDA 的使用,导致MD5和SHA算法的分析,完全是在汇编+Ollydbg的状态下进行,去逆向群问了下老司机,推荐说用F5, 我一看IDA 伪代码傻眼了...于是开始关注“伪代码vs汇编vs源代码” 三者关系,方便加速逆向分析。下面是一段函数的三者关系分析,未接触过IDA伪代码的朋友感受下。 汇编代码 .text:00401040 mov eax, [esp+sh] .text:00401044 push esi .text:00401045 mov esi, [esp+4+byte] .text:00401049 mov ecx, [eax] ; length[0] .text:0040104B and esi, 0FFh .text:00401051 shr ecx, 5 ; sh->length[0]/32 .text:00401054 and ecx, 0Fh ; cnt=(int)((sh->length[0]/32)%16); .text:00401057 mov edx, [eax+ecx*4+28h] ; sh->w[cnt] .text:00401057 ; sh+40->&w[0] .text:0040105B shl edx, 8 ; sh->w[cnt]<<=8; .text:0040105E or edx, esi .text:00401060 pop esi .text:00401061 mov [eax+ecx*4+28h], edx ; sh->w[cnt]|=(unsigned int)(byte&0xFF); .text:00401065 mov ecx, [eax] ; sh->length[0]+=8; .text:00401067 add ecx, 8 .text:0040106A mov [eax], ecx .text:0040106C jnz short loc_40107B ; if (sh->length[0]==0L): .text:0040106E mov ecx, [eax+4] .text:00401071 mov dword ptr [eax], 0 ; sh->length[0]=0L; .text:00401077 inc ecx .text:00401078 mov [eax+4], ecx ; sh->length[1]++ .text:0040107B .text:0040107B loc_40107B: ; CODE XREF: sha1_process+2Cj .text:0040107B test dword ptr [eax], 1FFh ; if ((sh->length[0]%512)==0) .text:00401081 jnz short locret_40108A .text:00401083 push eax .text:00401084 call sha1_transform ; sha1_transform(sh); .text:00401089 pop ecx .text:0040108A retn .text:0040108A sha1_process endp 人工翻译伪代码 ecx = (ecx >> 5) & 0xf edx = structx[4 * ecx + 10 * c_int] << 8 edx |= esi & 0xff structx[0] += 8 if stuctx[0] != 0: if stuctx[0] & 255 != 0: return eax else: func401090() else: stuctx[1] += 1 IDA 伪代码 int __cdecl sha1_process(int sh, unsigned __int8 byte) { int result; // eax@1 bool blx; // zf@1 int v4; // ecx@2 result = sh; *(_DWORD *)(sh + 4 * ((*(_DWORD *)sh >> 5) & 0xF) + 40) = byte | (*(_DWORD *)(sh+ 4 * ((*(_DWORD *)sh >> 5) & 0xF)+ 40) << 8); // cnt=(int)((sh->length[0]/32)%16); 对应等号左侧 // sh->w[cnt]|(unsigned int)(byte&0xFF); 对应等号右侧 blx = *(_DWORD *)sh == -8; // sh->w[cnt]<<=8; // if (sh->length[0]==0L){} *(_DWORD *)sh += 8; // sh->length[0]+=8; if ( blx ) { v4 = *(_DWORD *)(sh + 4); // sh->length[1]++; *(_DWORD *)sh = 0; *(_DWORD *)(sh + 4) = v4 + 1; // sh->length[0]=0L; } if ( !(*(_DWORD *)sh & 0x1FF) ) result = sha1_transform(sh); return result; } 源代码如下 typedef struct { unsigned int length[2]; // sha unsigned int h[8]; // sha + 8 unsigned int w[80]; //sha + 40 } sha; void sha1_process(sha *sh,int byte) { /* process the next message byte */ int cnt; cnt=(int)((sh->length[0]/32)%16); sh->w[cnt]<<=8; sh->w[cnt]|=(unsigned int)(byte&0xFF); sh->length[0]+=8; if (sh->length[0]==0L) { sh->length[1]++; sh->length[0]=0L; } if ((sh->length[0]%512)==0) sha1_transform(sh); } |
|
[原创]Python和《加密与解密》
#!/usr/bin/env python3 # coding: utf-8 # 2016-01-06 23:08:46 # Be care of BE LE from hashlib import md5 from ctypes import c_uint32 from sys import exit DELTA = 0x9e3779b9 BLOCK_SIZE = 2 # number of 32-bit ints KEY_SIZE = 4 ROUNDS = 32 SUMATION = 0xc6ef3720 def encrypt_block(block, key, verbose=False): ''' Encrypt a single 64-bit block with a 128-bit key @param block: list of two c_uint32s @param key: list of four c_uint32s ''' assert len(block) == BLOCK_SIZE assert len(key) == KEY_SIZE s = c_uint32(0) delta = c_uint32(DELTA) for idx in range(ROUNDS): s.value += delta.value block[0].value += \ ((block[1].value << 4) + key[0].value) ^ \ (block[1].value + s.value) ^\ ((block[1].value >> 5) + key[1].value) block[1].value += \ ((block[0].value << 4) + key[2].value) ^\ (block[0].value + s.value) ^\ ((block[0].value >> 5) + key[3].value) if verbose: print('\t --> Encrypting block round %d of %d' % (idx + 1, ROUNDS)) return block def decrypt_block(block, key, verbose=False): '''Decrypt a single 64-bit block with 128-bit key @param block: list of two c_uint32s @param key: list of four c_uint32s ''' assert len(block) == BLOCK_SIZE assert len(key) == KEY_SIZE s = c_uint32(SUMATION) delta = c_uint32(DELTA) for idx in range(ROUNDS): block[1].value -=\ ((block[0].value << 4) + key[2].value) ^\ (block[0].value + s.value) ^\ ((block[0].value >> 5) + key[3].value) block[0].value -=\ ((block[1].value << 4) + key[0].value) ^\ (block[1].value + s.value) ^\ ((block[1].value >> 5) + key[1].value) s.value -= delta.value if verbose: print('\t<-- Decrypting block round %d of %d' % (idx +1, ROUNDS)) return block def to_c_array(string): '''Convert a string to a list of c_uint32s''' c_array = [] for idx in range(0, len(string), 8): x = int(string[idx:idx+8], 16) # convert BE to LE x = ((x >> 24) & 0xff) | ((x >> 16) & 0xff) << 8 |\ ((x >> 8) & 0xff) << 16 | (x&0xff) << 24 c_array.append(c_uint32(x)) return c_array def to_string(c_array): '''Converts a list of c_uint32s to a Python (ascii) string''' tmp = [] for idx in range(len(c_array)): x = c_array[idx].value x = ((x >> 24) & 0xff) | ((x >> 16) & 0xff) << 8 |\ ((x >> 8) & 0xff) << 16 | (x&0xff) << 24 tmp.append(x) output = '' for block in tmp: output += hex(block)[2:].zfill(8) return output.upper() def xor(c_arrayX, c_arrayY): '''every bits of c_arrayX xor per c_arrayY's @param c_arrayX: list of two c_uint32s @param c_arrayY: list of two c_uint32s output: bytes list ''' output = [] for idx in range(2): x = c_arrayX[idx].value y = c_arrayY[idx].value output.append(c_uint32((x & 0xff) ^ (y & 0xff) |\ (((x>>8)&0xff) ^ ((y>>8)&0xff)) << 8 |\ (((x>>16)&0xff) ^ ((y>>16)&0xff)) << 16 |\ (((x>>24)&0xff) ^ ((y>>24)&0xff)) << 24)) return output def main(): name = input('usr: ').strip() assert len(name) != 0 m = md5() m.update(name.encode('ascii')) k = to_c_array(m.hexdigest()) v = [k[2], k[3]] v = xor(v, k) v = decrypt_block(v, k) print(to_string(v)) #return to_string(v) if __name__ == "__main__": main() |
操作理由
RANk
{{ user_info.golds == '' ? 0 : user_info.golds }}
雪币
{{ experience }}
课程经验
{{ score }}
学习收益
{{study_duration_fmt}}
学习时长
基本信息
荣誉称号:
{{ honorary_title }}
能力排名:
No.{{ rank_num }}
等 级:
LV{{ rank_lv-100 }}
活跃值:
在线值:
浏览人数:{{ visits }}
最近活跃:{{ last_active_time }}
注册时间:{{ user_info.create_date_jsonfmt }}
勋章
兑换勋章
证书
证书查询 >
能力值