Win10上误删Linux子系统的解决方法
最近在折腾Win10里的WSL的时候,不小心把WSL的目录删除了,然后无论如何再也装不上去。运行bash命令会报以下错误:
1 | C:\>bash |
百度、Google均无解,于是想到使用英文去搜索,终于找到了下面这篇帖子:
以下是解决方法:
1 | C:\>wslconfig /list |
此时,运行bash命令也正常了
1 | C:\>bash |
终于可以正常安装WSL了。
QT4A重打包实现原理
一次Art Hook失败问题的跟进
Ubuntu部署squid代理服务器
0x00 前言
squid是一款高性能的代理缓存服务器,常用来部署HTTP(S)代理服务器。本文是在Ubuntu上使用squid部署HTTP(S)代理服务器的方法总结。
使用的Ubuntu版本是:Ubuntu 16.04 x64。
0x01 安装和配置
使用如下命令安装squid:
1 | apt install squid -y |
安装后,会在/etc/squid目录下生成默认的配置文件squid.conf,需要对其做一些自定义的修改.
修改默认端口
将http_port 3128这行中的3128修改为期望的端口号,比如8080,或是非常用端口,这样可以避免服务被shodan之类的搜索引擎探测到。
允许外部访问
squid默认只能从本地访问,是因为它设置了http_access allow localhost。
但正常情况下,我们都是需要从外部访问的,这就需要添加以下两行配置:
1 | acl net src 0.0.0.0/0 |
表示接收任意外部地址。
允许CONNECT所有端口
squid默认只可以CONNECT443端口,如果要开放所有端口,需要注释掉http_access deny CONNECT !SSL_ports这行。
修改安全端口
squid默认策略只允许代理访问以下端口:
1 | acl Safe_ports port 80 # http |
因此,会有部分端口无法访问,直接返回403 Forbidden。如果需要访问这些端口,可以增加以下配置:
1 | acl Safe_ports port 1-1024 |
不允许访问本地网络
squid默认允许访问本地(localhost)服务,但建议去掉#http_access deny to_localhost的注释
允许所有访问
如果觉得以上操作过于繁琐,在不考虑安全性的情况下,也可以修改http_access deny all为http_access allow all
设置访问密码
为了安全,我们通常会给代理服务器设置密码。
先安装htpasswd工具,使用如下命令:
1 | apt install apache2-utils -y |
创建密码文件:
1 | htpasswd -c /etc/squid/passwd proxy_username |
在squid.conf中添加以下内容:
1 | auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwd |
0x02 启动squid服务
1 | systemctl start squid |
使用PyInstaller打包可执行文件
0x00 前言
之前都是使用py2exe将Python程序打包成可执行文件,但是最近需要打包成Macos上的可执行程序。于是,选择了py2app,但是使用下来发现坑比较多,最终还是放弃了。
于是,将目光转向了PyInstaller,它可以同时支持Windows和Macos,并且使用方法差异也很小。
PyInstaller与py2exe的主要差异(Windows):
PyInstaller打包出来的是一个正常的exe;py2exe打包出来的既是exe,也是zip文件,可以进行解压
PyInstaller打包出来的程序在运行后会创建一个临时目录,把dll等文件解压到临时目录中;py2exe使用了内存加载dll的技术,可以在不解压dll的情况下直接加载,看上去更优雅一些
Python中动态创建类的方法
0x00 前言
在Python中,类也是作为一种对象存在的,因此可以在运行时动态创建类,这也是Python灵活性的一种体现。
本文介绍了如何使用type动态创建类,以及相关的一些使用方法与技巧。
0x01 类的本质
何为类?类是对现实生活中一类具有共同特征的事物的抽象,它描述了所创建的对象共同的属性和方法。在常见的编译型语言(如C++)中,类在编译的时候就已经确定了,运行时是无法动态创建的。那么Python是如何做到的呢?
来看下面这段代码:
1 | class A(object): |
在Python2中执行结果如下:
1 | <class '__main__.A'> |
在Python3中执行结果如下:
1 | <class '__main__.A'> |
可以看出,类A的类型是type,也就是说:type实例化后是类,类实例化后是对象。
0x02 使用type动态创建类
type的参数定义如下:
type(name, bases, dict)
name: 生成的类名bases: 生成的类基类列表,类型为tupledict: 生成的类中包含的属性或方法
例如:可以使用以下方法创建一个类A
1 | cls = type('A', (object,), {'__doc__': 'class created by type'}) |
输出结果如下:
1 | <class '__main__.A'> |
可以看出,这样创建的类与静态定义的类基本没有什么差别,使用上还更灵活。
这种方法的使用场景之一是:
有些地方需要传入一个类作为参数,但是类中会用到某些受外界影响的变量;虽然使用全局变量可以解决这个问题,但是比较丑陋。此时,就可以使用这种方法动态创建一个类来使用。
以下是一个使用的示例:
1 |
|
在上面的例子中,由于目标服务器地址是由用户传入的,而PortForwardingRequestHandler类的实例化是在ThreadingTCPServer里实现的,我们没法控制。因此,使用动态创建类的方法可以很好地解决这个问题。
0x03 使用元类(metaclass)
类是实例的模版,而元类是类的模版。通过元类可以创建出类,类的默认元类是type,所有元类必须是type的子类。
下面是元类的一个例子:
1 | import struct |
以上代码在Python2.7中输出结果如下:
1 |
|
在Python3中,metaclass的定义方法做了修改,变成了:
1 |
|
为了兼容性。可以使用six库中的方法:
1 |
|
使用元类的优点是可以使用更加优雅的方式创建类,如上面的c_ubyte * 5,提升了代码可读性和技巧性。
0x04 重写__new__方法
每个继承自object的类都有__new__方法,这是个在类实例化时优先调用的方法,时机早于__init__。它返回的类型决定了最终创建出来的对象的类型。
请看以下代码:
1 |
|
输出结果如下:
1 | <__main__.B object at 0x023576D0> |
可以看到,明明实例化的是A,但是返回的对象类型却是B,这里主要就是__new__在起作用。
下面的例子展示了在__new__中动态创建类的过程:
1 |
|
结果输出如下:
1 | Hello World |
这个例子实现了动态创建两个类的子类,比较适合存在很多类需要排列组合生成N多子类的场景,可以避免要写一堆子类代码的痛苦。
0x05 总结
动态创建类必须要使用type实现,但是,根据不同的使用场景,可以选择不同的使用方法。
这样做对静态分析工具其实是不友好的,因为在运行过程中类型发生了变化。而且,这也会降低代码的可读性,一般情况下也不推荐用户使用这样存在一定技巧性的代码。
Python和JavaScript中的生成器与协程
0x00 前言
Python和JavaScript中都有生成器(Generator)和协程(coroutine)的概念。本文通过分析两者在这两种语言上的使用案例,来对比它们的差异。
0x01 Python中的生成器
Python中的生成器简介
使用过Python的同学对生成器的概念应该是很熟悉的,一个经典的例子是使用它生成斐波拉契数列。
1 | def fab(max): |
输出结果如下:
1 | >>> for n in fab(5): |
在Python中,使用了yield的函数不再是普通函数,而是一个生成器函数,执行它返回的是一个生成器对象,可以进行迭代,可以调用next函数获取下一个值。
1 | >>> fab |
yield也支持使用send方法进行参数传递。
1 | def gen_test(): |
输出结果为:
1 | 2 |
创建生成器函数后,需要先调用一次next函数,否则程序会报以下错误:
1 | TypeError: can't send non-None value to a just-started generator |
yield最大的特点是允许代码发生中断,并在调用next或send时继续往下执行。
Python中使用生成器实现协程
协程是一种通过代码实现的模拟多线程并发的逻辑,其特点是使用一个线程实现了原本需要多个线程才能实现的功能;而且由于避免了多线程切换,提升了程序的性能,甚至去掉了多线程中必不可少的互斥锁。
协程最大的一个特点是用同步的方式写异步代码,提升了代码的可读性,并降低了维护成本。
协程与多线程的主要差别如下:
- 协程只有一个线程,多线程有多个线程
- 协程中任务(逻辑线程)的切换是在代码中主动进行的;线程的切换是操作系统进行的,时机不可预期
- 进程中可以创建的线程数量是有限的,数量多了之后产生的线程切换开销比较大;协程可以创建的任务数量主要受CPU占用率、文件句柄数量等限制
由于Python中GIL的存在,多线程实际上并无法利用到多核CPU的优势。这种情况下使用协程 + 多进程无疑是最优实现方案。
yield天生的特性,为实现协程提供了极大的便利。
Python中使用生成器实现协程的典型库是:tornado。即便是自己实现也不是很复杂,基本原理就是维护一个事件队列,保存生成器对象,不断取出队列前面的生成器对象,去调用send方法,进行参数传递,从而维护了函数调用链。
下面是使用tornado的一个例子:
1 |
|
不过tornado中还是有很多地方需要写回调函数的,个人觉得这些地方实现得不是很优雅。
Python从3.5开始支持async和await关键字,从而在语言层面支持了协程。但是使用生成器实现协程的兼容性会更好。
0x02 JavaScript中的生成器
JavaScript中的生成器简介
JavaScript中可以使用function*创建生成器函数,这是在ES6规范中提出来的,Chrome从版本39才开始支持这一特性。
使用JavaScript生成斐波拉契数列的代码如下:
1 | function* fab(max) { |
执行结果如下:
1 | > x=fab(5) |
可以看出,使用方法与Python中是基本一致的,不过,JavaScript中并没有send方法,但是next是可以传参的,相当于结合了Python中next和send的功能。
JavaScript中使用生成器实现协程
JavaScript天生是一个单线程的环境,一般不能使用阻塞的操作,传统的实现多采用异步回调(callback)方式。但是,这种方式容易导致层层嵌套,变成回调地狱(Callback Hell),阅读和调试都不是很方便。
后来出现了Promise,可以用优雅一些的方法编写异步代码,但是仍然不够优雅。于是出现了基于生成器和Promise实现的co库,这个库目前只有200多行代码,可以将生成器函数变成Promise对象,并自动执行。它支持yield一个Promise对象,其效果与async和await(Chrome 55开始支持)相似。
co代码链接为:https://github.com/tj/co/blob/master/index.js。
关于co的具体介绍可以参考这篇文章。
以下是使用co的一个例子:
1 |
|
执行结果如下:
1 | Wed Jul 18 2018 14:39:44 GMT+0800 (中国标准时间) |
这里两次打印时间差了3秒,怀疑是执行误差所致。使用async和await也是如此,尚未找到具体原因。
如果只是不断调用gen_sleep的next函数,是不会进行sleep操作的。
使用async和await实现以上的功能,代码如下:
1 |
|
可以看出,这两种方式都可以实现协程的效果,但是后者是语言官方支持,应该会成为主流。
0x03 总结
从上面的例子可以看出,两者对生成器和协程的使用有很多相似之处,可以说是大同小异。在理解了语言的这些特性之后,编写协程代码会更加地轻松。
总的来说就是:语言都是相通的。