2009年5月26日星期二

FUCK GFW

本文将提供一种一劳永逸的翻墙方式(ssh -D),实施之后,那道墙——对你来说——将从此透明。

本文面向的用户:使用Windows作为操作系统并且使用Firefox作为常用浏览器。

第一步:免费获取拥有SSH权限的帐号和密码。

默认的免费获取方式:将本文转载到你自己的博客上,将转载后的文章网址发送到f.ckgfw#gmail.com

转载方式:拷贝文章代码至博客后台HTML编辑器中,直接发布即可,文章标题自拟,可在前后文插入自己的评论。

经过人工审核,你将收到一封附有五个拥有SSH权限的帐号和密码的电子邮件,你可以将它们赠与你信任的人。

更多获取方式将在今后陆续激活,请关注我们的最新更新:https://friendfeed.com/fuckgfw

第二步:配置MyEnTunnel软件

下载并安装MyEnTunnel,该软件全名为My Encrypted Tunnel。

一键下载:https://dl.getdropbox.com/u/873345/download/myentunnel.exe

myentunnel

按照上图将第一步收到的帐号信息填写到相应的地方后,点击save按钮,再点击hide按钮。

第一次连接过程中会出现一个认证对话框,按照提示确认即可。以后的自动连接中将不再出现此认证对话框。

最后点击hide按钮,使对话框隐藏到系统任务栏中。

提示:

为MyEntunnel创建一个快捷方式,将其复制到系统的【启动】(C:\Documents and Settings\当前用户名(需要修改成你自己的)\「开始」菜单\程序\启动)文件夹中,今后开机便可自动启动软件,并自动连接服务器。

tray

绿色代表连接成功且稳定;黄色代表正在连接或重新连接;红色代表连接失败。

第三步:配置Firefox浏览器

假设你正使用Firefox浏览器阅读本文。

一键安装:http://autoproxy.mozdev.org/latest.xpi

xpi-offical

点击立即安装,安装后,重新启动Firefox。然后你会看到如下对话框,选择gfwlist (P.R.China)后,点击确定。

gfwlist

接着你会看到Firefox主界面右上角出现有一个“福”字图案,点击“福”。

fu

点击“代理服务器——编辑代理服务器”。

edit

随即出现如下画面,你会看到如GAppProxy、Tor和Your Freedom这样一系列代理服务器名称。

before

将GAppProxy一栏的参数修改为如下图所示。

after

修改完毕后,点击确定。至此配置已全部就绪。

获取更多帮助,请关注反馈中心:https://friendfeed.com/fuckgfw-feedback

Bernie:"Eat me!"

第四步:支持fuckGFW

  1. 如果您翻墙成功,请大笑一声并用充满磁性地低音说出:Hello, world!
  2. 如果由于线路原因,始终翻墙不成,不要气馁,给我们发Email,咱们一起解决问题。
  3. 假如哪天突然无法正常连接,请先到反馈中心汇报,我们会及时做出反应。
  4. 目前您有如下几种方式及时获取我们的最新动态:FriendFeed | Twitter | Blog
  5. 保持默契,我们相信您一定可以做到。

版权信息:您可以自由复制、传播、演绎本作品且无需署名、无需注明原始出处。

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();

由番茄操蛋说开去

昨天饭否上发生了一件很有意思的事情,有几位闲到蛋痛的童鞋(包括我在内)立志要把一个从来没听过的词“番茄操蛋”推倒饭否的热门话题里面去,当然这个假设的前提是饭否的热门话题的选择是自动的依靠某种算法和数据,得到的结果(类似于Google那样的关键字排序)。但是我在这里想讨论的不是这个问题,而是另外一个问题:社会化的SNS网络信息传播模型,以及这个网络对“恶意”的虚假信息的免疫能力。
事先声明的是:我只是计算机系的学生,数学或者社会学方面的能力和知识菜的要死,所以说下面的分析和结论都只是我一个人YY的结果,有玉请砸,有槽就吐。

1,数学模型
我们吧问题抽象一下,每个人看作网络中的一个节点,类似于我们网络中的路由器,这个节点通过若干条单向线路与其他节点相连(被他人关注),那么我们得到第一个模型:任何一个节点所发出的信息被其他节点接受并且转发(重复的转发可以无视),这样看来,只要污染源选择的当,那么虚假的消息就会很快传播到整个网络。但是现实不是这么美好的,事实上我们的节点之间的通讯不是完美的,有一定的丢包率(此节点不把收到的消息转发出去,比如他对此消息不感兴趣)和误码率(节点转发的消息经过修改),所以说要想使消息更快更准确的消息,我们不得不有意识的选择多个干扰源,使得这种干扰的效果最大化。在节点的选择上,我们可以参考数据结构中有关图的某些知识,比如关键点和最小生成树。
2,引入社会学和概率论
但是,这个模型还远远不够,显示世界中还存在这对我们这些干扰者不利的因素,比如反论调,网络中的节点是人类,人类的特点就是可以对收到的信息进行思考,并且得到一个结论,由于网络的匿名性和虚拟性,人对消息的有可能采取不信任的态度,并且很有可能得出并传播与我们所希望的结论相违背的结论,对此我们可以采取的做法就是:谎言重复1000次,他就变成真相,并且如果可能的话,有意识的阻断持相反看法者与其他节点的联系。此节点的存在规律应该是服从某种统计学的定律(或者规律)。另外,事实证明单独的人类可能是聪明的,具有是非判别能力的。但是如果一群人类,尤其是相互间的信息交换频繁时,这种判别能力往往会降低,所以我们的信息对大环境污染到一定程度时,有一定概率扑灭或者同化反对者,但是,基于同样的理由,如果反对者在信息传播初期就开始对网络产生影响,那么对信息的传播是很不利的。
3,数据统计
前面我一直是站在信息传播者的角度来考虑问题的,现在我们不妨换个角度,从旁观者的角度研究一下整个消息的传播流程。对于此事件来说,饭否的搜索功能无疑为我们提供了研究的便利条件,我们可以通过程序分析并查找消息的传播源,传播途径和传播速度。最开始我想到的方法就是历遍所有有关番茄操蛋的消息,然后按照时间排序同时进行正则匹配,查找不同节点之间的消息联系(也就是@)从而生成一张树状结构图,但是有同学(@知何处)表示,节点间的信息传播不一定是点对点方式的,而是点对多点,甚至是广播形式,所以直接生成树的话,这颗树一定会有很多独立的小树,而不是我们期望的完整的消息传播树。而我也没想到什么更好的统计方式,所以只能这样放着了。
4,其他
那么研究这东西有什么用呢?我认为对两种人很有用:一种是五毛们,一种是美分们,这两种人时候都在竭尽全力在我们的网络中制造烟雾,引导舆论,如果有一个完备的数学,或者是社会学模型的话,五毛们的工作就会相应的轻松很多,而且不容易犯错误,从而为国家节省了宝贵的电力(大雾)。
另外,根据某个定律:“如果你想到了某些事,那么你一定不是第一个想到这个的人”。我们的生活中是不是已经开始充斥了某些舆论控制或者是虚假信息的存在呢?这个东西想起来还真让人觉得恶心呢。

PS:到目前为止,番茄操蛋还是没有上饭否的热门话题,这对番茄操蛋教的教徒来说,无疑是一个严重的打击。

2009年5月19日星期二

【翻墙】找到个好东西

今天找到个翻墙的好东西:FoxyProxy插件。感觉上要比Buggy的AutoProxy或者wjbutton要好用的多。因为平时我上网都是跑另外一个服务器上的squid缓存的,所以每次使用wjbutton以后都要手动去设置里更改代理设置,还装过一个AutoProxy插件,但是绝对不是很好用,因为好像不能按需选择代理的样子(PS:我怎么看都觉得这个插件是ADBlock改的- -||)。
FoxyProxy可以根据URL匹配选择合适的代理服务器,这一点有点像squid的那一大堆ACL选项,从上到下,第一个匹配的代理会被使用来处理请求的URL,也就是说页面元素也会被处理(比如Blogger里面的一些iframe【举个例子就是本页面上的blogger工具条】)。而这种处理是无缝的,使用的时候体验不出来(但是可能用某无界会慢一些吧XD)。
恩,大概就是这样。

2009年5月12日星期二

【收集】五毛党党章(又名《网络评论员工作指南》)

为了培养网络评论员的工作技能和合作技巧,特编写本工作指南,供全国各省市的网络评论员培训班使用。
  总则:网络舆论战争关系到中华人民共和国国家政权的生死存亡。为了祖国母亲的繁荣富强,为了中华民族的复兴,每一个网络评论员必须时刻准备着用自己的智慧和艰苦劳动保卫共和国的网络防线。
  基本工作方法:     
1、在工作时间内必须每小时至少查看一次工作邮箱,时刻注意领会上级指示的最新精神。
2、网络评论员根据上级指示进行合作,根据工作需要,将由跨地区、跨专业的网络评论员组成工作小组,执行特定的任务。在有必要增加人员时,上级将从其他小组抽调人员加以充实。
3、基本工作方法:日常工作按照网站分小组,每个重要网站的有关论坛由一个小组负责。日常工作是按照总体方针,维护正确的网络舆论导向。遇到突发事件,则按照上级部门的专门工作组的指令行事,暂时停止日常工作,把有关人员资源投入到突发事件的舆论导向工作。
4、网络评论员要善于隐瞒自己的真实身份,必需有多个不同的网名,而且不同的网名要发表不同风格的文章。必要的时候,可以由不同小组成员制造网友辩论的假象,然后由第三方推出强有力的证据,把公众舆论引导到第三方。
5、某些网络谣言出来的时候,必须尽快搜索到谣言的首发地点和首发人,然后勒令网站管理员删除原贴,网络评论员则拷贝内容,以不同的IP地址发表自己就是事发所在地的当地人的申明,然后由版主或以其他网友身份指出:他的IP地址不在事发所在地,该消息纯属谣传。
6、必要时可以制造更加耸人听闻的假新闻,吸引网民视线,然后很快澄清该消息纯属谣言。
7、某些论坛人气不错,网友信用度比较高,这时首先要做的是制造一种混乱,通过似是而非的文章进行干涉,跟贴作非理性的故意曲解、制造误会和争辩,转移网民注意力。
8、海外网站较难控制,当不能主导论坛舆论的时候,可以采用大量短贴、无实质内容贴、非理性贴进行刷屏,令版面充斥无意义的混乱,使读者失去兴趣,这样达到避免反动思想流通传播的目的。
9、不断学习,提高文字水平,学会使用不同的文笔风格写作,善于模仿他人文笔,这是网络评论员的基本功。
10、学会与网友交流的技巧,与网友私下打成一片,获取网友的信任,尤其是那些文章有影响力的网友。如果有可能,争取一些重要论坛的版主位置。
11、培养高超的判断力,能够在诸多贴子中迅速找到真正有影响力的帖子和写手,作为重点工作对象。
12、注意培养政策法规意识,不可误解当前的工作精神。注意吃透上级指示的近期发贴类型实例,融会贯通,举一反三。
13、灵活性与原则性相结合。一定要制造真假难辨的形象,成为一个不容易被鉴别身份的人。不仅要熟悉我们的观点,更要熟悉对方的思路,知己知彼。
14、网络评论员要时刻牢记自己的光荣任务,不被困难和误解阻挡,不在乎表面上的面子,做到任何情况下不会真正被对方激怒,永远保持理性、冷静的心理。
15、网络评论员要立场坚定,头脑清醒,在各种富有迷惑力的思潮面前保持清醒的头脑,珍惜自己的政治前途。
16、网络评论员实行小组监督和纠察监督相结合的原则。其工作成绩由上级有关部门评定。
  以上16条,是网络评论员的工作指南。希望各有关部门认真学习,落实贯彻,在“保先”运动中争做先锋。

另外附送中共中央真理部2008年4月-8月所下达的真理清单:https://docs.google.com/Doc?id=dd4chbjh_18gd2qtmd9

2009年5月10日星期日

免责声明最新版备忘

免责声明:
1. 本人是文盲,以上内容文字均不认识,也看不懂是什么意思(包括但不限于对所以上之内容的识别、阅读、理解、分析、记忆等等)
2、本人过去、现在以及将来都不认识楼主及楼主所述当事人,且自古以来与该相对人无利益关系。
3.本人昨天、今天以及明天都没有或者不准备去主贴所述地点。楼主表述之事与本人无关。本着“看贴(虽然看不懂)回贴,利人利己的中华民族优秀传统美德 ”的原则敲击键盘,从而为“保增长、扩内需、调结构,促民生”作出贡献,进而使社会more and more 水产 ,甚至促进世界much and much大同,做到为往圣继绝学,为万世开太平。
4.本人在此留言(包括但不限于汉字、拼音、拉丁字母 、斯拉夫字母 ,日语假名,阿拉伯字母,单词、句子、图片、影像、录音,以及前述之各种任意组合等等)均为随意敲击键盘所出,用于检验本人电脑键盘录入、屏幕显示的机械、光电性能,并不代表本人局部或全部同意、支持或者反对楼主观点。
5. 本人记忆力差,故设此ID密码为1234,因密码简单,存在被黑客、五毛党、酱油党、民进党、国民党、P民、城管、相对人、李登辉、陈水扁、奥巴马、萨达姆、日本鬼子、高丽棒子、轮子功、黑猩猩以及一小部分不明真相的群众和一小撮怀有不可告人目的的险恶分子等等人和动物破解并用于发帖之可能性,本人盖不知情且不负任何由此引发的任何纠纷。
6. 本人是中国人,但从未参与中华人民共和国或中华民国的任何选举、从未参政议政,无任何政治倾向。所以其言论只是代表个人,如果此贴造成了误会,皆可理解为误会。
7.人生有风险,入世需谨慎。本回贴不暗示、鼓励、支持或映射读者作出生活方式、工作态度、婚姻交友、股票债券买卖、车辆房屋购置、古玩字画收藏、子女入托求学教育的积极或消极判断。未成年人请在监护人陪同下阅读本贴。无完全民事行为能力者,请立即关闭网页,并用20%高锰酸钾+75%乙醇对键盘、硬盘、电压插座、显示器、鼠标、cpu进行灌溉消毒。
8.因删贴不及时所产生的任何法律(包括宪法,加法、减法、乘法、除法、剑法、拳法、脚法、指法、民法,刑法,书法,公检法,基本法,劳动法,婚姻法,输入法,没办法,国际法,今日说法,吸星大法,与台湾关系法及文中涉及或可能涉及以及未涉及之法,各地治安管理条例)纠纷或责任本人概不负责。
9.看帖者请于24小时内自觉、主动、完全忘记。
10.本人持有居住地三级甲等医院心血管科,心胸外科,精神科,神经内科,神经外科,遗传学科所出具有法律效力之健康证明,可证明本人无心脏病史及家族病史,无做噩梦病史及家族病史.同时还持有由本人幼儿院老师,院长,院党委书记,小中大学班主任,校长,校党委书记以及数十亲友共同签字证词,可证明本人素无参与或企图参与躲猫猫,弹脑门及任何可能危害生命健康的不良游戏之意向. 
11.综上所述,恳请各位官员请勿动用武装力量请勿跨村、跨镇、跨市、跨省、跨国、跨洲、跨星球、跨银河系追捕。确因不抓不足以平民愤,或不抓就领不到薪水养家户口的公职人员,建议携带工作证、身份证、结婚证/离婚证、独生子女证、健康证、暂住证、毕业证、边防证、县以上政府机关出具的介绍信温情操作,轻拿轻放。抓捕按照以下排序之倒序:楼主、原作者以及网络管理员以及网络运行商、电信运营商、电力供应商、电脑生产销售商。
12.本贴著作权归本人所有,受著作权法保护,未经许可,不得用于商业目的。转载请联系版主。此声明最终解释权归本人所有。

2009年5月7日星期四

Ubuntu 9.04 ext4文件系统下启用quota的方法

904开始支持ext4了,但是我装好以后发现quota找不到我的挂载点了,而我确定所有的设置没问题。于是开始怀疑是ext4的问题,Google一下发现很久以前这个bug就出了,但是到现在还没fix,找到一个邮件说要给quota打个patch,因为旧版的quota支持的是ext4dev,也就是开发板的ext4.
那么在官方没有升级之前,我就先山寨一个用咯:
要打的补丁是

Index: quota-tools/mntopt.h
===================================================================
--- quota-tools.orig/mntopt.h 2007-08-22 04:26:55.000000000 -0700
+++ quota-tools/mntopt.h 2008-10-29 16:37:57.000000000 -0700
@@ -6,7 +6,8 @@
/* filesystem type */
#define MNTTYPE_EXT2 "ext2" /* 2nd Extended file system */
#define MNTTYPE_EXT3 "ext3" /* ext2 + journaling */
-#define MNTTYPE_EXT4 "ext4dev" /* ext4 filesystem */
+#define MNTTYPE_EXT4 "ext4" /* ext4 filesystem */
+#define MNTTYPE_EXT4DEV "ext4dev"/* ext4dev filesystem */
#define MNTTYPE_MINIX "minix" /* MINIX file system */
#define MNTTYPE_UFS "ufs" /* UNIX file system */
#define MNTTYPE_UDF "udf" /* OSTA UDF file system */
Index: quota-tools/quotacheck.c
===================================================================
--- quota-tools.orig/quotacheck.c 2008-10-29 16:38:50.000000000 -0700
+++ quota-tools/quotacheck.c 2008-10-29 16:39:09.000000000 -0700
@@ -1064,6 +1064,7 @@ static void check_all(void)
!hasmntopt(mnt, MNTOPT_GRPJQUOTA) && !warned &&
(!strcmp(mnt->mnt_type, MNTTYPE_EXT3) ||
!strcmp(mnt->mnt_type, MNTTYPE_EXT4) ||
+ !strcmp(mnt->mnt_type, MNTTYPE_EXT4DEV) ||
!strcmp(mnt->mnt_type, MNTTYPE_REISER))) {
struct utsname stats;

Index: quota-tools/quotasys.c
===================================================================
--- quota-tools.orig/quotasys.c 2008-10-29 16:39:45.000000000 -0700
+++ quota-tools/quotasys.c 2008-10-29 16:40:01.000000000 -0700
@@ -63,6 +63,7 @@ static int correct_fstype(char *type)
if (!strcmp(type, MNTTYPE_EXT2) ||
!strcmp(type, MNTTYPE_EXT3) ||
!strcmp(type, MNTTYPE_EXT4) ||
+ !strcmp(type, MNTTYPE_EXT4DEV) ||
!strcmp(type, MNTTYPE_JFS) ||
!strcmp(type, MNTTYPE_MINIX) ||
!strcmp(type, MNTTYPE_UFS) ||


--

他给的patch的目录可能和你的不一样,每行前面有-的是要删除的,有+的是要添加的。然后去吧源代码拉下来先。

apt-get source quota

获取源代码以后按照上面给出的patch改源代码,其实就3处改动,手工改也可以。configure了以后不用make,直接用dpkg-buildpackage吧他打成deb包,这时可能提示缺少若干-dev包,装上就行了,之后用dpkg -i装上去就可以了,装之前推荐用apt-get remove quota吧原来的卸载掉。

继续折腾:通过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