尹良灿得闲

巴甫洛夫很忙……巴甫洛夫正在死亡

[Python] 校园网网页验证登录脚本

| Comments

依旧毫无技术含量 =。=,走了很多弯路,很多原理也没深入学习,不过还是记录一下吧。。。

学校的校园网可谓关卡重重,先要锐捷验证一次,然后还要在网页端登录一次,如果一段时间没有网络活动的话又会自动下线(挂 Q 也会)。

之前写过一个自动填写验证码的小书签,后来改成了油猴脚本,让浏览器记住帐号和密码后打开浏览器就能自动登录了。

可是能不能把这一步也省了,开机登录桌面就能直接上网呢? 因为有一些开机启动的程序是需要网络的。比如 Docky 的天气插件,获取不到数据就会变成一个问号难看死了(主要因为强迫症,每次网页登录后都要手动点击一下 Check Weather 把那个问号变回天气图标,虽然它会定期自动检查。。。),还有就是 Dropbox,因为这个原因我一直都没设置开机启动。

所以就想看看能不能用 Python 来实现这个功能了。

因为对这一切都毫无概念,所以一开始只能用固有的思维方式考虑问题: 不通过浏览器,就是要用其它语言实现填写文本框然后按确认?或者通过其它语言间接调用 Javascript ? 导致在这个上面绕了好久,看了半天 Pyv8, Python-Spidermonkey 什么的也没弄懂怎么回事 T^T

其实填写文本框然后按确认按钮的过程就是填写一个表单(<form>),然后浏览器把表单中的数据提交到相应服务器的过程。而 Python 或者其它的一些语言都已经有相应的库提供 post/submit 网页表单数据的方法了。所以现在需要做的就是找出 提交数据的地址 和 需要提交的数据项,然后使用对应的方法提交数据。

提交数据的地址可以由 网页文件中 <form> 标签的 action 属性找到。

  • 校园网的验证页面地址格式为:http://address/login.jsp?wlanuserip=xxx.xx.xxx.xx&wlanacip=xxx.xxx.xx.xx
  • action 的值为:”/login.do”
  • 所以提交地址就是:http://address/login.do

提交的数据就是 <form> 里的 文本框的输入值,有 5 个。两个属性 type 为 “hidden” 的 <input> 标签,叫做 eduuser 和 edubas,它门的值分别对应验证页面地址中的 wlanuserip 和 wlanacip 的值,前一个是我的内网 ip 地址,后一个没搞明白是啥。

另外 3 个是 userName1,password1,rand,就是用户名,密码,和验证码。 其中验证码在写小书签时已经知道,它的生成方法其实是由在浏览器中的 Javascript 产生一个四位数字,然后向服务器请求一张相应数值的图片。

小书签里使用 js 获取执行结果,不过现在我要直接 post 数据并不解释网页。既然那个四位数是客户端脚本所产生的,就是说并不由服务端所控制,所以现在我可以伪造这个事实,直接由我向服务器请求一张图片,而且那个数值也不用随机生成,找一个任意确定的四位数就可以了。请求的方式是访问这样一个格式的地址:http://address/common/image.jsp?rand=xxxx

其实到这里我原本的想法是,服务器应该会把对验证页面地址和验证码图片地址的访问作为同一个 session,就是服务器会记录下你请求了的图片的验证码数值,在提交表单时里面的 rand 项的值必须与之相符才能通过验证(关于 session 什么的我不太知道是不是该这么表达)。 可事实是,那个验证码图片的请求不是必须的,就是说我可以直接随便提交一个四位数作为 rand 的值也能通过验证!!! 这个页面的验证码的部分除了那张图片,全都是由客户端 js 来完成的 Σ(っ °Д °;)っ

整个脚本所做的事情就是,提交一个包含验证信息的表单。

autologin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python

import requests, time

url = "http://address/login.do"
info = {'userName1': '...',
        'password1': '...',
        'rand': '2222',
        'eduuser': '...',
        'edubas': '...'
       }

while True:
    requests.post(url, data = info)
    time.sleep(65)

这里用了 requests 这个模块,设置每 65 秒验证一次防止自动下线(表问我为神马是 65 而不是 60)。

ps:

过程中还找到了 Firefox 的一个扩展 HttpFox,可以用来分析 HTTP 网络活动,下面是一张从访问验证页面到提交信息的记录截图:
http
通过它才发现我原本还遗漏了 eduuser 和 edubas 两个数据项。

另外,点击下面的 “raw”,可以看到还能通过把数据嵌入到 url 地址的方式提交表单:
raw
url 的格式为: http://address/login.do?userName1=xxx&password1=xxx&rand=xxxx&eduuser=xxx&edubas=xxx

pps:

接下来打算把我的树莓派设置成路由器,用这个脚本进行验证(话说买回来没多久就闲置了 -________-”

还有不知要不要写成 Daemon 的形式?或者学一下写个 Systemd 的 Service 来运行?现在暂时用 Openbox 来自动执行。

不过还都是打算,我的拖延症又犯了。。。

参考资料

Arch Linux 下搭建 MonoGame 开发环境

| Comments

其实就是安装几个包而已:

1
$ yaourt -S monodevelop-git monogame monodevelop-monogame-addin-git

第一个是 IDE,Extra 仓库里的更新太慢了,还停留在 4.0.13MonoDevelop 的稳定版现在是 4.2.1

最后那个是 MonoDevelopMonoGame 插件,有了它才能在 MonoDevelop 里创建 MonoGame 的解决方案。话说这个当初把我给搞蒙了,看教程以为直接安装好 MonoGame 就可以了,看了 aur 里的评论才知道这么个东西。觉得维护者应该把它加到可选依赖里。

现在试着创建一个 Solution:

  • 在菜单栏选择 File -> New -> Solution
  • 接着选择 C# -> MonoGame -> MonoGame Linux Application

  • 命名并保存

    (编辑器的配色可以在 Edit -> Preferences -> Text Editor -> Syntax Highlighting 里设置)

  • 在编译运行应用前可以把第 23 行的 graphics.IsFullScreen 设为 false,全屏运行的话会带来一些不便。

  • 按 F5 或 选择菜单 Run -> Start Debugging 启动程序

    不太有趣 _(:3」∠)_

为了体验最新的特性,还可以通过 MonoDevelopVersionControl 插件下载最新的 MonoGame 代码用来创建应用:

详见:Getting and Building the Latest Sources (develop3d)Creating a MonoGame Application (develop3d version)

参考资料

Arch Linux 下自动挂载 MTP

| Comments

买了台 N7 二代,发现不支持 USB 大容量存储模式连接电脑(听说 Android 3 开始就不再支持 USB Mass Storage 协议了,不过 Defy 上刷的 CM 10 倒是可以支持,现在看来应该是第三方添加的功能了),只能以 MTPPTP 的方式连接,导致 Arch 下一直无法自动挂载只能用 Airdroid 传文件,可是用脚本生成的 Wi-Fi 会时不时的断线,传大文件时就杯具了 QAQ

终于让我找到了这个 PKGBUILD 包下载下来后需要先修改一下 PKGBUILD99-android-mtp.rules 这两个文件。

一、

首先是为你使用的设备添加相应的挂载规则。(默认的挂载规则已经加入了对 Nexus 7 一代,Galaxy Nexus 和 HP Touchpad 32GB 的支持,如果你使用的是这些设备可以跳过这一步。)

通过 USB 线把设备连接到电脑,在终端里输入命令:

1
$ udevadm monitor --environment --udev

然后拔出设备,就可以看到类似下面的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing

UDEV  [39055.590819] remove   /devices/pci0000:00/0000:00:12.2/usb1/1-1/1-1:1.0 (usb)
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:12.2/usb1/1-1/1-1:1.0
DEVTYPE=usb_interface
ID_MODEL_FROM_DATABASE=Nexus 4 (debug)
ID_VENDOR_FROM_DATABASE=Google Inc.
INTERFACE=255/255/0
MODALIAS=usb:v18D1p4EE2d0228dc00dsc00dp00icFFiscFFip00in00
PRODUCT=18d1/4ee2/228
SEQNUM=1985
SUBSYSTEM=usb
TYPE=0/0/0
USEC_INITIALIZED=662109

UDEV  [39055.592153] remove   /devices/pci0000:00/0000:00:12.2/usb1/1-1/1-1:1.1 (usb)
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:12.2/usb1/1-1/1-1:1.1
DEVTYPE=usb_interface
ID_MODEL_FROM_DATABASE=Nexus 4 (debug)
ID_VENDOR_FROM_DATABASE=Google Inc.
INTERFACE=255/66/1
MODALIAS=usb:v18D1p4EE2d0228dc00dsc00dp00icFFisc42ip01in01
PRODUCT=18d1/4ee2/228
SEQNUM=1986
SUBSYSTEM=usb
TYPE=0/0/0
USEC_INITIALIZED=48662167

UDEV  [39055.661296] remove   /devices/virtual/bdi/0:41 (bdi)
ACTION=remove
DEVPATH=/devices/virtual/bdi/0:41
SEQNUM=1988
SUBSYSTEM=bdi
USEC_INITIALIZED=660985

UDEV  [39055.667918] remove   /devices/pci0000:00/0000:00:12.2/usb1/1-1 (usb)
ACTION=remove
BUSNUM=001
DEVLINKS=/dev/libmtp-1-1
DEVNAME=/dev/bus/usb/001/016
DEVNUM=016
DEVPATH=/devices/pci0000:00/0000:00:12.2/usb1/1-1
DEVTYPE=usb_device
ID_BUS=usb
ID_FOR_SEAT=usb-pci-0000_00_12_2-usb-0_1
ID_MEDIA_PLAYER=1
ID_MODEL=Nexus_7
ID_MODEL_ENC=Nexus\x207
ID_MODEL_FROM_DATABASE=Nexus 4 (debug)
ID_MODEL_ID=4ee2
ID_MTP_DEVICE=1
ID_PATH=pci-0000:00:12.2-usb-0:1
ID_PATH_TAG=pci-0000_00_12_2-usb-0_1
ID_REVISION=0228
ID_SERIAL=asus_Nexus_7_06e9d04d
ID_SERIAL_SHORT=06e9d04d
ID_USB_INTERFACES=:ffff00:ff4201:
ID_VENDOR=asus
ID_VENDOR_ENC=asus
ID_VENDOR_FROM_DATABASE=Google Inc.
ID_VENDOR_ID=18d1
MAJOR=189
MINOR=15
PRODUCT=18d1/4ee2/228
SEQNUM=1987
SUBSYSTEM=usb
TAGS=:seat:uaccess:
TYPE=0/0/0
USEC_INITIALIZED=48644741

我们需要的是 ID_MODEL_IDID_VENDOR_ID 这两项等于号后面的字符串。

接下来编辑 99-android-mtp.rules 文件,仿照已有的规则和上面得到的信息添加相应的条目,比如我的是 Nexus 7 二代:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Google Nexus 7 (Wi-Fi, 2013 version) 32GB MTP mode
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4ee1", MODE="0666" # MTP media
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4ee2", MODE="0666" # MTP media with USB debug on

###################################
# Android devices, when unplugged #
###################################

# Nexus 7 2013
ENV{ID_MODEL_ID}=="4ee1", ENV{ID_VENDOR_ID}=="18d1", ACTION=="remove", RUN+="/usr/bin/systemctl stop 'android-mtp@$env{ID_MODEL}.service'"
ENV{ID_MODEL_ID}=="4ee2", ENV{ID_VENDOR_ID}=="18d1", ACTION=="remove", RUN+="/usr/bin/systemctl stop 'android-mtp@$env{ID_MODEL}.service'"

####################################
# Android devices, when plugged-in #
####################################

# Nexus 7 2013
ENV{ID_MODEL_ID}=="4ee1", ENV{ID_VENDOR_ID}=="18d1", ACTION=="add", RUN+="/usr/bin/systemctl start 'android-mtp@$env{ID_MODEL}.service'"
ENV{ID_MODEL_ID}=="4ee2", ENV{ID_VENDOR_ID}=="18d1", ACTION=="add", RUN+="/usr/bin/systemctl start 'android-mtp@$env{ID_MODEL}.service'"

这里包含了纯 MTP 模式 和 MTP,USB debug 模式同时打开时的规则,可以在手机切换到对应的模式通过上一步骤得到相关数据。
其中,第一部分里的 ATTR{idVendor} 对应 ID_VENDOR_IDATTR{idProduct} 对应 ID_MODEL_ID

ID_MODEL_ID 除了这两种情况,在 PTP 模式,PTP 和 USB debug 模式,Bootloader 模式 还有 Recovery 模式都会有对应的数值,不过我尝试添加了 PTP 模式的规则后没能成功挂载,只好作罢。

二、

然后需要修改一下 PKGBUILD 中对应 99-android-mtp.rules 文件的 md5 值,无论你有没有进行上一步的修改,这个值都是错误的。

从 AUR 安装所需的依赖 go-mtpfs-gitdaemonize

1
$ yaourt -S go-mtpfs-git daemonize

打包

1
2
$ cd android-automount/
$ makepkg

安装

1
$ sudo pacman -U android-automount-0.1-1-any.pkg.tar.xz

OK,现在连接你的 Android 设备到电脑上,看看 Arch 是不是成功挂载了呢~
如果需要断开连接,只需直接拔掉数据线就好,无需手动 unmount。

参考资料

修正图片文件扩展名的 Python 脚本

| Comments

有时候从往上下再下来的图片会出现扩展名与实际的图片格式不一致的情况,在 Linux 里我用 Eye of GNOME 看图,就会因为这样而无法显示。

最近尝试自学 Python,查了一下相关资料后试着写了一个识别图片格式并更正扩展名的脚本。

1. 识别图片格式

不看扩展名如何知道一个图片文件是什么格式呢? 网上说的比较多的是通过检查文件头标志的方法来判断图片格式,可是自己写的话感觉有点麻烦。

其实已经有现成的轮子可以用了,就是 imghdr 这个模块,可以看一下它的 Docstring 的说明

1
2
3
>>> import imghdr
>>> print imghdr.__doc__
Recognize image file formats based on their first few bytes.

只能识别一些常见的图片格式,对一些特殊的图片文件比如 svg, psd 就无力了,不过已经满足我的需求啦。

用法:

1
imghdr.what(<image_file>)

返回对应格式的扩展名字符串。

2. 更正文件扩展名

需要用到一些文件操作的方法,用到 os 这个模块,记录一下:

  • os.chdir(dirname): 用于改变工作路径到 dirname。
  • os.listdir(dirname): 列出 dirname 下的目录和文件。
  • os.path.isfile(name): 判断 name 是否为文件。
  • os.path.splitext(): 分离文件名与扩展名。
  • os.rename(a, b): 重命名文件。

3. 脚本

correct-image-file-extension.pylink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python

import os, sys, imghdr

#允许指定文件目录,默认则检测当前目录
if len(sys.argv) == 2:
    try:
        os.chdir(sys.argv[1])
    except OSError, e:
        if e.errno == 2:
            print "No such file or directory: '%s'" % sys.argv[1]

for i in os.listdir('.'):
    if os.path.isfile(i):
        name = i
        #有时候会出现 ".png.jpg" 这样格式的扩展名,为了美观,作相应的调整。
        while os.path.splitext(os.path.splitext(name)[0])[1] != '':
            name = os.path.splitext(name)[0]
        fmt = imghdr.what(i)
        xname = os.path.splitext(name)[1][1:].lower()
        if fmt != None and fmt != xname:
            #JPEG 图片的扩展名有时会简写为 jpg,而 imghdr 里则表示为 jpeg,遇到这样的情况直接跳过不再更改。
            if fmt == 'jpeg' and xname == 'jpg':
                continue
            cn = os.path.splitext(name)[0] + '.' + fmt
            os.rename(i, cn)
            print 'Renamed %s to %s' % (i, cn)

参考资料

JS 操作 Iframe 非跨域引用页面的DOM

| Comments

偶然发现了学校校园网登录页上验证码的奇葩特性:

于是写了个小书签,自动填写验证码并登录:

1
javascript:(function(){var a=document.getElementById("main").contentWindow;a.document.getElementById("rand").value=a.document.getElementById("confirmrand").value;a.document.getElementById("login1").submit();})()


登录信息填写的部分是一个由 iframe 标签引用的非跨域页面,可以通过 contentWindow 属性在父页面中对其元素进行操作:

1
var a=doucument.getElementById("iframe 标签的ID").contentWindow;

参考资料

SQL:一次性插入多个元组

| Comments

书本上没有,在 StackOverflow 找到了,做一下笔记

句式一:

1
2
3
4
5
INSERT INTO 'tablename' ('column1', 'column2') VALUES
('data1', 'data2'),
('data3', 'data4'),
('data5', 'data6'),
('data7', 'data8')


句式二:

1
2
3
4
5
INSERT INTO 'tablename'
      SELECT 'data1' AS 'column1', 'data2' AS 'column2'
UNION SELECT 'data3', 'data4'
UNION SELECT 'data5', 'data6'
UNION SELECT 'data7', 'data8'

参考资料

Windows下编译支持多种脚本语言的64位VIM(GVIM)

| Comments

想要玩一下 VimIRC.vim 这款插件,不过这货竟然是 Perl 写的,而我用的 vim 没有加入 Perl 支持,所以想不如试试编译一个支持多种脚本语言的 vim

  • Vim版本:7.3.905
  • 需要支持的语言:Python2/3, Ruby, Perl, Lua, Tcl
  • 编译环境:Cygwin
  • 操作系统:64 位的 Windows8

一.准备工作

1.Cygwin

除了默认的组件外,还需要选择以下的包:autobuild,autoconf,automake,binutils,cmake,gcc-core,gcc,gcc-g77,gcc-g++,gcc4-core,gcc4,gcc4-g++,make,makedepend,pkg-config,gcc-mingw-core,gcc-mingw-g++,gdb,最后,因为现在是编译 64 位版本的 vim 而我又不清楚具体需要哪些包,所以直接搜索 64,选择安装所有的搜索结果 XD

2.各种语言的包

我一律选择了 64 位的版本。Python 分别为 2.7.3 跟 3.3.1。Ruby 本来纠结是用 1.9.3 还是 2.0.0 好,本来电脑里因为配置 Octopress 已经安装了 1.9.3 ,可是 RubyInstaller 似乎只有 2.0 有 64 位的版本,又不知道 Octopress 对 2.0 会不会有兼容性问题。最后选择了下 2.0.0 的 zip 包。Perl 5 的实现选用了 ActivePerl,版本为 5.16.3。Lua 到这里下载 lua-5.2.1_Win64_bin.ziplua-5.2.1_Win64_dllw4_lib.zip 这两个文件,解压到同一目录下。

update (20130501):增加对 Tcl 的支持,版本为 ActiveTcl 8.5.13。原本打算用 8.6 的,无奈编译不成功,原因不明。。。

3.VIM 源代码

在这里 ftp://ftp.vim.org/pub/vim/pc/ 下载,当然是选 7.3啦,找到 vim73_46rt.zip,vim73_46src.zip 这两个包,前一个是运行时文件后一个是源代码,从 7.3 开始语言文件已经打包进了runtime里。然后再到这里 ftp://ftp.vim.org/pub/vim/patches/7.3/ 下载补丁。

不过这些补丁实在太多了(911 个!),下载起来实在有点麻烦。最开始想到用 Firefox的DownThemAll 扩展,不过不知为什么下载下来的文件名开头都多了个 2.3(记不太清,反正就是多了几个数字,不过现在想想其实也没什么影响 ←_←)。然后又想到在资源管理器打开再全选复制,不过速度实在慢的可以。最终 google 后选择了 wget,查了一下命令:

1
$ wget -c -r -nd -np www.xxx.org/pub/path/

-c:断点续传;-r:递归目录;-nd:递归是不创建各层目录直接下载到当前目录下;-np:递归时不搜索上层目录。

把vim73_46src.zip里的src 文件夹(其他的可有可无)解压到一个暂命名为 make 的目录下,把所有的补丁文件放到与 make 目录同级的 patches 文件夹里。打开 Cygwin,c d到 make 目录下(在 Cygwin 里的 /cygdrive 路径下可以找到系统里的各分区),执行以下命令打补丁:

1
$ for file in ../patches/7.3.*; do patch -Np0 -i $file; done

二.开始编译

cd 到 src 文件夹下,执行以下编译命令:

1
$ make -f Make_cyg.mak CC=x86_64-w64-mingw32-gcc RC=x86_64-w64-mingw32-windres ARCH=x86-64 PYTHON=/cygdrive/c/Python27 PYTHON_VER=27 DYNAMIC_PYTHON=yes PYTHON3=/cygdrive/c/Python33 PYTHON3_VER=33 DYNAMIC_PYTHON3=yes RUBY=/cygdrive/c/Ruby200_64 RUBY_VER=200 RUBY_VER_LONG=2.0.0 DYNAMIC_RUBY=yes LUA=/cygdrive/c/Lua52 LUA_VER=52 DYNAMIC_LUA=yes PERL=/cygdrive/c/Perl64 PERL_VER=516 DYNAMIC_PERL=yes TCL=/cygdrive/c/Tcl TCL_VER=85 DYNAMIC_TCL=yes -j5 GUI=yes STATIC_STDCPLUS=yes OLE=yes FEATURES=HUGE USERNAME=wenLiangcan USERDOMAIN=Cygwin

*_VER 跟 *_VER_LONG 的区别是,前者使用不带“.”的版本号,如 200,而后者则需要,如 2.0.0。查看 Make_cyg.ma k文件可以发现,对 ruby 的支持编译需要通过版本号定位到 \$(RUBY)/lib/ruby/\$(RUBY_VER_LONG)/… 这样的路径下,所以对 ruby 使用的这个参数。

不使用 STATIC_STDCPLUS=yes 的话,gvim 运行时会需要 libstdc++-6.dll 跟 libgcc_s_sjlj-1.dll 这两个文件。

GUI=yes 就是编译 gvim,GUI=no 则编译 vim。

多字长跟 cscope 支持都是默认为 yes。

编译完成后,复制 src/ 下的 vim.exe,gvim.exe,install.exe,uninstal.exe,vimrun.exe 跟 GvimExt/ 下的 gvimext.dll(用来添加系统右键菜单的“Edit with Vim”) 还有 xxd/ 下的 xxd.exe到运行时目录下(由 vim73_46rt.zip 解压得到)就可以啦。如果需要安装,就在 cmd 里以管理员权限运行 install.exe。

三.查看成果

运行 vim/gvim ,执行命令:

1
:version

( ̄一 ̄)y

附上下载

四.补充

  • 中文系统下 cygwin 的 shell 也会自动支持中文,如果编译过程出现错误想要复制 google 一下,英文关键词的搜索结果往往会更准确,所以我想把它改为英文提示,可以在 ~/.bashrc 里末尾加上:
1
2
export LANG='en_US'
export LC_ALL='en_US.GBK'
  • 编译后可以执行以下命令清理编译文件:
1
$ make -f Make_cyg.mak clean

五.参考资料