會(huì)開(kāi)車了,也慢慢不規(guī)矩起來(lái)了,于是乎,違章信息也慢慢多起來(lái)了,但是無(wú)法第一時(shí)間通知到自己。
雖說(shuō),有個(gè)網(wǎng)站:
http://www.hzti.com/service/qry/violation_veh.aspx?pgid=&type=1&node=249
可以查詢非現(xiàn)場(chǎng)違章情況,
不過(guò):
1.我是懶人,主動(dòng)去查詢的時(shí)候不太樂(lè)意做
2.車輛識(shí)別碼,永遠(yuǎn)記不住
3.每次輸驗(yàn)證碼,挺麻煩的
所以,我希望,只要有違規(guī)信息,能主動(dòng)通知到我。
程序員嘛,自己動(dòng)手,豐衣足食。 :)
實(shí)現(xiàn)思路:
1.爬取
http://www.hzti.com/service/qry/violation_veh.aspx?pgid=&type=1&node=249的信息
2.使用飛信命令發(fā)送短信消息(http://www.it-adv.net/)--免費(fèi)的哦。
實(shí)現(xiàn)難點(diǎn):
1.驗(yàn)證碼破擊
我對(duì)圖片毫無(wú)研究,幸好它的驗(yàn)證碼太簡(jiǎn)單了,只有#000000(黑色)才是驗(yàn)證碼信息,其他的都是無(wú)用信息。并且元素的位置都是固定的。
2.網(wǎng)站用.net實(shí)現(xiàn),在post信息的時(shí)候,有一陀無(wú)用的數(shù)據(jù)信息。所幸,post數(shù)據(jù)的時(shí)候,并沒(méi)有對(duì)數(shù)據(jù)做加密,不然都挺難判斷具體參數(shù)的意義
驗(yàn)證碼破解思路:
1.自我學(xué)習(xí):通過(guò)程序下載多張驗(yàn)證碼圖片,去噪點(diǎn),切割圖片元素,找出最清楚的0-9這10個(gè)元素圖片。作為樣本;
2.破解:下載需要破解的驗(yàn)證碼圖片,去噪點(diǎn),切割圖片元素,和樣本像素比較,相似度在95%以上的視為一致。
得到圖片信息并且去噪點(diǎn)的方法:
1 // 得到圖片信息,去除噪點(diǎn)的圖片
2 BufferedImage getBufferedImage(InputStream in) throws Exception {
3 BufferedImage img = ImageIO.read(in);
4 for (int i = 0; i < img.getWidth(); i++) {
5 for (int j = 0; j < img.getHeight(); j++) {
6 // 黑色的才是驗(yàn)證碼
7 if (!CODE_RGB.equals(Integer.toHexString(img.getRGB(i, j)))) {
8 img.setRGB(i, j, -1);
9 }
10 }
11 }
12 in.close();
13 return img;
14 }
得到圖片像素信息(RGB值)
1 int[][] getImageFeature(BufferedImage img) throws Exception {
2 int[][] feature = new int[WIDTH][HEIGHT];
3 for (int i = 0; i < WIDTH; i++) {
4 for (int j = 0; j < HEIGHT; j++) {
5 if (img.getWidth() < i || img.getHeight() < j) {
6 continue;
7 }
8 feature[i][j] = img.getRGB(i, j);
9 }
10 }
11 return feature;
12 }
下載多張圖片,切割,保存,人工尋找最清晰的0-9這10個(gè)元素圖片
public void learn(int count) throws Exception {
for (int c = 0; c < count; c++) {
BufferedImage img = getBufferedImage(new URL(IMG_URL).openStream());
// 保存樣本
for (int i = 1; i < 5; i++) {
BufferedImage bi = img.getSubimage(i * X_BASE, Y_BASE, WIDTH, HEIGHT);
String name = String.valueOf(c) + "_" + String.valueOf(i);
ImageIO.write(bi, IMAGE_FORMAT, new File(IMG_REF + "learn/" + name + ".bmp"));
}
}
}
備注:
GifImageWriter有bug。對(duì)于BufferedImage的subBufferedImage輸出一直有問(wèn)題。Sun很早就有人提交了bug,貌似還沒(méi)有修復(fù)。
破解代碼(說(shuō)破解,有點(diǎn)汗。。。其實(shí)壓根兒和破解沒(méi)關(guān)系)
1 public String crack(InputStream in) throws Exception {
2 BufferedImage img = getBufferedImage(in);
3 StringBuilder ret = new StringBuilder();
4
5 for (int c = 1; c < 5; c++) {
6 BufferedImage bi = img.getSubimage(c * X_BASE, Y_BASE, WIDTH, HEIGHT);
7 int[][] feature = getImageFeature(bi);
8
9 for (ImageCode ic : imageCodes) {
10 int success = 0;
11 for (int i = 0; i < WIDTH; i++) {
12 for (int j = 0; j < HEIGHT; j++) {
13 if (feature[i][j] == ic.getFeature()[i][j]) {
14 success++;
15 }
16 }
17 }
18 // 判斷是否匹配上
19 if (success * 100.0 / (WIDTH * HEIGHT) > 95) {
20 ret.append(ic.getCode());
21
22 continue;
23 }
24 }
25
26 }
27
28 return ret.toString();
29 }
其中,imageCodes是初始化準(zhǔn)備的0-9這10個(gè)元素圖片的特征信息和對(duì)應(yīng)的值
之后的事情就簡(jiǎn)單了。通過(guò)LiveHttpHeader找到Post提交的數(shù)據(jù),通過(guò)shell腳本,模擬。搞定。
主要用到curl命令
1 curl -b "ASP.NET_SessionId=$SESSION_ID" "$IMAGE_CODE_URL" -o $TMP_CODE_BASE
2 code=`java -cp lib/traffic.jar TrafficImgCracker $TMP_CODE_BASE`
3
6 curl -A "$AGENT" -e $TRAFFIC_URL -b "ASP.NET_SessionId=$SESSION_ID;isLoginedWeb=T;ImageV=$code" -d "$QUERY_PARAM" "$TRAFFIC_URL" -o $TMP_CONTENT_BASE
7
8 now=`cat $TMP_CONTENT_BASE | grep -r '<td class="xxcxsspoptds">' | sed 's/<[^>]*>//g' | sed 's/\s\+/ /g'`
9 his=`cat $HIS_CONTENT_BASE | grep -r '<td class="xxcxsspoptds">' | sed 's/<[^>]*>//g' | sed 's/\s\+/ /g'`
10
11 if [ "$now" != "$his" ];then
12 msg=`cat $TMP_CONTENT_BASE | grep -r '<td class="xxcxsspoptds">' | sed 's/<[^>]*>//g' | sed 's/\s\+/ /g' | head -n 6`
13 echo $msg
14 #send
15 fi
shell一直不熟,代碼估計(jì)挺猥瑣的。。。
最后通過(guò)飛信命令發(fā)送消息:
1 $FETION_HOME/fetion --mobile=12345678901 --pwd=bugaosuni --to=12345678901 --msg-utf8="$msg"
廣告下:
飛信命令行,挺好的東東,像系統(tǒng)報(bào)警等,都能考慮用它來(lái)完成。
最后,附上圖片破解的代碼:
1 import java.awt.image.BufferedImage;
2 import java.io.File;
3 import java.io.FileInputStream;
4 import java.io.InputStream;
5 import java.net.URL;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import javax.imageio.ImageIO;
10
11 /**
12 * 交通網(wǎng)圖片分析工具
13 *
14 * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2010-10-10
15 */
16 public class TrafficImgCracker {
17
18 private static final String IMG_URL = "http://www.hzti.com/government/CreateCheckCode.aspx";
19 private static final String IMG_REF = "/home/stone/tmp/imgref/";
20
21 private static final int X_BASE = 9;
22 private static final int Y_BASE = 0;
23 private static final int WIDTH = 7;
24 private static final int HEIGHT = 22;
25 private static final String IMAGE_FORMAT = "bmp";
26 private static final String CODE_RGB = "ff000000";
27
28 private List<ImageCode> imageCodes = new ArrayList<ImageCode>(10);
29
30 public static void main(String[] args) throws Exception {
31 if (args.length != 1) {
32 System.out.println("java TrafficImgCracker $file_path");
33 System.exit(1);
34 }
35
36 String file = args[0];
37
38 TrafficImgCracker cracker = new TrafficImgCracker();
39 cracker.init();
40 System.out.print(cracker.crack(file));
41 }
42
43 /**
44 * init
45 *
46 * @throws Exception
47 */
48 public void init() throws Exception {
49 File ref = new File(IMG_REF);
50 File[] files = ref.listFiles();
51 for (File f : files) {
52 if (!f.isFile()) {
53 continue;
54 }
55 String code = f.getName().substring(0, f.getName().indexOf("."));
56 BufferedImage img = getBufferedImage(new FileInputStream(f));
57 int[][] feature = getImageFeature(img);
58 // add to image codes
59 imageCodes.add(new ImageCode(code, feature));
60 }
61 }
62
63 /**
64 * crack
65 *
66 * @param file
67 * @return
68 * @throws Exception
69 */
70 public String crack(String file) throws Exception {
71 return crack(new FileInputStream(file));
72 }
73
74 /**
75 * crack image code
76 *
77 * @param in image input stream
78 * @return code
79 */
80 public String crack(InputStream in) throws Exception {
81 BufferedImage img = getBufferedImage(in);
82 StringBuilder ret = new StringBuilder();
83
84 for (int c = 1; c < 5; c++) {
85 BufferedImage bi = img.getSubimage(c * X_BASE, Y_BASE, WIDTH, HEIGHT);
86 int[][] feature = getImageFeature(bi);
87
88 for (ImageCode ic : imageCodes) {
89 int success = 0;
90 for (int i = 0; i < WIDTH; i++) {
91 for (int j = 0; j < HEIGHT; j++) {
92 if (feature[i][j] == ic.getFeature()[i][j]) {
93 success++;
94 }
95 }
96 }
97 // 判斷是否匹配上
98 if (success * 100.0 / (WIDTH * HEIGHT) > 95) {
99 ret.append(ic.getCode());
100
101 continue;
102 }
103 }
104
105 }
106
107 return ret.toString();
108 }
109
110 /**
111 * learn
112 *
113 * @param count learning count
114 * @throws Exception
115 */
116 public void learn(int count) throws Exception {
117 for (int c = 0; c < count; c++) {
118 BufferedImage img = getBufferedImage(new URL(IMG_URL).openStream());
119 // 保存樣本
120 for (int i = 1; i < 5; i++) {
121 BufferedImage bi = img.getSubimage(i * X_BASE, Y_BASE, WIDTH, HEIGHT);
122 String name = String.valueOf(c) + "_" + String.valueOf(i);
123 ImageIO.write(bi, IMAGE_FORMAT, new File(IMG_REF + "learn/" + name + ".bmp"));
124 }
125 }
126 }
127
128 // 得到圖片信息,去除噪點(diǎn)的圖片
129 private BufferedImage getBufferedImage(InputStream in) throws Exception {
130 BufferedImage img = ImageIO.read(in);
131 for (int i = 0; i < img.getWidth(); i++) {
132 for (int j = 0; j < img.getHeight(); j++) {
133 // 黑色的才是驗(yàn)證碼
134 if (!CODE_RGB.equals(Integer.toHexString(img.getRGB(i, j)))) {
135 img.setRGB(i, j, -1);
136 }
137 }
138 }
139 in.close();
140 return img;
141 }
142
143 // 得到圖片特征
144 private int[][] getImageFeature(BufferedImage img) throws Exception {
145 int[][] feature = new int[WIDTH][HEIGHT];
146 for (int i = 0; i < WIDTH; i++) {
147 for (int j = 0; j < HEIGHT; j++) {
148 if (img.getWidth() < i || img.getHeight() < j) {
149 continue;
150 }
151 feature[i][j] = img.getRGB(i, j);
152 }
153 }
154 return feature;
155 }
156
157 /**
158 * image code bean
159 *
160 * @author <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2010-10-10
161 */
162 public static class ImageCode {
163
164 private String code;
165 private int[][] feature;
166
167 public ImageCode(String code, int[][] feature){
168 this.code = code;
169 this.feature = feature;
170 }
171
172 public String getCode() {
173 return code;
174 }
175
176 public void setCode(String code) {
177 this.code = code;
178 }
179
180 public int[][] getFeature() {
181 return feature;
182 }
183
184 public void setFeature(int[][] feature) {
185 this.feature = feature;
186 }
187 }
188
189 }
====================================================================
10月29日編輯的內(nèi)容:
最近在學(xué)python,用python寫了個(gè)版本,發(fā)覺(jué)確實(shí)簡(jiǎn)潔很多:
1 import os, sys, Image
2
3 class TrafficImgCracker(object):
4 """
5 交通信息網(wǎng)站圖片破解
6 """
7
8 def __init__(self, base):
9 """
10 base:參考圖片基本路徑
11 codes:圖片code和feature的元組集合
12 """
13 self.base = base
14 self.codes = []
15
16 for i in [images for images in os.listdir(base) if images.endswith('bmp')]:
17 self.codes.append((i[0], list(Image.open(base + i).getdata())))
18
19
20 def crack(self, imgfile):
21 img = Image.open(imgfile)
22 ret = []
23 for i in range(1, 5):
24 i = img.crop((i * 9, 0, i * 9 + 7, 22))
25 feature = [f if(f == 40) else 15 for f in i.getdata()]
26 ret.append(self._getcode(feature))
27 return "".join(ret)
28
29
30 def _getcode(self, feature):
31 for code in self.codes:
32 ref = zip(code[1], feature)
33 if(len([x for x in ref if x[0] == x[1]]) * 100.0 / len(ref) > 95):
34 return code[0]
35 return None