显示标签为“蛋痛”的博文。显示所有博文
显示标签为“蛋痛”的博文。显示所有博文

2009年5月25日星期一

【撞墙】那啥,反正已经蛋痛过一次了……再来画图吧

下午很蛋痛的用正则表达分析网页去了,然后刚才发现原来饭否提供的API里面有直接搜索的API,OTL……
蛋痛并继续蛋痛吧,吧频率图的程序补完- -,依赖pygooglechart @ pygooglechart.slowchop.com

然后不负责任的贴代码走人,output_fqcd是前一个程序的输出- - Python不支持unicode的模块名么?


# -*- coding: UTF-8 -*-

from output_fqcd import fanfou_data

mapdata = map(lambda data: data['time'][2:7] ,fanfou_data)

def reduce_func(d,r):
if r in d:
d[r] = d[r]+1
else:
d[r] = 1
return d

data = reduce(reduce_func,mapdata,{})

new_data = []
for x,y in data.iteritems():
new_data.append((x,y))

new_data.sort(lambda a,b: cmp(a[0],b[0]))

print(new_data)

x_data = []
y_data = []

for item in new_data:
x_data.append(item[0])
y_data.append(item[1])


from pygooglechart import Chart
from pygooglechart import StackedVerticalBarChart
from pygooglechart import Axis

min_y = 0 #min(data)
max_y = max(y_data)

chart = StackedVerticalBarChart(600, 375, y_range=[min_y, max_y])

chart.set_title('Search Count of "番茄操蛋"')
chart.add_data(y_data)

chart.set_bar_width(30)

left_axis = range(min_y, max_y + 1, max_y/10)
chart.set_axis_labels(Axis.LEFT, left_axis)

# X axis labels
chart.set_axis_labels(Axis.BOTTOM, x_data)

chart.download('fanfou.png')

【蛋痛】吧饭否的老底翻出来

蛋痛的饭否搜索抓取,开始的时候被饭否耍了一下,url里面有个p的参数是控制最大搜索数量的。
抓取结果保存为python模块(Python3.0)


#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import urllib.request
import re

key_word = '番茄操蛋'
key_enc = urllib.parse.quote(key_word)

#不用代理的无视
proxy_support = urllib.request.ProxyHandler({"http" : "http://192.168.60.250:8080"})
opener = urllib.request.build_opener(proxy_support)

urllib.request.install_opener(opener)

def load(url):
f = urllib.request.urlopen('http://fanfou.com' + url)
resp = f.read().decode("utf-8")
f.close()
return resp

user_re = re.compile(r'<a href="/(?P<uid>[^"]+?)" title="(?P<nick>[^"]+?)" class="avatar"><img src="(?P<avatar>[^"]+?)".+?<span class="content">(?P<content>.+?)</span>.+?<a href="/statuses/(?P<mid>[^"]+?)" class="time" title="(?P<time>[^"]+?)">')
next_re = re.compile(r'<a href="(?P<url>[^"]+?)">下一页</a>')

collected_data = [];
import codecs
f = codecs.open('output_'+key_word+'.py','w','utf-8')
f.write('''
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
[
''')
def parse_page(text):
for m in user_re.finditer(text):
data = m.groupdict()
f.write(str(data))
f.write(',\n')
collected_data.append(data)
print(m.group('mid'),'@',m.group('time'))
mid = m.group('mid')
print('---')

m = next_re.search(text)
if m:
return '/search?q='+key_enc+'&noframe=yes&m='+mid
else:
return None

url = '/search?q='+key_enc+'&noframe=yes'
while url:
text = load(url)
url = parse_page(text)
print(url)

f.write(']\n')
f.close();

2009年5月7日星期四

继续折腾:通过Web方式修改Linux用户密码的PHP脚本

搞了一晚上,现在有点头晕,可能写的不是很清楚,有些遗漏,有问题请留言,谢谢。

昨天协会到了一台服务器,在我的强烈要求下跑了Linux,然后有人提出来可不可以在上面跑个Apache+PHP给小朋友们做主页玩,我觉得这个想法不错于是开搞。最开始是计划用proftpd进行文件上传的,但是配置了半天,发现使用mysql后端的proftpd性能不是一般的差- -||,进行个ls都要等5秒以上,Google无果,以前也没有接触过这玩意儿,所以决定换回相对熟悉的vsftpd,外加apache的userdir模块,然后使用系统帐号进行控制(我知道vsftpd也能用mysql做后端,但是一样没用过)。
但是只用系统帐号就存在一个密码更改的问题,总不能人家每次想改个密码都要告诉我手工改吧(不想给他们开ssh权限)。于是,下面这个玩意儿出现了。
首先是代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh_cn" lang="zh_cn">
<head>
<title>修改登录密码</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<?php
$u = $_POST['u'];
$o = $_POST['o'];
$p = $_POST['p'];
$r = $_POST['r'];
$error = 'no error';
if ($u){
echo '<pre>';
if($p!=$r){
echo 'ERROR: Password not match, please check your input.';
//}else if($u!=$_SERVER['PHP_AUTH_USER']){
// echo 'ERROR: Unable to change other\'s password.';
}else if(!$p){
echo 'ERROR: New Password is empty!';
}else if(strpbrk($p,"\r\n")||strpbrk($u,"\r\n")){
echo 'ERROR: Account name and password should not contain \r or \n!';
}else{
echo "Tying to change password for {$u}...\n";
if(!pam_auth($u,$o,$error)){
echo "ERROR: $error\n";
}else{
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a file to write to
);

$cwd = NULL;
$env = NULL;

$process = proc_open('sudo /usr/sbin/chpasswd', $descriptorspec, $pipes, $cwd, $env);
if (is_resource($process)) {
fprintf($pipes[0],"%s:%s\n",$u,$p);
fclose($pipes[0]);

echo stream_get_contents($pipes[1]),"\n";
fclose($pipes[1]);

echo stream_get_contents($pipes[2]),"\n";
fclose($pipes[2]);

$return_value = proc_close($process);

echo "Command returned $return_value";
}
}
}
echo '</pre>';
}else{
$u=$_SERVER['PHP_AUTH_USER'];
}
?>
<form method=POST><table>
<tr><td colspan=2 align='center'>修改用户口令</td></tr>
<tr><td>用户名: </td><td><input type=text name='u' value='<? echo $u; ?>'/></td></tr>
<tr><td>原密码: </td><td><input type=password name='o'/></td></tr>
<tr><td>新密码: </td><td><input type=password name='p'/></td></tr>
<tr><td>重复密码:</td><td><input type=password name='r'/></td></tr>
<tr><td colspan=2 align='center'><input type=submit value='确认'>  <input type=reset value='取消'></td></tr>
</table></form>
</body>
</html>

然后讲一下如何配置:
首先,这里用到了一个php扩展:pam,我的服务器是ubuntu的,装这个东西时也废了不少功夫,首先apt-get install php-pear 然后还要装几个dev库(php5-dev libpam-dev)然后就可以用pear pam下载编译这个叫pam的扩展了。
装好这个还要设置:新建/etc/pam.d/php,内容从login文件里面复制就可以了,或者你可以试试他说明文档中提到的那个:

# /etc/pam.d/php
#
# note: both an auth and account entry are required

auth sufficient /lib/security/pam_pwdb.so shadow nodelay
account sufficient /lib/security/pam_pwdb.so

这个我试过,好像不能用,不知道是不是版本的问题。
然后麻烦的事情来了,要设置其他用户的密码,必然需要root权限,假如我们给予了apache的用户某些特权,必然会对整个系统的安全造成影响,尤其是我现在还允许用户上传自己的PHP,而PHP的disable_functions只能全局设置,所以我觉得从根本上吧用户和特权基本分类:使用多个Apache实例,每个实例使用不同的php配置。
设置参考这个帖子:http://blog.datajelly.com/company/blog/46-multiple-apache-instances-in-ubuntu.html
我是这样设置的:新的apache只监听SSL的443端口,使用web_admin这个低权账户登录系统(而用户使用的旧apache使用www-data),最小化配置(关闭所有无用的modules),关闭php的安全设置。在独立Php配置时使用了PhpIniDir这个httpd.conf的参数,用过win32下的apache+php的应该熟悉。

使用visudo修改/etc/sudoer这个文件,在文件的最后添加:
web_admin ALL=NOPASSWD: /usr/sbin/chpasswd

注意这里一定要用visudo这个东西修改,不要直接打开!
这样就赋予了web_admin这个用户使用root权限运行/usr/sbin/chpasswd的权限了,比直接设置sid之类的方法要安全一些

这样一来,整个系统就大概可以投入使用了。

2009年5月2日星期六

蛋痛产物:用Python读erlang字节码

连着断网两天啊,在宿舍闲着(NND管机房的老师都出去旅游了,结果看着机房里的老鼠在那里咬光纤就是进不去……生气ing)
闲着蛋痛(Workspace整个都在服务器上,光纤一断什么都干不了)与是吧很久以前的工作干完了,于是有了下面这个玩意:
有点类似javap的感觉的一个玩意儿,就是纯粹的读取beam文件然后打印结果,主要是为了熟悉beam字节码的结构,说起来这玩意可真要命,几乎找不到任何文档说明,只能去看源代码,所以放寒假的时候曾经研究过一段时间,但是最后放弃了OTL。这几天是实在蛋痛才拿出来鼓捣。而且到最后关于字节码的行为和定义还是没弄懂(erts在加载字节码时还要变形……而且要命的是这玩意儿是基于寄存器的而不是基于堆栈的,弄起来不是特别顺手- -)
下面是主要代码,python3.0通过:

<[[CDATA--
from chunk import Chunk
from struct import unpack_from
from zlib import decompress
from beam.beam_ops import *
from io import BytesIO

input = open('a.beam','rb')
beam = Chunk(input)
print("Name=%s Size=%x"%(beam.getname(),beam.getsize()))
beam_head = beam.read(4)
print("BeamHeader=%s"%beam_head)
chunks = {}
while True:
try:
ch = Chunk(beam,align=False)
except EOFError:
break
print("\tName=%s Size=%x"%(ch.getname(),ch.getsize()))
skip = input.tell()+4*int((ch.getsize()+3)/4);#for 4 bytes align
chunks[ch.getname()]=ch.read()
input.seek(skip)

input.close()

atoms = [b'a']
if chunks[b"Atom"]:
print("Atom Table:")
data = chunks[b"Atom"]
count = unpack_from('>L', data[0:4])[0]
ptr = 4
print("\tCount=%d"%count)
for i in range(count):
name_len = data[ptr]
ptr = ptr+1
name = data[ptr:ptr+name_len]
ptr = ptr+name_len
atoms.append(name)
print("\tAtom[%d]=%s"%(i+1,name))

imports=[]
if chunks[b"ImpT"]:
print("Import Table:")
data = chunks[b"ImpT"]
count = unpack_from('>L', data[0:4])[0]
print("\tCount=%d"%count)
for i in range(count):
x = unpack_from('>LLL', data[i*12+4:i*12+16])
module_idx = x[0]
name_idx = x[1]
arity = x[2]
imports.append(x)
print("\tImport[%d]=%s:%s/%d"%(i,atoms[module_idx],atoms[name_idx],arity))

exports=[]
if chunks[b"ExpT"]:
print("Export Table:")
data = chunks[b"ExpT"]
count = unpack_from('>L', data[0:4])[0]
print("\tCount=%d"%count)
for i in range(count):
x = unpack_from('>LLL', data[i*12+4:i*12+16])
name_idx = x[0]
arity = x[1]
lable_idx = x[2]
exports.append(x)
print("\tExport[%d]=%s/%d@%d"%(i,atoms[name_idx],arity,lable_idx))

literals=[]
if chunks[b"LitT"]:
zdata = chunks[b"LitT"]
zlen = len(zdata)
size = unpack_from('>L', zdata[0:4])[0]
data = decompress(zdata[4:])
ulen = len(data)
print("Literal Table: {Compressed=%d Uncompressed=%d Decleared=%d}"%(zlen,ulen,size))
count = unpack_from('>L', data[0:4])[0]
ptr = 4
print("\tCount=%d"%count)
for i in range(count):
lit_len = unpack_from('>L', data[ptr:ptr+4])[0]
ptr = ptr+4
x = unpack_from('>Bc', data[ptr:ptr+2])
VERSION_MAGIC = x[0]
EXT_TAG = x[1]
if EXT_TAG==b'k':
value = data[ptr+4:ptr+lit_len]
else:
value = data[ptr+2:ptr+lit_len]
ptr = ptr+lit_len
literals.append(value)
print("\tLiteral[%d]={v:%d,t:%s} %s"%(i,VERSION_MAGIC,op_external_tags[EXT_TAG],value))

lambdas=[]
if chunks[b"FunT"]:
print("Lambda Table:")
data = chunks[b"FunT"]
count = unpack_from('>L', data[0:4])[0]
print("\tCount=%d"%count)
for i in range(count):
off = i*24+4
x = unpack_from('>LLLLLL', data[off:off+24])
function_index = x[0]
arity = x[1]
lable_index = x[2]
index = x[3]
num_free = x[4]
old_uniq = x[5]
exports.append(x)
print("\tLambda[%d]=%s/%d@%d %d %d %d"%(i,atoms[function_index],arity,lable_idx,index,num_free,old_uniq))

codes=[]
if chunks[b"Code"]:
print("Code Section:")
data = chunks[b"Code"]
x = unpack_from('>LLLLL', data[0:20])
code_len = x[0]
code_ver = x[1]
code_max = x[2]
num_lables = x[3]
num_functions = x[4]
print("\tCodeLen=%d"%code_len)
print("\tCodeVer=%d"%code_ver)
print("\tCodeMax=%x"%code_max)
print("\tLables=%d"%num_lables)
print("\tFunctions=%d"%num_functions)
data=data[20:]
dlen = len(data)
input = BytesIO(data)
while input.tell() off = input.tell()
op = input.read(1)[0];
arity = op_arities[op];
args = []
for j in range(arity):
args.append(read_arg(input,atoms=atoms,literals=literals))
codes.append((op,args))
print("\tCodeOffset[%04X]=%s %s"%(off,op_names[op],args))
--]]>

然后顺便还从erts里面提取了一些字节码定义数据,又臭又长,不贴了,放地址:
py.rar

2009年4月26日星期日

火狐,重启以及其他

今天用FF搜索“羊叫兽”(我知道我蛋痛- -)的时候,很不幸的,火狐崩了。然后在我准备重新打开火狐的时候,系统来了这么一句提示:
您的火狐浏览器尚未关闭,请关闭所有窗口或者重启以后再试
看上去挺普通的啊,但是亮点在最后的重启上。
明白人都知道,其实只要用任务管理器吧firefox这个进程kill掉(或者你要是蛋痛使用taskkill甚至xkill之类的东西我也不管)就完事了,但是干嘛开发人员要让人家重启呢?我想大概原因不外乎这样一条准则:
不是所有用户都是专家
的确,对于一个连任务管理器都不知道是什么的用户来说“请结束所有剩余的firefox.exe进程”这几话无外乎是天书,而重启这种万金油试的解决办法的确是一个好选择。
说到重启这个玩意,想起一个笑话,很久以前在网吧,机器出点什么问题基本上只能叫“网管”,然后“网管”来了以后解决办法基本上是“重启”于是乎有了下面的对话:
  • “网管,我上不了QQ了!”
  • “重启。”
  • “网管,我机器没反应了!!”
  • “重启。”
  • “网管,我的机器怎么老是自动重启啊?!”
  • “重启。”
罗嗦完重启以后在唠叨两句,以前我用Emeditor时也出现过这个现象,也就是前一个进程假死了,结果后面的进程就都启动不了。但是emeditor可没有得到我天朝网吧管理员的真传,新进程连个P都没放就自动退出了。结果还得我折腾半天(可惜我就是没重启),最后连重量级的Ollydbg都请出来了,才发现原来是原来的emeditor死掉了,一直挂在那里,新进程就一直向老进程发消息,然后自己就退出。这样说起来,FF应该比Emeditor先进,至少它会回PING新进程,告诉他:这事我已经处理了,你就放心的去吧- -||

恩要吐槽的基本上就是这些,最近真的很蛋痛(凌晨5点还在上网,而且还会去Google杨叔的人不是蛋痛是啥)