用 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

Ubuntu: add-apt-repository uses proxy

In order to have console applications use a proxy, the easiest way is to set the proxy in system environment, this can either be done by using command line:

Or simply to set it by setting “Network Proxy” in system settings (Can be found in network panel in Ubuntu 18.04, which may also work on other Linux distributions, but the export command always work.)

After above setting, the system setting will work for many software including wget, etc. however, certain software will be stubborn enough to connect to the Internet directly without the proxy, such as apt-get and apt-apt-repository, apt-get can set its own proxy by configuring

Just as my previous post mentioned.

There is one way to solve the problem:

In addition to configuring proxies, tell sudo to preserve the environment with the -E option:

Notice: -E can be replaced with: -E, --preserve-env 

HERE, you will be able to find further instruction:

https://askubuntu.com/questions/53146/how-do-i-get-add-apt-repository-to-work-through-a-proxy

如何限定apt-get走IPv4或IPv6协议

如果你希望手动控制 Debian 或 Ubuntu 系统在使用 apt-get 更新系统或软件时走 IPv4 或 IPv6,可以通过配置其 Acquire group 选项来实现。Acquire group只有如下两个选项可供我们选择:

  • ForceIPv4:全局强制使用 IPv4 协议下载
  • ForceIPv6:全局强制使用 IPv6 协议下载

当然,如果要使用 IPv6 的话,需要你的运营商和路由器支持,并在操作系统上进行了正确的配置才行。

方法一:手动指定

apt-get全局使用IPv4示例

apt-get全局使用IPv6示例

方法二:配置Bash别名

如果你不想每次都输得这么麻烦的话,可以直接更改 Bash Shell 的配置文件 ~/.bashrc

方法三:创建apt-get配置文件

如果你希望 apt-get 强制使用 IPv6 可以创建如下配置文件

并将其内容填写上:

如果你希望 apt-get 强制使用 IPv4 可以创建如下配置文件

并将其内容填写上:

ubuntu:(设置终端代理IP)简单有效地设置全局代理上网

因为要在ubuntu系统上安装很多软件,但ubuntu系统所在的网络环境是需要使用http代理才能连接网络的。
正常使用火狐等浏览器上网时,可以很简单的设置网络代理服务器,但若是在ubuntu的终端中使用sudo apt-get install 。。。这样的命令,浏览器中设置的代理就不能用了,因此我们必须对ubuntu的终端设置代理,使其能够在apt-get install 时联通网络。
1. 通过export http代理使用apt-get(临时有效)

在使用apt-get之前,在终端中输入以下命令

2. apt.conf文件中配置http代理信息(永久有效)

保存apt.conf文件即可

3. 在.bashrc文件中配置代理信息(apt-get, wget 等等)(全局有效)

保存文件,重新开启终端。

汉语普通话同音文

季姬寂,集鸡,鸡即棘鸡。棘鸡饥叽,季姬及箕稷济鸡。鸡既济,跻姬笈,季姬忌,急咭鸡,鸡急,继圾几,季姬急,即籍箕击鸡,箕疾击几伎,伎即齑,鸡叽集几基,季姬急极屐击鸡,鸡既殛,季姬激,即记《季姬击鸡记》。  (赵元任)


石室诗士施氏,嗜狮,誓食十狮。施氏时时适市视狮。十时,适十狮适市。是时,适施氏适市。施氏视是十狮,恃矢势,使是十狮逝世。氏拾是十狮尸,适石室。石室湿,氏使侍拭石室。石室拭,氏始试食是十狮尸。食时,始识是十狮尸,实十石狮尸。试释是事。  (赵元任,1930)


唧唧鸡,鸡唧唧。几鸡挤挤集矶脊。机极疾,鸡饥极,鸡冀己技击及鲫。机既济蓟畿,鸡计疾机激几鲫。机疾极,鲫极悸,急急挤集矶级际。继即鲫迹极寂寂,继即几鸡既饥,即唧唧。   (佚名)

法国的电信运营商

法国的运营商( 法语FAI )分成两大类,MNO(Mobile Network Operator,基础运营商)和MVNO(Mobile Virtual Network Operator,虚拟运营商)。

所谓MNO(基础运营商),是指拥有自己的网络频段和基站设备的电信运营商,目前法国拥有四家基础运营商,其中最传统的三家(也就是所谓“御三家”)是:ORANGE(前身是FRANCE TELECOM 法国电信),SFR,BOUYGUES。法国的基础运营商MNO除了提供电信、互联网和有线电视这“三网”基础服务,还出租自家的基站给其他不具备独立基站设备的电信公司,而后者这些公司便被称为MVNO(虚拟运营商)。Free原本是MVNO,但是最近也在法国境内拥有自己的基站,成为了法国第四家MNO。只不过Free的独立基站数量暂时比较有限,因此自主铺设的网络覆盖率还很低,很大程度上还依赖于租借“御三家”的基站设备完成自己的网络覆盖,也尚未把自己的网络资源转租给别人。

MVNO(虚拟运营商),指的是没有自己的网络频段和基站设备的电信运营商。为了实现自己的运营,需要租用MNO(基础运营商)的网络和设备。MVNO出现的重要意义在于打破了传统MNO在电信市场上的垄断!因为传统的MVNO一般都是主营价格低廉的产品以及无合约套餐,也没有专门的店面,大多数通过网络的方式订购,给用户最大的实惠。而MNO则有大量的店面,兼卖手机等通信产品,手机套餐费用相对较高,用户一般都是签订合约机,利用订购较贵套餐的方式来获得购买手机的优惠。

需要指出的是,另外还有一些广义上是MVNO,但其实是代理商(如phonehouse,M6)的运营商,本文就不讨论了。

法国通过这两种运营商的格局,一方面可以使得MNO可以做到不浪费自己的网络资源,尽可能把网络设备转化成利润;另一方面MVNO则可以自由打包自己的通信产品,降低了市场的价格,满足不同的人群的需要。

市面上的手机卡类型

法国市面的手机卡类型一般可以分成:预充值卡 (carte prépayée),无合约套餐 (forfait sans engagement),合约套餐 (forfait avec engagement) 。

预充值卡 (Carte prépayée)

使用预充值的付费方式,一般需要用户自己买充值卡或者网上提前充值,预存的话费用完便不能再使用,类似于国内中国移动的“神州行”“动感地带”等品牌。这种手机卡使用起来比较方便,不需要使用个人信息注册也无需和银行账号绑定,很适合刚刚来法国的童鞋在银行账户尚未办理好的时候临时使用。(等到办好银行账户后可以采用“保号”方式转成其他套餐,手机号码不变!)当然也很适合来法国旅游的人!

无合约套餐 (forfait sans engagement)

需要与银行账户绑定,每个月从绑定的银行扣取一定的套餐费用,同时享受套餐内的通信资源(通话/短信/上网流量等)。如果使用超出套餐内的资源,则会自动从银行账户中扣费。无合约套餐以月为周期生成账单并扣费,所谓“无合约”,指的是可以随时解除该套餐合约,而无需支付任何收费用。

合约套餐 (forfait avec engagement)

服务形式类似无合约套餐,但需要与运营商签订合约,使用改套餐一定的期限(一年/两年)。如果提前解除合约,需要支付一定的解约费用。签约套餐一般是签订合约机时选择,或者是搭配该运营商的上网盒子(BOX)一起使用。虽然套餐费用较贵,也不能随意解约,但是一般可以低价甚至免费购买到手机,或者是同上网盒子(BOX)一起享受一定优惠。

无合约套餐介绍(强烈推荐)

在以前,合约套餐以前一直是法国消费者的唯一选择,因此很多法国人抱怨说签了合约就像卖身契一样。这是因为法国电信业的垄断相当厉害且稳固,“御三家”通过抬高套餐价格,来推销手里的合约机,帮各手机商赚钱(因为门店里的店员一般都是推销手机的,如果只销售低价套餐的话,将难以成事门店的成本)。

而MVNO的出现打破这个垄断的市场。不少MVNO以无合约套餐来招揽用户,其价格比起御三家要实惠得多。特别要提到的是Free以超低的价位杀入市场,给法国的电信市场带来巨大的冲击,其作用几乎等同于订立了一个行业标准,使得其他运营商——包括御三家——纷纷推出了与之相仿的无合约套餐,形成了法国电信业的一股新的行业风气。

在2011年下半年,“御三家”也都相应推出了用来销售无合约套餐的低价品牌,但是与自己本家的品牌分开经营,这明摆着是要从Free和其他MVNO的“无合约套餐”市场中分一杯羹。其他的MVNO也及时调整了自己的价格,推出了与Free相仿的套餐,也即所谓“同质化竞争”,这是法国电信业目前的一个重要特性。

本文要介绍的无合约套餐的运营商,包括了御三家的低价品牌(Orange副品牌Sosh,SFR副品牌RED,Bouygeus副品牌B&You),Free,以及其他一些常见的MVNO的无合约套餐。

总体上来说,无合约套餐有这么几个特点:

– 低价:无合约套餐的竞争将价格压到最低。在Free出现后,无合约套餐的行业价格基本是:“无限”套餐仅需20欧/月甚至更低(包括无限通话时间,无限短信和彩信,高达3G/月的高速上网流量,超过后转为低速上网流量无限)

– 几乎全是网上订购:为了降低成本,提供无合约套餐的运营商(包括“御三家”的低价品牌)通常没有门店,仅提供网签的形式,在网上输入个人信息、保号信息、银行RIB信息(什么是法国银行的RIB)等,无需长居,护照复印件等个人身份证明之类,可直接在网上完成注册过程,甚至不需要签纸质合同。sim卡将邮寄至信箱,通常几天到一周内可以送到。理论上,尽管有自己的门店,但御三家下属的无合约套餐品牌(sosh,red,b&you)也必须通过网络订购。对于只提供网签的运营商,如果用户实在需要当场办理并拿到sim卡的话,也可以去phoneo之类的第三方门店购买。

换套餐、退订非常自由,通过保号可以随意在各家运营商中切换:如果想要更改套餐的话,只需要联系客服或者自行在网上修改即可;退订套餐(不再使用这个号码)的话有些需要寄挂号信,有的只需要电话退订即可。这里非常推荐“保号”操作——法国所有的运营商都支持保号服务。“保号”是一种保留手机号码,更换手机运营商的操作(如果在同一个运营商提供的不同套餐间更换,则不属于“保号”)。选择以“保号”方式认购新运营商的套餐后,无需任何操作,你和旧运营商的套餐就被退订了。这里还有一个用“保号”省sim卡费的小技巧:部分运营商第一次申请sim卡免费或只要1欧,但丢失后补卡则需要10欧甚至更高。如果真的丢失,你甚至可以选择先“保号”转移到另一家sim卡免费的无合约套餐运营商,用一个月之后再转回原来的,这样能在保留号码的情况下省掉这笔sim卡补卡费用。当然实际这样操作有时候会引起运营商注意,并不十分推荐。

终上所述,无合约套餐非常适合已经有手机的用户。对于打算新买手机的用户,“裸机+无合约套餐”的形式很多时候也比合约机要合算,特别是你要明白“无合约套餐”意味着你可以随时换更优惠的套餐,而签订“合约机”虽然买手机便宜了些,但也承诺了一年甚至两年要一直用这个较贵的套餐。所以,各位无手机用户也不妨将这一套餐形式当作选择之一,与合约机比较后再做决定。

也有一些手机运营商会在无合约套餐基础上搭配销售手机,然后由手机生产厂家提供一定额度的返款(ODR,详见“法国的返款ODR促销方式详解 ”),对于需要手机的客户,也是一种不错的选择。

哪家运营商的信号好/服务好?

关于信号:

关于手机信号好坏,并没有什么实际的意义,从MVNO的定义可以看出,法国所有运营商基本都基于御三家的网络,三家的覆盖率都达到了全法98%以上(当然Free仍在不断建设自己的基站),官方并没有任何关于“御三家”网络覆盖率高低的公开说法(可能为了防止竞争)。在实际生活中,的确会在郊外或者地下空间内察觉到不同公司间的信号好坏,可能收的到orange但是收不到bouygues等等,但这更多的情况下是看人品的。如果需要查询某个地点(比如家中)是否能接收到良好的电信信号,可以专门到官网查询:

Orange:http://couverture-reseau.orange.fr/france/netenmap.php

SFR:http://assistance.sfr.fr/mobile_forfait/mobile/couverture-reseau-sfr/en-48-71060

Bouygues Telecom:http://www.cartographie.bouyguestelecom.fr/eCouverture/eCouverture.aspx

关于服务

会有一些差别,但比较意义也不大。前面都说了这类无合约套餐全部都是网签的,客服基本只是在遇到问题时才会接触到的。如果没有什么问题,从用户购买到退订,特别是保号退订的情况下,很可能连一次与客服的交流也没有,所有的过程都是在网上自动完成的。当然,在不靠谱的法国,还是经常会遇到这样那样的问题的,这个时候客服的态度的确会影响到服务的质量。但其实服务人员的好坏取决于其本身的素质(甚至心情),与公司运营策略等关系并不直接。而且你会经常发现,不同的工作人员经常对本公司自己的销售条款作出完全不同的解读。所以,如果对客服的答复不满意的话,可以尝试咨询其他的客服人员,完全有可能会给出一些更能令人接受的答复。过于纠结于比较这些公司之间的服务好坏,我个人认为也是没有必要的。比起去纠结哪家的客服态度好,还不如好好研究下网站上白纸黑字写的那些销售和服务条款,特别是那些小字部分才是最需要我们注意的。

有这么多运营商,我该选择哪个套餐?

具体当然得看每个人的要求了。每个运营商基本上都有自己的特点,适用于不同类型的人群。这里介绍各种常见的无合约套餐运营商的价位和特点,方便选择。

所有信息都取自于网络,而且套餐信息和价格经常会更改,一切信息请在购买前登陆相应官网核实。

运营商
特点
SOSH(Orange低价品牌)
推荐24.99E无限套餐,在欧洲漫游时享受:漫游流量5G/年,漫游短信无限,通过Libon应用无限打法国电话。
RED(SFR低价品牌)
和SOSH基本一样,推荐25.99E套餐(如果保号前半年22.99E),享受漫游流量5G/年;SFR WiFi热点也多。
B&YOU(Bouygeus低价品牌)
强烈推荐19.99E的无限套餐,可免费无限拨打中国座机和手机。
强烈推荐19.99E的无限套餐,可免费无限拨打中国座机和手机,FreeBox用户只需15.99E。另外也推荐2E的forfait作为副卡,起码接电话不要钱,FreeBox用户该套餐免费。
套餐可自行定制,随时在网上更改,甚至开关。也适合更看重网络流量的用户
所有套餐都带上网流量并且价格较低。
有29.99欧/月无限打世界各地座机和手机的套餐(只需中国的话见B&You或Free)。
无限套餐(通话,短信,网络)中价格最低:16.99欧/月。
甚至不用你特地去换套餐,自动调节套餐月费。
15.99欧/月即可无限畅打中国;5.99欧一档亦可将通话时间用在国内。
无限量套餐中价格最低:仅需9.5欧/月即可无限通话+短信+100M流量

需要指出的是:各运营商也会不定期有各种促销活动,活动时办理套餐可以享受更优惠的价格。

法国的移动网络制式?国内手机能否使用?

法国所有的运营商都采用同样的制式:

2G网络采用的是GSM;

3G网络采用的是WCDMA;

4G网络采用的是FDD-LTE;

如果要从国内带手机:只需要用2G网络的话,移动和联通的手机都可以带;如果要用到3G网络的话,只能是支持联通3G的手机;关于4G网络,国内由于FDD-LTE和TDD-LTE混合组网,比较乱,也建议带支持联通4G的一般没什么问题。

刚来法国,没有银行账户/备用手机号,能否办理无合约套餐?

如果是刚来法国没有银行RIB的话(什么是法国银行的RIB),是没有办法签订无合约套餐的。

技巧:先以你最方便的方式买一张预付费的充值sim卡,去tabac、超市一般都能买到。这样可以先得到一个手机号。随后在办理各种手续的时候,都可以填入这个号码作为你的联系方式。虽然预付费sim卡资费较贵,但是刚来法国的话需要打出的电话不会多,顶多作为一个能让别人联系自己的途径,而接电话一般都是不要钱的。

然后,等你开银行账户拿到RIB之后(一般不需要等到办下来银行卡),再使用“保号”方式认购套餐。只需要在下订单时申请保号,那么你的号码还是保持不变,别人依旧可以通过同一个号码联系你。

什么是保号?

保号(Portabilité de Numéro)类似于中国的“携号转网”,指的是在保留用户手机号码不变的情况下更换运营商(在同一个运营商内更改套餐不属于“保号”)。

保号系统是由ARCEP(法国电子通信与邮政监管局)在2007年5月21日实现的一个独立于各运营商的系统。所有保号过程都可以自动完成。最初,保号所需时间至少10天,2011年11月7日后,ARCEP缩短了保号所需时间,只需三个工作日即可实现保号。

进行保号操作时有这样几项需要注意的:

– 需要RIO号 (Relevé d’Identité Opérateur):每一个手机号码在某个运营商内有一个类似于合同号的号码。通过该手机拨打3179即可获得通过语音或短信获得这个号码,可能需要在6:00~22:00间才能拨打才行。如果无法获取,联系自己的旧运营商咨询即可。

– 选择“保号”,旧手机号必须是自己名下的么?

并不是,只要有该手机号码的RIO号码就行。旧套餐完全可以是用别人RIB签的,或者是根本不记名的Carte prépayée预付费手机卡,然后“保号”转成用你的RIB付款。

– 千万不要自行解除旧运营商的合约:“保号”过程会自动解除旧合约,如果你申请了保号又自行解除了旧合约,不仅毫无必要,还可能造成“保号”失败。由于保号需要一定处理时间,一般在合约快结束的时候,或者是月扣费周期快结束时,提前10天左右选择保号认购新套餐即可,完全可以在新的扣费周期到来前,解除你的旧合约。

– 保号的“交接”时间一般是可以自由选择的:而在你指定的“保号交接”时间到来前,你应该已经收到了新的手机sim卡了。这里会出现两种情况:情况一(有临时号码):在保号交接时间前,可以激活新sim卡,并分配给新sim卡一个临时号码,新套餐的费用也从sim卡激活日期开始算起,旧sim卡也能正常使用,而在“保号交接时间”,新sim卡的临时号码作废,变成“保号”的号码,而旧sim卡也同时不能使用了。情况二(无临时号码):在选定的“保号交接”日期前,新sim卡不提供临时号码(因此也不能正常使用),旧sim卡可以正常使用,新套餐计费从保号成功日期算起。

– 选择“保号”是否会导致一段时间手机不能用?

基本不会。因为旧sim卡在保号交接日前一直有效,直到保号交接的时刻才会自动注销,此后,新卡就能够正常使用了。由于技术原因,有可能在“保号交接”的过程中,会出现几个小时的时间服务不稳定,但基本不会太久。如果新卡有临时号码而且因为某些原因保号失败的话,新卡的临时号码也将一直有效。

– 使用预付费sim卡“保号”转套餐后,旧卡余额怎么办?

如果旧sim卡是预充值卡 (Carte prépayée),保号后旧sim卡中的余额很可能作废,具体情况需要咨询旧的运营商,和保号的技术过程无关。

– 避免选择“保号”后取消订单

如果在申请保号的订单生成之后几天决定取消订单,那么你的这个手机号码有可能因为技术原因自此永久丢失。因为保号需要一定准备时间,如果已经指定保号日期,系统开始处理保号,旧合约已经开始解除,而你又取消了新订单……就会导致这种情况。

– 已签订合约套餐还没有到期,是否可以保号订购无合约套餐?

如果已签订合约还未到期就进行解约,一般需要支付违约金。保号这个过程带有“强制性解约”的功能,旧运营商会视作用户提前解约而要求客户支付违约金。

根据Loi Chatel,如果是两年的合约的话,已执行合约一年以上(包括一年)的,可以在仅支付剩余月费的1/4的情况下解除旧运营商的合约。比如还剩一年整的话,违约金就是三个月的月费;还剩四个月,违约金就是一个月的月费。因此在换套餐之前,自己可以算一笔账看看是否真的划算。

具体违约金数额需要看旧运营商的条款,最坏的情况可能是支付全部剩下的月费作为违约金咯。

这里需要特别注意的是旧合约自动续期的问题。如果你希望能够“尽可能充分”地享受上一份合同的服务,也就是希望在旧合约到期的最后一个月末进行保号操作。请注意,如果不进行任何操作的话,在前合约逾期之后运营商一般会自动给用户续期一年或两年合同,这恐怕是绝大多数人想要避免的情况。因为旧合约是一致生效的“保号交接”的时间的。本文的建议是:稍微提前一些,在合约到期前半个月左右保号订新套餐,这样扣去寄新sim卡和保号的时间,也能够保证在旧合约到期前完成保号交接。具体时间可以根据运营商的政策自己把握,但总而言之尽可能要避免合约逾期自动续约这一最糟糕的情况。

– 无合约套餐“保号”换运营商,旧套餐最后一个月如何收费?

旧运营商将以“保号”生效的一天为解约日期。目前,绝大部分运营商对无合约套餐在涉及解约的最后一个月,都会按照“天”来收取套餐费。也就是:套餐月费用 ÷ 30 × 实际使用天数。所以大可不必担心因为旧套餐仅用了几天而被收取整月套餐费。

总之,相对于申请新号,保号有着不少好处:

首先,号码不变——这既方便了用户,也节省了电信公司的网络资源。

其次,省了自己解约旧运营商合约的过程。保号这个过程本身带有“强制性解约”的功能。否则的话,即使旧套餐是无合约的,也可能需要打电话甚至发挂号信去解约,费心费力。

最后,不少公司对于从其他运营商保号转入自己公司的新用户会有一些优惠,比如免去sim卡费用,或者在保号签合约机时获得一定返款等。

所以结论是:如希望更换手机运营商,没有非常特殊的需求的话,保号是最佳选择。

什么是Forfait Bloqué

forfait bloqué是在订购套餐中常见的一个选项(如SFR),如果没有选择“无限”套餐,那么套餐的月费中会包括定量资源,如果选择了forfait bloqué,那么打光了月费包含的时间就不能再打出去了(短信、上网流量也一样)。而forfait non bloqué的用户在打完月费中的资源后,仍然可以继续使用套餐外资源,超出资源会按用量计费并在月底与套餐费一起结账,额外资源的单价详见运营商官网,但一般不会很贵,至少预充值sim卡 (Carte prépayée) 要便宜的多。唯一的缺点就是手机被盗的情况下仍有被人打爆了的风险。

 

 

“无限”套餐是否真的不限量?

在无合约套餐的介绍中,经常会出现这个字眼——无限(Illimité)。那么,运营商声称的所谓“通话无限,短信无限,上网无限”,真的是没有任何限制吗?

并不是。各运营商在自己的经营条款里都标明在其无限套餐(Forfait illimité)中的限制,当超过这些限制的时候运营商仍然需要额外收费。所以,即使订购了所谓“无限”套餐,在月底收到一张比高于月费的账单也不是不可能的事。

一般来说,这些限制可能体现在这些方面:

  • 单次通话的时长(很少有运营商限制)
  • 每月通信(通话或短信)的不同联系人的人数(防止用来群发垃圾短信)
  • 高速(在3G/4G网络)上网流量

重点解释一下最后一条:大多数运营商都限制每个月只有一定的高速流量(在3G/4G网络下),超出这个流量则切断高速上网功能,最高只允许EDGE(2.75G)上网,对应的下载速度也就30-40Kbyte/s的样子,基本跟不上现代节奏。另外,除非特别说明,大多数运营商原则上不允许手机网络用作热点或者过度使用网络电话等服务,如果被运营商监测到,可能会收到罚单

因此,尽管在宣传上写着诱惑人的Internet illimité,但实际也不可能完全像wifi上网一样享受手机网络。如果平时只是看看短信发发微博之类,甚至偶尔在运营商允许的情况下打网络电话等都是可以自由享受的,但如果要下载文件、看在线视频之类的,恐怕就要悠着点了。

附:各运营商“无限”中的限制比较 :

单通电话时长
一个月通信的联系人人数
高速上网流量
SOSH
3h
250
3G
RED
3h
未知
6G
B&YOU
3h
199
3G
FREE
未知
未知
3G
VIRGIN
3h
199
3G
NUMERICABLE
2h
99
3G
ZEROFORFAIT
2h
100
500M
PRIXTEL
2h
199
750M
Budget Telecom
1h
20
不包括
SIMPLUS
2h
100
500M
Coriolis Telecom
3h
99
500M

以上信息因为随时可能被运营商修改,所以仅作参考。

漫游费用的问题

在欧洲生活,遇上放个假什么的,大家都是随性就跑到其他欧盟国家去来一场“说走就走的旅行”了。这个时候,手机套餐的漫游费用一般如何呢?

首先,法国的手机运营商完全没有像中国有“跨省漫游”这么一说,在法国境内完全是执行同样的资费。只有在其他国家使用手机,以及法国海外省(DROM)和海外领地(CTOM)使用手机,才可能产生漫游费用。

然后,小编想说几个身边的朋友经常混淆的情况:

– 所谓“漫游费用”,都是指你在法国本土以外的国家使用手机(包括拨/接听电话、收发短信、上网等);而不是指你在法国本土,给国外的电话号码打电话或者发短信(这应该称为“长途费用”);

– 你和你的朋友一起去意大利玩耍,你们两个人都是用法国运营商的手机号在意大利漫游。这时你给你的朋友打电话,目的地算是打往(vers)法国,还是意大利?小编答:这个情况算是从(depuis)意大利打往(vers)法国,都按运营商号码来区分目的地,而不是对方手机目前处于哪里。

– 我在法国本土,接听我朋友从国外打来的电话,算漫游么?小编答:当然不算,所谓“本地接听”,是指你在本地接听电话,而不管你接听来自哪里的电话。

关于具体的漫游费用,你需要查阅你所订购的套餐的详细资费信息,根据你在哪个国家(depuis),往哪个国家拨打电话(vers),确定具体的资费情况。

但是,也有一些漫游中省钱的小技巧。例如Free的“Pass Destination”可以让你每年35天在多数欧盟国家漫游时享受和法国本土同样的资费;“SFR Voyage”计划可以享受比较低廉的漫游资费。具体情况可以搜索这些关键字,查看官网的最新资费说明。