用 Python 反编译 Python 软件

【文章标题】: 用 Python 反编译 Python 软件
【文章作者】: Ptero
【软件名称】: ****
【下载地址】: ****
【加壳方式】: UPX
【保护方式】: 序列号,重启验证
【使用工具】: 7-zip, LordPE, Python, WinHex
【操作平台】: Windows, Linux
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
——————————————————————————–
【版权声明】: 本文原创于看雪软件安全论坛, 转载请注明作者并保持文章的完整, 谢谢!

论坛讨论 Python 的文章似乎比较少。此文权当抛砖引玉,不当之处请多指教。

关于 Py 的反编译,网上流传的说法是,2.4 版本以后的比较困难。因为针对低版本,有一些开源代码可以实现反编译。到 2.4 以后,原作者要么不更新了,要么开始收费了。只有少数牛人能修改旧版的代码继续反编译下去。
本文提供了一种方法,使得在没有反编译器的情况下,也能分析用 Python 写的软件。

下面开始。

试炼软件是用 upx 加壳的,upx -d 简单脱掉。
再用 peid 查看, 发现如下信息:
Microsoft Visual C++ 7.0 Method2 [ZIP SFX]。
很少见。再用OD加载,查看字符串,发现是 py2exe 生成的。
这种文件,可以用 7zip 当作压缩包打开,里面可以看到一堆编译好的 pyc 和 pyo 文件。从名称来看,那些应该是 Python 自带的库文件,与程序无关。这里从库文件的名称可以看出是 Python 2.4 版本。

程序自带了一个 pyo 文件,200 多 K。网上找到能免费反编译 Python 2.4 以后版本的,只有 decompyler 2.3 的一个修改版(2.3 开源,后续版本收费了), 还有就是 UnPyc。
decompyler 的修改版,我没有找到。UnPyc,貌似只支持 Linux 的。在学校的 Linux 服务器上安装上,反编译却得到一堆错误。反汇编(生成 bytecode 的助记符,相当于汇编代码)倒是可以。

在反汇编当中查找关键字符串,没找到。我把所有的函数名称看了一遍,也没有可疑的。看来那个 200 多 K 的文件,不是关键代码所在。还得另谋出路。

关键代码不在附带的库里面,那还能在哪里呢?只能在程序自身了!一定是 py2exe 把那些代码编到 exe 里面了。
有2种可能:1、编成 native code。2、编成 Python bytecode,通过 Python 虚拟机执行。在OD中跟踪一下,发现执行到关键代码的时候,已经处在虚拟机的代码之中了。所以排除 1 的可能性。

下面,要找出关键代码的藏身之处。代码会藏在哪呢?.text 段?不大可能,因为那里都是 sfx 的代码。.data 段?也不可能,因为那里的数据只会被 sfx 用到。在 zip 压缩包里面?没找到可疑文件。那么,只剩下 .rsrc 了!

用 LordPE 查看资源段(这里不用 Reshacker,因为它 dump 下来的不正确),找到 “Python24.dll”, “PZIB.PYD”,还有就是 “PYTHONSCRIPT”。光是看名字就很可疑了。把它 dump 下来,在 WinHex 当中查找关键字符串,果然找到了。
下面就是要如何反编译这个 script 了。

网上没有找到相关资料。于是下载了一份 py2exe 源码,找到这里:

# We create a list of code objects, and write it as a marshaled
# stream. The framework code then just exec’s these in order.
# First is our common boot script.
boot = self.get_boot_script(“common”)
boot_code = compile(file(boot, “U”).read(),
os.path.abspath(boot), “exec”)
code_objects = [boot_code]
if self.bundle_files < 3:
code_objects.append(
compile(“import zipextimporter; zipextimporter.install()”,
“<install zipextimporter>”, “exec”))
for var_name, var_val in vars.items():
code_objects.append(
compile(“%s=%r\n” % (var_name, var_val), var_name, “exec”)
)
if self.custom_boot_script:
code_object = compile(file(self.custom_boot_script, “U”).read() + “\n”,
os.path.abspath(self.custom_boot_script), “exec”)
code_objects.append(code_object)
if script:
code_object = compile(open(script, “U”).read() + “\n”,
os.path.basename(script), “exec”)
code_objects.append(code_object)
code_bytes = marshal.dumps(code_objects)

if self.distribution.zipfile is None:
relative_arcname = “”

si = struct.pack(“iiii”,
0x78563412, # a magic value,
self.optimize,
self.unbuffered,
len(code_bytes),
) + relative_arcname + “\000”

script_bytes = si + code_bytes + ‘\000\000’
self.announce(“add script resource, %d bytes” % len(script_bytes))
if not self.dry_run:
add_resource(ensure_unicode(exe_path), script_bytes, u”PYTHONSCRIPT”, 1, True) 可以看出,是由几个 py 文件编译后,添加到一个 list 里面,然后直接 dump 下来的,当然前后还加了一堆东西。

用 WinHex 修改 dump 下来的文件,把添加的东西都去掉,这样就只剩下了 code_bytes。

现在,本文的主人公要华丽地出场了!有请 Python!(掌声)

在 Python 中输入:

>>>import marshal
>>>mylist=marshal.load(open(“dumpfile”, “r”))目的是为了把 dump 下来的文件加载到内存当中,成为 Python 的一个对象。

注:加载dump下来的对象,Python 版本一定要和 dump 时候的版本兼容才行。这个例子中,dump 时用的 2.4 ,我用 2.5 load,完全可以。用 3.1 load,就出错了。现在可以看看我们的这个对象了:

>>>mylist
[<code object ? at 0xb75df650, file “D:\python24\lib\site-packages\py2exe\boot_common.py”, line 44>, <code object ? at 0xb75df698, file “<install zipextimporter>”, line 1>, <code object ? at 0xb75f4ad0, file “****.py”, line 2>]包含了 3 个 code object 对象。第一个是 py2exe 初始化用的,第二个是解压 zip 用的,第三个就是我们的关键脚本了。

这里简单介绍一下 py, pyc, pyo, bytecode, code object 之间的关系。py 是 Python 的源代码文件,纯文本文件。用 Python 可以编译成二进制伪代码,也就是 bytecode。code object 实际上就是这些伪代码。把 code object 前面加一个 header,写成文件,就是 pyc 了,也就是编译过的 py 文件。如果在编译的时候加上优化选项,则会生成 pyo 文件,也就是优化过的 py 文件,本质上和 pyc 是一样的。
如果说 py 相当于 java 文件,那么 pyc, pyo, bytecode, code object 就相当于 class 文件了。

下面言归正传。
Python 有一个很好很强大的库:dis,里面有一个很好很强大的同名函数:dis()。这个函数就是实现反汇编功能了。它能把 code object 生成可读的代码(类似于汇编)。
对这个函数加以简单扩展,可以让其变得更好更强大。(参见 http://blog.csdn.net/balabalamerobert/archive/2007/06/22/1662025.aspx)

import dis as pydis
import types

code = None
def read(filename):
f = open(filename)
content = f.read()
global code
code = compile(content, filename, ‘exec’)
f.close()

def find_code(code, name):
for item in code.co_consts:
if isinstance(item, types.CodeType):
if item.co_name == name:
return item
return None

def dis(code_name=None):
if code_name is None:
co = code
pydis.dis(co)
return co
names = code_name.split(“.”)
co = code
for name in names:
co = find_code(co, name)
if not co:
print ‘%s is not a valid name’ % code_name
if co:
print (” byte code for %s ” % code_name).center(60, ‘*’)
pydis.dis(co) 下面就可以慢慢找出关键模块了。

首先看一下模块包含哪些常量:

>>>mylist[2].co_consts:
(1, None, (‘gdi’,), (‘EnumProcesses’,), (‘Button’,), (‘Editbox’,), (‘Textin’,), (‘LOWORD’, ‘HIWORD’, ‘RGB’, ‘RECT’), (‘Msg’,), (‘Listview’,), (‘Listbox’,), (‘Combobox’,), (‘Checkbox’,), (‘Radiobox’,), (‘Treeview’,), (‘StaticText’,), (‘Groupbox’,), (‘ContextMenu’,), (‘Menu’,), (‘SystemCursor’,), (‘GetVirtualScreenSize’,), (‘static’,), (‘Queue’,), (‘latin_1’, ‘gbk’, ‘utf_8’, ‘ascii’, ‘gb2312’, ‘gb18030’), (‘StringIO’,), (‘dbapi2’,), (‘WinExec’,), (‘CF_TEXT’, ‘GHND’), ‘Display_REG_Dialog’, <code object Display_REG_Dialog at 0xb7cc9f50, file “****.py”, line 50>, ‘Display_INPUT_Dialog’, <code object Display_INPUT_Dialog at 0xb7cd7188, file “****.py”, line 96>, ‘Display_INPUT_TIME_Dialog’, <code object Display_INPUT_TIME_Dialog at 0xb7cd7380, file “****.py”, line 134>, ‘Setup_Find_Dialog’, <code object Setup_Find_Dialog at 0xb7cd7530, file “****.py”, line 168>, ‘Get_yo2_user_Dialog’, <code object Get_yo2_user_Dialog at 0xb7cd7770, file “****.py”, line 209>, ‘Edit_User_Dialog’, <code object Edit_User_Dialog at 0xb7cd79b0, file “****.py”, line 279>, ‘Add_User_Dialog’, <code object Add_User_Dialog at 0xb7cd7d10, file “****.py”, line 332>, ‘Add_Cate_Dialog’, <code object Add_Cate_Dialog at 0xb7cd7f50, file “****.py”, line 546>, ‘Get_Pic_Dialog’, <code object Get_Pic_Dialog at 0xb7cdd260, file “****.py”, line 602>, ‘Get_MPic_Dialog’, <code object Get_MPic_Dialog at 0xb7cdd530, file “****.py”, line 934>, ‘Select_ExportType_Dialog’, <code object Select_ExportType_Dialog at 0xb7cdd9b0, file “****.py”, line 1276>, ‘Setup_PROXY_Dialog’, <code object Setup_PROXY_Dialog at 0xb7cddad0, file “****.py”, line 1412>, ‘Update_Dialog’, <code object Update_Dialog at 0xb7cddba8, file “****.py”, line 1564>, ‘MyWindow’, <code object MyWindow at 0xb7cef2f0, file “****.py”, line 1698>, ”)Display_REG_Dialog 这个比较可疑,但是跟进去发现只是保存了注册码就返回了。相关代码省略。
因为软件是在启动时检测注册码的,所以窗口的启动代码也比较可疑。

>>>import sdis
>>>sdis.code = mylist[2]
>>>sdis.dis(“MyWindow.__init__”)
************ byte code for MyWindow.__init__ ************
(省略部分代码)
1732 439 LOAD_CONST 5 (0)
442 STORE_FAST 8 (conf_user_tag)

1733 445 LOAD_GLOBAL 40 (os)
448 LOAD_ATTR 35 (path)
451 LOAD_ATTR 41 (isfile)
454 LOAD_CONST 21 (‘****.ini’)
457 CALL_FUNCTION 1
460 JUMP_IF_FALSE 48 (to 511)
463 POP_TOP

1734 464 SETUP_EXCEPT 28 (to 495)

1735 467 LOAD_GLOBAL 42 (dict4ini)
470 LOAD_ATTR 43 (DictIni)
473 LOAD_CONST 21 (‘****.ini’)
476 CALL_FUNCTION 1
479 LOAD_FAST 0 (self)
482 STORE_ATTR 44 (conf_user)

1736 485 LOAD_CONST 22 (1)
488 STORE_FAST 8 (conf_user_tag)
491 POP_BLOCK
492 JUMP_ABSOLUTE 518

1737 >> 495 POP_TOP
496 POP_TOP
497 POP_TOP

1738 498 LOAD_CONST 5 (0)
501 STORE_FAST 8 (conf_user_tag)
504 JUMP_ABSOLUTE 518
507 END_FINALLY
508 JUMP_FORWARD 7 (to 518)
>> 511 POP_TOP

1740 512 LOAD_CONST 5 (0)
515 STORE_FAST 8 (conf_user_tag)

1741 >> 518 LOAD_FAST 8 (conf_user_tag)
521 LOAD_CONST 5 (0)
524 COMPARE_OP 2 (==)
527 JUMP_IF_FALSE 50 (to 580)
530 POP_TOP

1742 531 LOAD_GLOBAL 42 (dict4ini)
534 LOAD_ATTR 43 (DictIni)
537 LOAD_CONST 21 (‘****.ini’)
540 CALL_FUNCTION 1
543 LOAD_FAST 0 (self)
546 STORE_ATTR 44 (conf_user)

1743 549 LOAD_CONST 23 (”)
552 LOAD_FAST 0 (self)
555 LOAD_ATTR 44 (conf_user)
558 LOAD_ATTR 45 (config)
561 STORE_ATTR 46 (regnum)

1744 564 LOAD_FAST 0 (self)
567 LOAD_ATTR 44 (conf_user)
570 LOAD_ATTR 47 (save)
573 CALL_FUNCTION 0
576 POP_TOP
577 JUMP_FORWARD 1 (to 581)
>> 580 POP_TOP

1745 >> 581 LOAD_FAST 0 (self)
584 LOAD_ATTR 44 (conf_user)
587 LOAD_ATTR 48 (has_key)
590 LOAD_CONST 24 (‘config’)
593 CALL_FUNCTION 1
596 JUMP_IF_TRUE 32 (to 631)
599 POP_TOP

1746 600 LOAD_CONST 23 (”)
603 LOAD_FAST 0 (self)
606 LOAD_ATTR 44 (conf_user)
609 LOAD_ATTR 45 (config)
612 STORE_ATTR 46 (regnum)

1747 615 LOAD_FAST 0 (self)
618 LOAD_ATTR 44 (conf_user)
621 LOAD_ATTR 47 (save)
624 CALL_FUNCTION 0
627 POP_TOP
628 JUMP_FORWARD 1 (to 632)
>> 631 POP_TOP

1748 >> 632 LOAD_FAST 0 (self)
635 LOAD_ATTR 44 (conf_user)
638 LOAD_ATTR 45 (config)
641 LOAD_ATTR 48 (has_key)
644 LOAD_CONST 25 (‘regnum’)
647 CALL_FUNCTION 1
650 JUMP_IF_TRUE 32 (to 685)
653 POP_TOP

1749 654 LOAD_CONST 23 (”)
657 LOAD_FAST 0 (self)
660 LOAD_ATTR 44 (conf_user)
663 LOAD_ATTR 45 (config)
666 STORE_ATTR 46 (regnum)

1750 669 LOAD_FAST 0 (self)
672 LOAD_ATTR 44 (conf_user)
675 LOAD_ATTR 47 (save)
678 CALL_FUNCTION 0
681 POP_TOP
682 JUMP_FORWARD 1 (to 686)
>> 685 POP_TOP

1751 >> 686 LOAD_CONST 23 (”)
689 LOAD_FAST 0 (self)
692 STORE_ATTR 49 (regno)

1752 695 LOAD_CONST 23 (”)
698 LOAD_FAST 0 (self)
701 STORE_ATTR 50 (regno2)

1753 704 LOAD_FAST 0 (self)
707 LOAD_ATTR 51 (get_reg_no_true)
710 CALL_FUNCTION 0
713 LOAD_FAST 0 (self)
716 STORE_ATTR 52 (reg_true)上面是取注册码,并且调用 get_reg_no_true() 函数来验证,然后把结果保存在 reg_true 变量里面。再往下看:

2110 >> 4879 LOAD_FAST 0 (self)
4882 LOAD_ATTR 52 (reg_true)
4885 LOAD_CONST 214 (‘YES’)
4888 COMPARE_OP 3 (!=)
4891 JUMP_IF_FALSE 29 (to 4923)
4894 POP_TOP

2111 4895 LOAD_GLOBAL 155 (Msg)
4898 LOAD_FAST 0 (self)
4901 LOAD_ATTR 156 (Hwnd)
4904 LOAD_CONST 228 (‘\xb5\xb1\xc7\xb0****\xce\xaa\xce\xb4\xd7\xa2\xb2\xe1\xb0\xe6\xb1\xbe\xa3\xac\xb5\xbc\xb3\xf6\xb9\xa6\xc4\xdc\xbb\xe1\xca\xdc\xb5\xbd\xcf\xde\xd6\xc6\xa3\xac\xc7\xeb\xb5\xbd****.com\xb8\xb6\xb7\xd1\xd7\xa2\xb2\xe1\xa3\xa1’)
4907 LOAD_CONST 229 (‘\xcc\xe1\xca\xbe’)
4910 LOAD_CONST 225 (‘ok’)
4913 LOAD_CONST 226 (‘defbutton1’)
4916 CALL_FUNCTION 5
4919 POP_TOP
4920 JUMP_FORWARD 1 (to 4924)
>> 4923 POP_TOP 如果 reg_true 不等于 ‘YES’,就要弹出提示注册的对话框了。
关键 call 就是 get_reg_no_true() 了。只要让其返回 ‘YES’ 便可。

这里可以修改 get_reg_no_true() 爆破了,但既然已经走了这么远了,索性再走一程,深入关键 call 去看个究竟。

>>>sdis.dis(“MyWindow.get_reg_no_true”)
******** byte code for MyWindow.get_reg_no_true *********
2117 0 SETUP_EXCEPT 638 (to 641)

2118 3 SETUP_EXCEPT 173 (to 179)

2119 6 LOAD_CONST 1 (”)
9 STORE_FAST 9 (mac_address)

2120 12 LOAD_CONST 2 (‘.’)
15 STORE_FAST 6 (strComputer)

2121 18 LOAD_GLOBAL 2 (win32com)
21 LOAD_ATTR 3 (client)
24 LOAD_ATTR 4 (Dispatch)
27 LOAD_CONST 3 (‘WbemScripting.SWbemLocator’)
30 CALL_FUNCTION 1
33 STORE_FAST 3 (objWMIService)

2122 36 LOAD_FAST 3 (objWMIService)
39 LOAD_ATTR 6 (ConnectServer)
42 LOAD_FAST 6 (strComputer)
45 LOAD_CONST 4 (‘root\\cimv2’)
48 CALL_FUNCTION 2
51 STORE_FAST 8 (objSWbemServices)

2123 54 LOAD_FAST 8 (objSWbemServices)
57 LOAD_ATTR 8 (ExecQuery)
60 LOAD_CONST 5 (‘Select * from Win32_NetworkAdapter’)
63 CALL_FUNCTION 1
66 STORE_FAST 10 (colItems)

2124 69 SETUP_LOOP 80 (to 152)
72 LOAD_FAST 10 (colItems)
75 GET_ITER
>> 76 FOR_ITER 72 (to 151)
79 STORE_FAST 4 (objItem)

2125 82 LOAD_FAST 4 (objItem)
85 LOAD_ATTR 11 (MACAddress)
88 LOAD_CONST 0 (None)
91 COMPARE_OP 3 (!=)
94 JUMP_IF_FALSE 50 (to 147)
97 POP_TOP

2142 98 LOAD_CONST 6 (‘VEN_’)
101 LOAD_FAST 4 (objItem)
104 LOAD_ATTR 13 (PNPDeviceID)
107 COMPARE_OP 6 (in)
110 JUMP_IF_FALSE 30 (to 143)
113 POP_TOP
114 LOAD_CONST 7 (‘DEV_’)
117 LOAD_FAST 4 (objItem)
120 LOAD_ATTR 13 (PNPDeviceID)
123 COMPARE_OP 6 (in)
126 JUMP_IF_FALSE 14 (to 143)
129 POP_TOP

2143 130 LOAD_FAST 4 (objItem)
133 LOAD_ATTR 11 (MACAddress)
136 STORE_FAST 9 (mac_address)

2145 139 BREAK_LOOP
140 JUMP_ABSOLUTE 148
>> 143 POP_TOP
144 JUMP_ABSOLUTE 76
>> 147 POP_TOP
>> 148 JUMP_ABSOLUTE 76
>> 151 POP_BLOCK

2146 >> 152 LOAD_FAST 9 (mac_address)
155 LOAD_CONST 1 (”)
158 COMPARE_OP 2 (==)
161 JUMP_IF_FALSE 10 (to 174)
164 POP_TOP

2147 165 LOAD_CONST 8 (’00:0A:EB:F5:D4:14′)
168 STORE_FAST 9 (mac_address)
171 JUMP_FORWARD 1 (to 175)
>> 174 POP_TOP
>> 175 POP_BLOCK
176 JUMP_FORWARD 13 (to 192)

2148 >> 179 POP_TOP
180 POP_TOP
181 POP_TOP

2149 182 LOAD_CONST 8 (’00:0A:EB:F5:D4:14′)
185 STORE_FAST 9 (mac_address)
188 JUMP_FORWARD 1 (to 192)
191 END_FINALLY

2150 >> 192 SETUP_EXCEPT 68 (to 263)

2151 195 LOAD_GLOBAL 14 (md5)
198 LOAD_ATTR 15 (new)
201 LOAD_FAST 9 (mac_address)
204 CALL_FUNCTION 1
207 LOAD_ATTR 16 (hexdigest)
210 CALL_FUNCTION 0
213 STORE_FAST 7 (s)

2152 216 LOAD_GLOBAL 14 (md5)
219 LOAD_ATTR 15 (new)
222 LOAD_FAST 7 (s)
225 LOAD_CONST 9 (16)
228 SLICE+1
229 LOAD_FAST 7 (s)
232 LOAD_CONST 9 (16)
235 SLICE+2
236 BINARY_ADD
237 CALL_FUNCTION 1
240 LOAD_ATTR 16 (hexdigest)
243 CALL_FUNCTION 0
246 LOAD_CONST 10 (6)
249 LOAD_CONST 11 (-6)
252 SLICE+3
253 LOAD_FAST 0 (self)
256 STORE_ATTR 19 (regno)
259 POP_BLOCK
260 JUMP_FORWARD 16 (to 279)

2153 >> 263 POP_TOP
264 POP_TOP
265 POP_TOP

2154 266 LOAD_CONST 12 (’00e6e95aff213b8e40ff’)
269 LOAD_FAST 0 (self)
272 STORE_ATTR 19 (regno)

2155 275 JUMP_FORWARD 1 (to 279)
278 END_FINALLY

2156 >> 279 SETUP_EXCEPT 323 (to 605)

2157 282 LOAD_CONST 1 (”)
285 STORE_FAST 2 (tmp_result)

2158 288 LOAD_FAST 0 (self)
291 LOAD_ATTR 21 (conf_user)
294 LOAD_ATTR 22 (config)
297 LOAD_ATTR 23 (regnum)
300 LOAD_FAST 0 (self)
303 STORE_ATTR 24 (regno2)

2159 306 LOAD_GLOBAL 14 (md5)
309 LOAD_ATTR 15 (new)
312 LOAD_FAST 0 (self)
315 LOAD_ATTR 19 (regno)
318 CALL_FUNCTION 1
321 LOAD_ATTR 16 (hexdigest)
324 CALL_FUNCTION 0
327 STORE_FAST 7 (s)

2160 330 LOAD_GLOBAL 14 (md5)
333 LOAD_ATTR 15 (new)
336 LOAD_FAST 7 (s)
339 LOAD_CONST 13 (14)
342 SLICE+1
343 LOAD_CONST 14 (‘bb2’)
346 BINARY_ADD
347 LOAD_FAST 7 (s)
350 LOAD_CONST 13 (14)
353 SLICE+2
354 BINARY_ADD
355 CALL_FUNCTION 1
358 LOAD_ATTR 16 (hexdigest)
361 CALL_FUNCTION 0
364 STORE_FAST 1 (md5_tmp)

2161 367 LOAD_FAST 1 (md5_tmp)
370 LOAD_CONST 15 (28)
373 SLICE+1
374 LOAD_FAST 1 (md5_tmp)
377 LOAD_CONST 9 (16)
380 LOAD_CONST 16 (20)
383 SLICE+3
384 BINARY_ADD
385 LOAD_FAST 1 (md5_tmp)
388 LOAD_CONST 17 (0)
391 LOAD_CONST 18 (5)
394 SLICE+3
395 BINARY_ADD
396 LOAD_FAST 1 (md5_tmp)
399 LOAD_CONST 19 (7)
402 LOAD_CONST 20 (9)
405 SLICE+3
406 BINARY_ADD
407 LOAD_FAST 1 (md5_tmp)
410 LOAD_CONST 18 (5)
413 LOAD_CONST 19 (7)
416 SLICE+3
417 BINARY_ADD
418 LOAD_FAST 1 (md5_tmp)
421 LOAD_CONST 20 (9)
424 LOAD_CONST 9 (16)
427 SLICE+3
428 BINARY_ADD
429 LOAD_FAST 1 (md5_tmp)
432 LOAD_CONST 16 (20)
435 LOAD_CONST 15 (28)
438 SLICE+3
439 BINARY_ADD
440 LOAD_FAST 1 (md5_tmp)
443 LOAD_CONST 21 (8)
446 LOAD_CONST 22 (12)
449 SLICE+3
450 BINARY_ADD
451 LOAD_FAST 1 (md5_tmp)
454 LOAD_CONST 23 (17)
457 LOAD_CONST 24 (21)
460 SLICE+3
461 BINARY_ADD
462 STORE_FAST 5 (reg_tmp)

2162 465 LOAD_FAST 0 (self)
468 LOAD_ATTR 24 (regno2)
471 LOAD_CONST 17 (0)
474 LOAD_CONST 25 (4)
477 SLICE+3
478 LOAD_FAST 5 (reg_tmp)
481 LOAD_CONST 17 (0)
484 LOAD_CONST 25 (4)
487 SLICE+3
488 COMPARE_OP 2 (==)
491 JUMP_IF_FALSE 106 (to 600)
494 POP_TOP

2163 495 LOAD_FAST 0 (self)
498 LOAD_ATTR 24 (regno2)
501 LOAD_CONST 25 (4)
504 LOAD_CONST 21 (8)
507 SLICE+3
508 LOAD_FAST 5 (reg_tmp)
511 LOAD_CONST 25 (4)
514 LOAD_CONST 21 (8)
517 SLICE+3
518 COMPARE_OP 2 (==)
521 JUMP_IF_FALSE 72 (to 596)
524 POP_TOP

2164 525 LOAD_FAST 0 (self)
528 LOAD_ATTR 24 (regno2)
531 LOAD_CONST 19 (7)
534 LOAD_CONST 26 (15)
537 SLICE+3
538 LOAD_FAST 5 (reg_tmp)
541 LOAD_CONST 19 (7)
544 LOAD_CONST 26 (15)
547 SLICE+3
548 COMPARE_OP 2 (==)
551 JUMP_IF_FALSE 38 (to 592)
554 POP_TOP

2165 555 LOAD_FAST 0 (self)
558 LOAD_ATTR 24 (regno2)
561 LOAD_CONST 13 (14)
564 SLICE+1
565 LOAD_FAST 5 (reg_tmp)
568 LOAD_CONST 13 (14)
571 SLICE+1
572 COMPARE_OP 2 (==)
575 JUMP_IF_FALSE 10 (to 588)
578 POP_TOP

2166 579 LOAD_CONST 27 (‘ok’)
582 STORE_FAST 2 (tmp_result)
585 JUMP_ABSOLUTE 593
>> 588 POP_TOP
589 JUMP_ABSOLUTE 597
>> 592 POP_TOP
>> 593 JUMP_ABSOLUTE 601
>> 596 POP_TOP
>> 597 JUMP_FORWARD 1 (to 601)
>> 600 POP_TOP
>> 601 POP_BLOCK
602 JUMP_FORWARD 7 (to 612)

2167 >> 605 POP_TOP
606 POP_TOP
607 POP_TOP

2168 608 JUMP_FORWARD 1 (to 612)
611 END_FINALLY

2169 >> 612 LOAD_FAST 2 (tmp_result)
615 LOAD_CONST 27 (‘ok’)
618 COMPARE_OP 2 (==)
621 JUMP_IF_FALSE 8 (to 632)
624 POP_TOP

2170 625 LOAD_CONST 28 (‘YES’)
628 RETURN_VALUE
629 JUMP_FORWARD 5 (to 637)
>> 632 POP_TOP

2172 633 LOA代码有点长,看着看着就迷失在代码的丛林里了。这里可以讨个巧,借助免费的反编译引擎来帮忙。

首先获得 get_reg_no_true() 这个函数的 code object,然后使用 marshal.dump() 保存成文件(从 py2exe 的源代码哪里学来的)。然后用 WinHex 加上 8 个字节的 file header。前 4 个字节代表 Python 版本号,2.4 是 6DF20D0A。后 4 个字节是 timestamp,随便写就是。
接着来到这里:http://www.depython.net/
这个据说是 team509 (http://www.team509.com/) 做的,它可以免费反编译小于 5KB 的文件。

反编译出的结果:

try:
try:
mac_address = ”
strComputer = ‘.’
objWMIService = win32com.client.Dispatch(‘WbemScripting.SWbemLocator’)
objSWbemServices = objWMIService.ConnectServer(strComputer, ‘root\\cimv2’)
colItems = objSWbemServices.ExecQuery(‘Select * from Win32_NetworkAdapter’)
for objItem in colItems:
if (objItem.MACAddress != None):
if ((‘VEN_’ in objItem.PNPDeviceID) and (‘DEV_’ in objItem.PNPDeviceID)):
mac_address = objItem.MACAddress
break

if (mac_address == ”):
mac_address = ’00:0A:EB:F5:D4:14′
except:
mac_address = ’00:0A:EB:F5:D4:14′
try:
s = md5.new(mac_address).hexdigest()
self.regno = md5.new((s[16:] + s[:16])).hexdigest()[6:-6]
except:
self.regno = ’00e6e95aff213b8e40ff’
try:
tmp_result = ”
self.regno2 = self.conf_user.config.regnum
s = md5.new(self.regno).hexdigest()
md5_tmp = md5.new(((s[14:] + ‘bb2’) + s[:14])).hexdigest()
reg_tmp = ((((((((md5_tmp[28:] + md5_tmp[16:20]) + md5_tmp[0:5]) + md5_tmp[7:9]) + md5_tmp[5:7]) + md5_tmp[9:16]) + md5_tmp[20:28]) + md5_tmp[8:12]) + md5_tmp[17:21])
if (self.regno2[0:4] == reg_tmp[0:4]):
if (self.regno2[4:8] == reg_tmp[4:8]):
if (self.regno2[7:15] == reg_tmp[7:15]):
if (self.regno2[14:] == reg_tmp[14:]):
tmp_result = ‘ok’
except:
pass
if (tmp_result == ‘ok’):
return ‘YES’
else:
return ‘ERR’
except:
return ‘ERR’算法一目了然!稍微改一下,注册机就可以出炉了。

综上,使用 Python 开发的商业软件,其安全性还值得商榷。抵御攻击的做法是使用第三方库编译成 native code,使用代码混淆器,或者修改 Python 源代码防止被反汇编。

github多账号如何切换?

作者:知乎用户
链接:https://www.zhihu.com/question/23028445/answer/416231632
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

git多账号切换其实是有https的解决方案的,可以省去ssh配置公钥相关的麻烦,不过安全性会降低,后面会提到。

比如你想用A账号管理本地仓库repoA,用B账号管理本地仓库repoB。
那么首先,看一下gloabal和system的config:

主要是看有没有credential.helper把账号密码存起来了。因为https的url方式每次push的时候都要输入密码,比较麻烦,一般就会用credential.helper把账号密码存在global里了。这样对单用户没问题,但多用户时就会有问题。如果存的是A账户,那在repoB里push的时候肯定就会permission denied。所以看看global或者system哪个设置了保存就unset一下:

第二个命令可能需要权限吧。

接下来就是对本地仓库的config设置了。比如进入本地仓库repoA之后,看一下url:

https开头的就是用的https了,git@ 开头的就是用的ssh了,一般用浏览器打开github仓库页面之后在页面里copy的都是https。一般是长这个样子:

github.com/UserA/repoA.

然后在https://和github.com之间加上用户名@ ,用set-url设置就好:

当然默认是origin分支,要设置其他分支也一样。@ 前的用户名和仓库权限的拥有者要对应起来。
改好之后,这时候push,就要输入一下用户A的密码,然后就能push上去了。对于repoB也是一样。每次push都需要输入密码了。那么为了避免麻烦,针对每一个本地仓库,设置一下local的credential.helper:

这样账号密码就只针对当前仓库保存,对其他仓库没有影响了。针对每一个需要管理的本地仓库,都需要按以上步骤设置一次url和credential.helper,设置好之后,就能一直正常push了。

总结一下:
1. 清空global和system的credential.helper
2. 对每一个本地仓库,设置一下url和local的credential.helper

关于安全性的问题,用git credentials存凭证的话,密码是以明文形式存储的,不论是git-credentials=store还是git-credentials=winstore(windows),git-credentials=osxkeychain(Mac),都有办法直接看到密码明文,除非用git-credentials=cache。当然也可以自定义,参考7.14 Git 工具 – 凭证存储

A4纸尺寸

A4纸是ISO 216(纸张国际化标准尺寸),是世别界上大多数国家所使用的A4纸尺寸。目前中国采用的是ISO 216标准,以规范纸大小,与国际通用。

 

A4纸尺寸

A4纸尺寸:210×297;
A3纸尺寸:297×420;
A2纸尺寸:420×594;
A1纸尺寸:594×841;
A0纸尺寸:841×1189;
备注:长(mm)×宽(mm) 单位:毫米(mm)
A4纸尺寸大小图解

A4纸大小记忆方法

通过上图各大小型号的纸张长宽度数据对比,我们可以看出纸张大小变化的规律,如此我们得出记忆方法如下。
方法:A0纸长度方向对折一半后变为A1纸;A1纸长度方向对折一半后变为A2纸;A2纸长度方向对折一半后变为A3纸,A3纸长度方向对折一半后变为A4纸。
A4规格的纸是我们日常生活中最常用到的,一般只要记住A4是210毫米×297毫米,我们就很快推理出其它规格纸的大小尺寸。

3

A4纸的像素和分辨率

根据A4纸尺寸是210毫米×297毫米,而1英寸=2.54厘米,我们可以得出当分辨率是多少像素时,得出A4纸大小尺寸为多少毫米。如下是我们较长用到的规格尺寸:
  • 当分辨率是72像素/英寸时,A4纸像素长宽分别是842×595;
  • 当分辨率是120像素/英寸时,A4纸像素长宽分别是2105×1487;
  • 当分辨率是150像素/英寸时,A4纸像素长宽分别是1754×1240;
  • 当分辨率是300像素/英寸时,A4纸像素长宽分别是3508×2479;

FROM: http://www.a4size.net/

 

The TeX family tree: LaTeX, pdfTeX, XeTeX, LuaTeX and ConTeXt

The LaTeX story goes back all the way to 1977 when Donald Knuth first understood the need for a high quality typesetting program. It’s a remarkable testament to the quality of LaTeX that the system he conceived back then is still the best tool for the job today, although many useful additions have been made in the decades since. In fact, LaTeX is one of these additions to the original system that Donald Knuth developed, which was called TeX.

To keep your street-cred in the LaTeX-world you’ve got write it with a capital T and X, and pronounce it like Lay-Tech. This is because the X in TeX is actually a capital Chi (χ) from Greek. Latex with a normal capitalisation and pronunciation is a type of rubber!

The original TeX

There are a large family of tools which are now derived from TeX, and it can look like a list of nonsense words on first sight: LaTeX, pdfTeX, XeLaTeX, LuaTeX, ConTeXt, and so on. The ancestor of all of these is of course the original TeX program by Knuth. This takes a document in plain text and transforms it into a beautifully typeset document. Knuth was quite particular about typesetting and the attention to detail in TeX is clear. An example TeX document might look something like this:

The output of this after running it through the TeX program is:

Notice how the combinations of letters ‘fi’, ‘fj’, and ‘ff’ in the example words run together in a pleasant way, and how the mathematics symbols are well spaced. On a larger scale, TeX does a very good job of breaking lines in the appropriate places to created well justified text, with hyphenations where appropriate.

Not only does it typeset text nicely, it also has a selection of commands, like \bye and \TeX in the example above. These can do simple things like change the font size, or change the way text is laid out, as well as much more powerful things like keeping a section count to cross reference your pages, or automatically building up a table of contents. The TeX program has about 300 commands built in, but other commands can be defined within it. Donald Knuth wrote another 600 or so useful commands from within TeX, in a package called Plain TeX which makes some common typesetting tasks easier.

LaTeX

The commands in TeX and Plain TeX are still quite basic and it isn’t easy to do complicated things with them. To help with this, Leslie Lamport created LaTeX in the early 1980s to provide a higher level language to work in than TeX. LaTeX is a set of commands defined in terms of the underlying TeX commands, often at many many layers of abstraction. All of the commands you use in a LaTeX document are actually just complicated sets of TeX commands underneath, unless of course you use a TeX command directly! Concepts like packages (\usepackage{...}), environments (\begin{environment} ... \end{environment}), and document classes (\documentclass{...}) were all introduced by Leslie Lamport in LaTeX.

By creating a standardised package system for LaTeX, Leslie Lamport allowed the community to grow huge. There are now thousands upon thousands of LaTeX packages available to let you typeset anything from subfigures to knitting patterns. There are also many document classes available for different types of document, whether your are writing a book, a lab report or a CV. Many publishers and journals have their own custom document classes that style their documents how they want.

pdfTeX

The TeX program is remarkably stable for a piece of software over 20 years old; Knuth declared that it was feature complete in 1989, and only bug fixes have been made since. Of course, this has not stopped the significant and ongoing development of LaTeX, because that is written in TeX itself. In fact, the stability of TeX has allowed packages like LaTeX which have been built on top of it to thrive.

This isn’t to say the underlying TeX program hasn’t seen any advances in the last 20 years. Far from it in fact, it’s just that improvements have been made alongside TeX, with the original TeX left stable. The most important improvement of the 1990s was the creation of pdfTeX by Hàn Thế Thành for his PhD thesis. The original TeX program outputs the typeset document into a custom format called DVI (DeVice Independent format), that can later be turned into a PostScript file for printing. However, the PDF format came along in 1993 and we can see today that it clearly won as the better format over PostScript. There are lots of features which make PDF better, such as hyperlinks between sections, a section of metadata that lets you see a table of contents in left hand side of your PDF viewer, and support for a wider and more modern range of image formats. pdfTeX is a modification of TeX which allows it to output to PDF directly and so take advantage of these extra features.

When you install a LaTeX distribution on your system today, it actually comes with two different programs: tex, and pdftex. It also comes with another two programs on top of these: latex and pdflatex, but these are actually just wrappers around tex and pdftex respectively. These first load the LaTeX packages before processing your document but are still TeX underneath. If you run latex or tex then you will get a DVI file which you can turn into a postscript file or a PDF, but if you run pdflatex you will get a PDF directly.

Mostly, pdftex and pdflatex are improvements over tex and latex, but there is one downside. The original TeX and DVI format have support for Encapsulated PostScript files (.eps) because these can easily be included in PostScript files (produced by converting .dvi files to Postscript via Dvips). However, pdftex cannot include EPS files, and you should instead use PDFs (which can be produced from EPS files with the command epstopdf which generally comes with LaTeX distributions). In exchange for this downside though, pdfTeX adds support for .png.jpg and .pdf images, whereas the original TeX only supports .eps (via Dvips).

XeLaTeX and LuaTeX

So far we have seen that TeX has evolved in two different ways since its beginning: With the addition of easier to use commands on top of the original system (LaTeX), and with updates to the underlying program to support PDF output (pdfTeX). The story does not stop there however and there have been continuing efforts to modernize TeX. In 2004 Jonathon Kew created XeTeX, which is another modification of the underlying TeX engine, this time to support a wider range of characters beyond just plain English numbers and letters, and to include support for modern font formats. This makes writing in foreign languages much easier, and also lets you use fonts within LaTeX which were traditionally only available to your word processor.

LuaTeX is an attempt to extend the original TeX program with a more sensible programming language. While in principle you can do absolutely anything from within TeX, in practice it’s a very clumsy programming language to work with, if it can even be called a programming language at all. Much of the internals of LaTeX is complicated and hard for outsiders to understand due to having to work only in TeX. LuaTeX is extended with the eponymous scripting language, Lua, which is a simple and stable language, ideal for writing complicated macros. As of 2012, it’s still in active development and is its API is liable to change, but it still very usable.

ConTeXt

We’ve mentioned LaTeX as an extension to the commands available in TeX, but it’s not the only package that has extended TeX in a significant way. ConTeXt is another system which was created in 1990 by Hans Hagen. LaTeX aims to separate the user from having to make decisions about typography and layout (you type \section and \emph but you don’t worry about what these do – that’s left to the document class or layout). On the other hand, ConTeXt aims to provide an easy interface to advanced typography features. Sadly I don’t know as much about ConTeXt as I would like, so I can’t elaborate much further, but if I ever get round to experimenting with it more I’ll be sure to write up my findings.

As a final historical note, I should mention AMSTeX which was an extension to the Plain TeX macros and was used by the American Mathematical Society (AMS) from 1982 to 1985. Its legacy survives in AMS-LaTeX packages which are a staple of many LaTeX documents: \usepackage{amsmath}.

The Future

What will the future hold for TeX and LaTeX? I don’t know yet, but as Alan Kay once said:

The best way to predict the future is to invent it.

With ShareLaTeX we hope to improve TeX and LaTeX in a third direction, by bringing significant improvements to the LaTeX workflow. Despite being over 20 years old, LaTeX is still largely a command line program that must be used in an awkward cycle of writing, compiling and checking the output. Methods of collaboration tend to involve clunky email messages rather than the streamlined processes available for word processing like Google Documents.

In 2 or 3 years time I hope that people will reflect back on online services like ShareLaTeX and be able to say that they have improved the way we work with LaTeX.

 

SOURCE: https://fr.overleaf.com/learn/latex/Articles/The_TeX_family_tree:_LaTeX,_pdfTeX,_XeTeX,_LuaTeX_and_ConTeXt