分类目录归档:未分类

wireshark学习笔记(九)——HTTPS协议

HTTPS的工作流程

使用HTTPS协议工作时,服务端和客户端的信息传输都会进行加密,所以传输的数据都是加密后的数据。HTTPS的工作流程如下:

1、客户端使用HTTPS的URL访问Web服务器,要求与Web服务器建立SSL连接。

2、Web服务器收到客户端的请求后,会将网站包含有公钥的证书信息传送一份给客户端。

3、客户端浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。

4、客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

5、Web服务器利用自己的私钥解密出会话密钥。

6、Web服务器利用会话密钥加密与客户端之间的通信。

HTTPS数据包的捕获

由于HTTPS是建立在TCP协议之上的,所以我们在捕获之前,可以设置筛选器为TCP。开始捕获后,可以通过登录QQ邮箱的方式,捕获到含有HTTPS协议的数据包。如下图所示(Lab15-1.pcapng):

在这个界面的Info列中,可以看到这些数据包都是使用HTTPS协议传输的。而在Protocol列中,可以看到SSL、TCP以及TLSv1.1等协议。其中的TLS(Transport Layer Security)其实是SSL的升级版,是为网络通信提供安全及数据完整性的一种安全协议。二者都是在传输层对网络连接进行加密的。

为了便于进一步的分析,这里我们依旧可以采用前几次课中说过的,通过着色后提取数据包的方式,将我们需要的数据包提取出来,这里不再赘述。

HTTPS数据包的分析

这里我们打开Lab15-1-https.pcapng这个实验文件,也就是经过提取后的文件进行分析:

首先可以发现1到3号数据包是TCP连接建立时的三次握手的过程。接下来从第4个数据包开始,其实就是SSL握手过程中所产生的数据包,也就是使用HTTPS协议加密的信息。

对于第4个数据包,包含有客户端向服务器发送的Client Hello信息,以便开始一个新的加密会话连接。展开SSL区段,可以看到客户端支持的TLS的版本(Version);生成的随机数(Random,以便用于生成对话密钥);Cipher Suites的值是15,说明支持15种加密算法;支持的压缩方式(Compression)以及一些扩展(Extension)等:

当服务器收到客户端的请求后,就会向客户端发出响应,也就是Server Hello。对于我们这个捕获文件而言,第6个数据包就是服务器发送给客户端的确认信息,告诉客户端请求已经收到:

展开SSL数据段,可以看到服务器支持的TLS版本为1.1。也可以看到服务器生成的随机数,用于生成对话密钥。下面还有加密套件信息,这里的加密套件表示使用RSA公钥算法来验证证书以及交换密钥,用AES加密算法对数据进行加密,使用SHA算法来校验消息内容。

客户端收到服务器的响应后,首先验证服务器的证书。如果证书不是由可信任的机构颁发的,或者证书中的域名与实际域名不一致,或者证书已过期,就会向访问者显示一个警告,由访问者选择是否还要继续通信。如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送一些信息,包括用服务器公钥加密的随机数、编码改变通知和客户端握手结束通知等。服务器发送给客户端的数据包,如下图所示:

06

在Certificate区段中,保存有证书的详细信息。可以看到请求的网站、组织名、地区名、省名以及国家等的信息。紧接下来的signedCertificate是签名证书信息。“签名”的作用就在于,客户端(浏览器)在确定是否要信任一个网站时,就是通过证书判断的。所以每个证书上必须要有一份“签名”。最后一行Length的值为0,表示告诉客户端“Hello”过程已经完成,也就意味着服务器将不验证客户端的证书。

在客户端收到并验证服务器响应的证书后,将会把生成的密钥传给服务器,如下图所示:

在Client Key Exchange中,我们可以看到TLS的类型、版本、长度、握手协议、握手类型、长度、密钥交换算法客户端参数、公钥长度以及加密公钥等信息。而在其它的两个记录中,也有着类似的结构。

经过以上几个阶段,整个握手过程也就结束了。接下来客户端与服务器进入加密通信的阶段,就完全是使用普通的HTTP协议,但是会使用会话密钥来加密内容。用户可以发送通过TLS层使用RC4加密的HTTP的消息,也可以解密服务端发送来的由RC4加密的消息。此外还会通过哈希算法来验证消息是否被篡改。

在应用层,客户端向服务器发送的信息为:

可见,发送的数据都经过了加密处理。服务器响应客户端的信息,如下图所示:

可以看到,这也是使用了SSL进行加密。

HTTPS数据包的解密

HTTPS的数据包并不是绝对安全的,在一定的条件下也可以实现解密的操作,这里我们打开Lab15-2.cap实验文件:

可以看到,这里的数据包使用了SSLv3进行加密。为了实现解密,我们需要Lab15-2.key文件。为了便于寻找,这里我将这个文件放在了我的电脑中的C盘根目录下。之后在Wireshark中选择菜单栏的“Edit”->”Preferences”->”Protocols”->”SSL”->”RSA keys list”->”Edit”->”New”,之后进行如下填写:

之后点击OK,就可以实现解码了:

现在已经可以看到GET方法等信息。尽管我们对捕获文件进行了解码操作,但是并不意味着HTTPS不安全。因为解码所需要使用的密钥只能在服务器导出,不同的服务器的情况并不一样,因此想要破解HTTPS的数据包,是需要满足很多的条件的。

作者:kevinhuangk
链接:https://www.jianshu.com/p/f8e0b859749a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

如何利用Wireshark解密SSL和TLS流量

英语版本:CTX116557 /   创建日期:2008年3月14日   /   更新日期:2012年11月29日

概要

本文介绍在Wireshark网络协议分析仪中如果解密SSL和TLS流量

要求

  • 以下基本知识:

• 网络追踪

• 网络,TCP/IP和SSL/TLS协议

• 证书和公私钥的使用

• Wireshark网络协议分析仪

  • Wireshark 软件支持SSL解密
  • 服务器或设备的私钥,私钥格式为PKCS#8 PEM

背景

在Wireshark中,SSL解析器功能完整,且支持高级特性,如提供加密私钥时的SSL解密。这对于使用SSL或TLS加密的思杰产品的排错有很大的帮助。

步骤

Wireshark 设置

  • 在Wireshark中,SSL解析器功能完整,且支持高级特性,如提供加密私钥时的SSL解密。这对于使用SSL或TLS加密的思杰产品的排错有很大的帮助。

  • 从菜单中选择Edit > Preferences.

  • 打开Preferences 窗口,展开Protocols.

  • 下拉选择 SSL.

  • 在RSA keys list后的空白处,提供以下信息<ip>,<port>,<protocol>,<key_file_name> (如上图所示)
    其中: 
    <ip>
    是具有私钥的服务器或设备的IP地址
    <port>
     是SSL/TLS端口号,通常是443
    <protocol>
     通常是HTTP
    <key_file_name>
     是私钥的名称和路径is the location and file name of the private key
    Note:
     逗号间没有符号。而且,使用分号间隔用于不同条目。
    “<ip>,<port>,<protocol>,<key_file_name>;<ip>,<port>,<protocol>,<key_file_name>;<ip>,<port>,<protocol>,<key_file_name>”.
  • SSL debug file后的空白处填入排错文件的路径和文件名
  • 点击 OK.
  • SSL则被解密(解密的SSL如下图所示)

私钥格式

Wireshark只要有私钥就可以解密SSL流量。私钥需要时decrypted PKCS#8 PEM format (RSA)格式。你可以打开私钥看它的内容。如果是二进制,则是DER格式,不能用于Wireshark解密。

你可以使用OpenSSL转换密钥格式。例如,可以将PKCS#8 DER格式的密钥转化成decrypted PKCS#8 PEM format (RSA)格式。在$提示符后输入如下指令:

openssl pkcs8 -nocrypt -in der.key -informat DER -out pem.key -outformat PEM

其中:

der.key 是DER密钥文件的文件名和路径

pem.key是pem文件的文件名和路径

解密后的decrypted PKCS#8 PEM format (RSA)格式如下:

注意密钥开头为:

—–BEGIN RSA PRIVATE KEY—–

如果开头为:

—–BEGIN ENCRYPTED PRIVATE KEY—–

则这个密钥需要用适当的方法解密。OpenSSL可以实现。

  • At the $ prompt, enter the command: 在$提示符,输入命令:
    openssl rsa

    If you enter this command without arguments, you are prompted as follows: 如果输入指令不带参数,则出现以下字样:
    read RSA key
  • 输入解密的私钥文件名

你可以在openssl rsa后加上参数,假如你知道私钥和解密的PEM的文件名。例如,私钥的文件名是myprivkey.pvk和解密的文件名问keyout.pem,命令如下:
openssl rsa –in myprivkeypvk -out keyout.pem

附加信息

http://www.wireshark.org/

http://wiki.wireshark.org/SSL

http://www.wireshark.org/docs/dfref/s/ssl.html

http://www.openssl.org/docs/apps/rsa.html#EXAMPLES

http://sourceforge.net/project/showfiles.php?group_id=23617&release_id=4880

手动获取我们所感兴趣网站X.509证书的一般方法

怎样手动获取我们所感兴趣网站的公钥证书?

一、通过抓包从网络协议数据流中获取

因为不管怎么说,在SSL协议的握手阶段,服务器必然会向客户端/浏览器发送自己的证书,所以我要详细说的这第一种获取证书的方法就是通过抓包从网络协议数据流中获取,这是一种通用性非常好的方法。以利用Wireshark 1.10(当然你若是有其他习惯的抓包工具也可以,只要支持SSL协议解析即可)获取account.test01.com的服务器证书例,方法如下:

(1)使用Wireshark 1.10把登录account.test01.com过程的HTTPS会话数据抓取下来(抓包的过程就不多说了,不会问度娘),为了后面定位数据包方便,抓包要尽量干净、完整;

(2)为了防止你还是不小心抓住了其他HTTPS会话的数据,你可以在Windows的命令提示符下用nslookup命令查询account.test01.com的IP地址,假如是192.168.1.57;

(3)使用 Ctrl+F 组合键打开查找对话框,By(查找类型)选择String,Search In(查找区域)选择Packet list,过滤条件写 Certificate ,设定完毕点 Find。如下图:

查找对话框

图1-1  查找对话框设置

(4)查看所查找到的数据包的源IP地址,是否为第(2)步中所获取的192.168.1.57,若是,则成功定位所需数据包;否则使用 Ctrl+N 组合键查看下一个符合查找条件的数据包,直到找到所需数据包为止;

证书所在数据包1

(a)证书和Sever Hello消息分开发

证书所在数据包2

(b)证书和Sever Hello消息在一个包里

图1-2 定位证书所在数据包的两种情况

(5)在上步定位好的数据包中找到证书链所在,如下图靛青色框所标注,

证书和证书链

图1-3 证书和证书链在数据包中所处的位置

        如上图,很多时候服务器发送的不止是自己的应用证书,而是会带上完整的证书链,其中第一个是本应用(即account.test01.com)的证书,图中黑色框标注。【应用证书的持有者,很多时候会使用通配符,例如本例中,account.test01.com所发送证书的持有者是*.test01.com,如果有类似于prox.test01.com,photo.test01.com之类的域名,他们也可以使用这个证书】

另外,图中灰色框标注了给该应用签名的子CA【即DigiCert High Assurance CA-3】的公钥证书,浅灰色框标注了上级CA【即DigiCert High Assurance EV Root CA】的公钥证书。

(6)Wireshark 提供了比较方便地方法从数据包中拷贝出证书,在上图中标注的证书所在位置右键,依次选择”copy”–>”Bytes”–>”Hex Stream”,获取16进制流形式的证书文件。如下图:

证书拷贝

图1-4  证书信息的拷贝

(7)找一款支持16进制流拷贝的工具,比如010Editor,新建一个文件,把上一步得到的十六进制流复制到本文件(一定注意不能使用简单的文本复制,必须基于16进制流格式粘贴,010Editor中使用 Ctrl+Shift+V 组合键即可达到此效果),文件另存为account.test01.com.der。这就是我们要的证书,可以直接双击打开。

 

二、通过浏览器自带的证书复制/导出功能

我要介绍的第二种方法,有一定局限性,但是操作更为简单。我们平时用浏览器访问HTTPS网站时,如果你有细心观察的话,应该能看到浏览器地址栏会有个小锁(不同浏览器显示的位置不太一致),对于搜狗浏览器,如图2-1。

浏览器地址栏

图2-1  搜狗浏览器访问https站点时的地址栏

       点击这个小锁,弹出如下提示:

提示信息

图2-2  “查看证书信息”

       单击“查看证书信息”弹出证书,在“详细信息”选项卡下有个“复制到文件”按钮,如图2-3,点击后按照提示即可成功获取证书文件。

证书复制

图2-3  证书复制

       这个方法的局限性在于只适用于可用浏览器访问的网站,对于那些只用客户端(或者手机应用)访问的网站就无能为力,因为人家的客户端或应用往往都不提供证书复制或导出功能;即使是可用浏览器访问的网站,也只适用于直接使用https协议的网站(比如Gmail),不适用于临时从http跳转到https又跳回http的情况(比如12306)。

使用下面的方法可以从wireshark抓的SSL报文里面得到DER格式的服务器证书。

http://www.wireshark.org/lists/wireshark-users/201003/msg00080.html

Is it possible to extract the x509 ssl certificate from a pcap file? I’m trying to compare a ssl certificate that I have with the one captured in a traffic capture. Searching the archives (and google) have only provided discussions on decrypting ssl traffic which is more than I need.

 

Yes, that’s possible. open the tracefile

1) make sure the setting “Allow subdissector to reassemble TCP streams” is on in the TCP protocol preferences
2) Then go to the packet which contains the SSL handshake message “Certificate”
3) In the packet detail pane, expand the SSL protocol
4) Expand the “Certificate” TLS record
5) Expand the “certificate” handshake protocol
6) Expand the list of certificates. There is now a list of certificate length and certificates (the list could be only 1 certificate). The first certificate is the server certificate, the second it’s signing CA, the third the CA that signed the CA, etc.
7) Now rightclick on the certificate that you want to export
8) Choose “Export selected packet bytes…”
9) Choose a filename and click on save

You now have a file containing the certificate in DER format. You can use openssl to process the certificate as needed.

在Wifi网络中嗅探明文密码(HTTP POST请求、POP等)

全世界,现在大约50%的网站没有使用SSL加密,天朝尤其多。

我们都知道通过HTTP发送的数据都是明文,没有使用任何加密,即使是在数据敏感的登录页面。

本文的目的是:如果你在不熟悉的网络环境中,要注意提高警惕。

没有人希望自己的密码暴露给他人,so,不要嗅探(狗)他人的密码。

想象有一个攻击者坐在某个咖啡馆里,桌子上放着他的笔记本电脑并连着咖啡馆的免费wifi。这个家伙就可以非常容易的获得网络里通信信息。如果密码是明文,连破解都省了。

系统要求:

  • Wireshark
  • ARP欺骗攻击(ARP spoof attack)
  • 无线网卡

无线网卡监听模式和混杂模式有什么不同:

  • 监听模式允许网卡不用连接wifi就可以抓取特性频道的数据,就是在空气中抓取某个波段的数据。可以用在破解wifi密码
  • 混杂模式(连接wifi)就是接收所有经过网卡的数据包,包括不是发给本机的包,即不验证MAC地址
  • 普通模式下网卡只接收发给本机的包

现在的无线路由器都是和主机直接通信,如果你直接使用嗅探工具,只会得到广播信息和自己的连接信息。为了得到网络中所有设备发送的的数据,攻击者必须想办法充当网关的角色,ARP欺骗攻击就干了这样一件事。如果有路由器控制权的话就省事了。

集线器可以直接嗅探,因为所有数据的发送形式是广播。看看这:集线器、交换机和路由器有什么不同

ARP欺骗攻击(ARP spoof attack):


下载安装Wireshark

它也支持Windows、Mac OSX;下载地址:https://www.wireshark.org/download.html

Kali Linux自带了这个工具。

Wireshark

ARP欺骗攻击

我选择使用ettercap,如果你不知道怎么使用,看,Kali Linux ettercap的使用

攻击同一wifi网络中的其他主机:

ettercap

使用Wireshark抓包

现在wifi网络中所有计算机发送的数据包都经过攻击者计算机,攻击者需要抓取这些数据包。

Wireshark

等待其他人使用明文密码。

例如,要找HTTP POST请求,过滤:

http.request.method == "POST"

Wireshark

查看明文密码:

Wireshark

ettercap也可以抓包,它直接提取了明文密码:

ettercap

POP抓取的是我163的邮箱账户。我手机安装了邮件客户端,如果连接网络它会定时收取邮件,在认证的过程中就把密码暴露了。

常用的其他未加密服务:FTP、Telnet等


总结:

  • 不要使用公共网络访问HTTP、POP等不安全服务,尽量使用HTTPS类型的网站。
  • 使用VPN加密连接;顺带还能翻墙。实际上也不是100%可靠,SSH服务器和目标之间还可以做手脚。
  • 密码要勤换,千万不要万年不变。
  • 感觉网络无安全,既要防黑、还要防朝廷。
Written on April 18, 2016

标准ES-POS命令打印,固定IP或蓝牙打印,支持黑白图片打印

Icon

ESC-POS命令打印辅助库

源码地址:ProjectX

介绍

标准ESC-POS命令打印,固定IP或蓝牙打印,支持黑白图片打印。 其中PrintCommands类列出了基本所有的ESC-POS打印命令。 打印指令参考自Commande ESCPOS.pdf文档。

截图

Screenshotsxample

先决条件

  • minSdkVersion 5
  • <uses-permission android:name="android.permission.INTERNET" />
  • <uses-permission android:name="android.permission.BLUETOOTH" />

入门

引用:

dependencies {
    ...
    implementation 'am.util:printer:2.1.0'
    ...
}

添加权限:

添加蓝牙权限<uses-permission android:name="android.permission.BLUETOOTH" />或者网络请求权限<uses-permission android:name="android.permission.INTERNET" />

实现接口:

实现PrintDataMaker接口,完成具体打印任务:

public class TestPrintDataMaker implements PrintDataMaker {

    private Context context;
    private String qr;
    private int width;
    private int height;

    public TestPrintDataMaker(Context context, String qr, int width, int height) {
        this.context = context;
        this.qr = qr;
        this.width = width;
        this.height = height;
    }

    @Override
    public List<byte[]> getPrintData(int type) {
        ArrayList<byte[]> data = new ArrayList<>();
        try {
            PrinterWriter printer;
            printer = type == PrinterWriter58mm.TYPE_58 ? new PrinterWriter58mm(height, width) : new PrinterWriter80mm(height, width);
            printer.setAlignCenter();
            data.add(printer.getDataAndReset());

            ArrayList<byte[]> image1 = printer.getImageByte(context.getResources(), R.drawable.ic_printer_logo);
            data.addAll(image1);

            printer.setAlignLeft();
            printer.printLine();
            printer.printLineFeed();

            printer.printLineFeed();
            printer.setAlignCenter();
            printer.setEmphasizedOn();
            printer.setFontSize(1);
            printer.print("我的餐厅");
            printer.printLineFeed();
            printer.setFontSize(0);
            printer.setEmphasizedOff();
            printer.printLineFeed();

            printer.print("最时尚的明星餐厅");
            printer.printLineFeed();
            printer.print("客服电话:400-8008800");
            printer.printLineFeed();

            printer.setAlignLeft();
            printer.printLineFeed();

            printer.print("订单号:88888888888888888");
            printer.printLineFeed();

            printer.print("预计送达:" +
                    new SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.getDefault())
                            .format(new Date(System.currentTimeMillis())));
            printer.printLineFeed();

            printer.setEmphasizedOn();
            printer.print("#8(已付款)");
            printer.printLineFeed();
            printer.print("××区××路×××大厦××楼×××室");
            printer.printLineFeed();
            printer.setEmphasizedOff();
            printer.print("13843211234");
            printer.print("(张某某)");
            printer.printLineFeed();
            printer.print("备注:多加点辣椒,多加点香菜,多加点酸萝卜,多送点一次性手套");
            printer.printLineFeed();

            printer.printLine();
            printer.printLineFeed();

            printer.printInOneLine("星级美食(豪华套餐)×1", "¥88.88", 0);
            printer.printLineFeed();
            printer.printInOneLine("星级美食(限量套餐)×1", "¥888.88", 0);
            printer.printLineFeed();
            printer.printInOneLine("餐具×1", "¥0.00", 0);
            printer.printLineFeed();
            printer.printInOneLine("配送费", "免费", 0);
            printer.printLineFeed();

            printer.printLine();
            printer.printLineFeed();

            printer.setAlignRight();
            printer.print("合计:977.76");
            printer.printLineFeed();
            printer.printLineFeed();

            printer.setAlignCenter();

            data.add(printer.getDataAndReset());

            String bitmapPath = FileUtils.getExternalFilesDir(context, "Temp") + "tmp_qr.jpg";
            if (QRCodeUtil.createQRImage(qr, 380, 380, null, bitmapPath)) {
                ArrayList<byte[]> image2 = printer.getImageByte(bitmapPath);
                data.addAll(image2);
            } else {
                ArrayList<byte[]> image2 = printer
                        .getImageByte(context.getResources(), R.drawable.ic_printer_qr);
                data.addAll(image2);
            }

            printer.printLineFeed();
            printer.print("扫一扫,查看详情");
            printer.printLineFeed();
            printer.printLineFeed();
            printer.printLineFeed();
            printer.printLineFeed();
            printer.printLineFeed();

            printer.feedPaperCutPartial();

            data.add(printer.getDataAndClose());
            return data;
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }
}

准备打印:

创建打印执行者:

PrintExecutor executor = new PrintExecutor(String ip, int port, int type);
PrintExecutor executor = new PrintExecutor(BluetoothDevice device, int type);

设置执行者状态监听(非必须):

executor.setOnStateChangedListener(new PrintSocketHolder.OnStateChangedListener() {
    @Override
    public void onStateChanged(int state) {
        switch (state) {
            case PrintSocketHolder.STATE_0:
                //生成打印页面数据...
                break;
            case PrintSocketHolder.STATE_1:
                //生成数据成功,开始创建Socket连接...
                break;
            case PrintSocketHolder.STATE_2:
                //创建Socket成功,开始获取输出流...
                break;
            case PrintSocketHolder.STATE_3:
                //获取输出流成功,开始写入打印页面数据...
                break;
            case PrintSocketHolder.STATE_4:
                //写入打印页面数据成功,正在完成打印...
                break;
        }
    }
});

设置执行者回调监听(非必须):

executor.setOnPrintResultListener(new PrintExecutor.OnPrintResultListener() {
    @Override
    public void onResult(int errorCode) {
        switch (errorCode) {
            case PrintSocketHolder.ERROR_0:
                //打印成功完成!
                break;
            case PrintSocketHolder.ERROR_1:
                //生成打印页面数据失败!
                break;
            case PrintSocketHolder.ERROR_2:
                //创建Socket失败!
                break;
            case PrintSocketHolder.ERROR_3:
                //获取输出流失败!
                break;
            case PrintSocketHolder.ERROR_4:
                //写入打印页面数据失败!
                break;
            case PrintSocketHolder.ERROR_5:
                //必要的参数不能为空!
                break;
            case PrintSocketHolder.ERROR_6:
                //关闭Socket出错
                break;
            case PrintSocketHolder.ERROR_100:
                //打印失败
                break;
        }
    }
});

执行打印:

int result = executor.doPrinterRequest(PrintDataMaker maker);//同步
executor.doPrinterRequestAsync(PrintDataMaker maker);//异步

自定义

如果你要实现自己的打印机PrinterWriter,那么你需要继承PrinterWriter,并实现其 必须的方法。示例:

public class PrinterWriter80mm extends PrinterWriter {

    public static final int TYPE_80 = 80;// 纸宽80mm
    public int width = 500;

    public PrinterWriter80mm() throws IOException {
    }

    public PrinterWriter80mm(int parting) throws IOException {
        super(parting);
    }

    public PrinterWriter80mm(int parting, int width) throws IOException {
        super(parting);
        this.width = width;
    }

    @Override
    protected int getLineWidth() {
        //一行能够放下多少个“-”
        return 24;
    }

    @Override
    protected int getLineStringWidth(int textSize) {
        //根据字体的大小,一行可以放下多少个英文字符
        switch (textSize) {
            default:
            case 0:
                return 47;
            case 1:
                return 23;
        }
    }

    @Override
    protected int getDrawableMaxWidth() {
        //图片能够全部打印在纸上的最大宽度
        return width;
    }
}

注意

  • 打印图片出现乱码或者打印不出,大部分原因是打印机缓存较小导致,可调小PrinterWriter80mm的parting参数,设置其图片高度分割值(0~255),调小了还不行的话,缩小图片尺寸或者调整输出流的写入方式。
  • 仅提供建立蓝牙连接打印,不包括蓝牙搜索及配对功能
  • 不包含二维码生成功能
  • PrintCommands包含了大量打印指令,但是并非全部经过测试,而且并非所有打印机都支持。

支持

如果发现错误,请在此处提出: https://github.com/AlexMofer/ProjectX/issues

许可

Copyright (C) 2015 AlexMofer

Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

国外免费BT离线下载服务收集

#收集控#国外免费BT离线下载服务收集

名称 价格 介绍
OffCloud 免费 每个月只能下载三次,做测试用
Streamza 免费 单文件 1G 限制,队列 1,无法下载回本地,可在线
BytesLoader 免费 单文件无限制,队列 2
FURK 免费 需要邀请码,单文件 2G 限制,队列 2,可在线
DirectTorrents 免费 总文件 10G 限制 | 队列 3
Zbigz 免费 单文件 1G 限制,队列 2,限速 150K,7 天保存
FileStream 免费 单文件 1G 限制,队列 2,3 天保存
FilesLoop 免费 单文件 1G 限制,队列 1
BitPort 免费 单文件 1G 限制,队列 1
Seedr 免费 单文件 2G 限制,队列 1
ByteBX 免费 单文件 100M 限制,队列不限,总空间 2.5G
TorrentSafe 免费 单文件无限制,月队列 3,总空间 10G
2Giga 免费 单文件 2G 限制,队列 1
SonicSeedbox 免费 单文件无限制,天队列 2,总空间 4G
HiperDown 免费 单文件无限制,队列 2,总空间 5G
HiperDown 免费 单文件无限制,队列 2,总空间 5G

linux crontab 实现每秒执行

linux crontab 命令,最小的执行时间是一分钟。如需要在小于一分钟内重复执行,可以有两个方法实现。

1.使用延时来实现每N秒执行

创建一个php做执行动作,非常简单,就是把当前时间写入log。

  1. <?php
  2. file_put_contents(‘/home/fdipzone/php/crontab/run.log’date(‘Y-m-d H:i:s’).“\r\n”, FILE_APPEND);
  3. ?>

crontab -e 输入以下语句,然后 :wq 保存退出。

  1. * * * * * php /home/fdipzone/php/crontab/tolog.php
  2. * * * * * sleep 10; php /home/fdipzone/php/crontab/tolog.php
  3. * * * * * sleep 20; php /home/fdipzone/php/crontab/tolog.php
  4. * * * * * sleep 30; php /home/fdipzone/php/crontab/tolog.php
  5. * * * * * sleep 40; php /home/fdipzone/php/crontab/tolog.php
  6. * * * * * sleep 50; php /home/fdipzone/php/crontab/tolog.php

使用 tail -f 查看执行情况,可以见到log每10秒被写入一条记录。

  1. fdipzone@ubuntu:~$ tail -f /home/fdipzone/php/crontab/run.log
  2. 2014-03-31 21:47:01
  3. 2014-03-31 21:47:11
  4. 2014-03-31 21:47:21
  5. 2014-03-31 21:47:31
  6. 2014-03-31 21:47:41
  7. 2014-03-31 21:47:51
  8. 2014-03-31 21:48:01

原理:通过延时方法 sleep N  来实现每N秒执行。

注意:

60必须能整除间隔的秒数(没有余数),例如间隔的秒数是2,4,6,10,12等。

如果间隔的秒数太少,例如2秒执行一次,这样就需要在crontab 加入60/2=30条语句。不建议使用此方法,可以使用下面介绍的第二种方法。

2.编写shell脚本实现

crontab.sh

  1. #!/bin/bash
  2. step=2 #间隔的秒数,不能大于60
  3. for (( i = 0; i < 60; i=(i+step) )); do
  4.     $(php ‘/home/fdipzone/php/crontab/tolog.php’)
  5.     sleep $step
  6. done
  7. exit 0

crontab -e 输入以下语句,然后:wq 保存退出。

  1. # m h  dom mon dow   command
  2. * * * * * /home/fdipzone/php/crontab/crontab.sh

使用 tail -f 查看执行情况,可以见到log每2秒被写入一条记录。

  1. fdipzone@ubuntu:~/php/crontab$ tail -f run.log
  2. 2014-03-31 22:23:01
  3. 2014-03-31 22:23:03
  4. 2014-03-31 22:23:06
  5. 2014-03-31 22:23:08
  6. 2014-03-31 22:23:10
  7. 2014-03-31 22:23:12
  8. 2014-03-31 22:23:14
  9. 2014-03-31 22:23:16
  10. 2014-03-31 22:23:18
  11. 2014-03-31 22:23:20
  12. 2014-03-31 22:23:22
  13. 2014-03-31 22:23:25
  14. 2014-03-31 22:23:27
  15. 2014-03-31 22:23:29
  16. 2014-03-31 22:23:31
  17. 2014-03-31 22:23:33
  18. 2014-03-31 22:23:35
  19. 2014-03-31 22:23:37
  20. 2014-03-31 22:23:39
  21. 2014-03-31 22:23:41
  22. 2014-03-31 22:23:44
  23. 2014-03-31 22:23:46
  24. 2014-03-31 22:23:48
  25. 2014-03-31 22:23:50
  26. 2014-03-31 22:23:52
  27. 2014-03-31 22:23:54
  28. 2014-03-31 22:23:56
  29. 2014-03-31 22:23:58
  30. 2014-03-31 22:24:00

原理:在sh使用for语句实现循环指定秒数执行。

注意:如果60不能整除间隔的秒数,则需要调整执行的时间。例如需要每7秒执行一次,就需要找到7与60的最小公倍数,7与60的最小公倍数是420(即7分钟)。

则 crontab.sh step的值为7,循环结束条件i<420, crontab -e可以输入以下语句来实现

  1. # m h  dom mon dow   command
  2. */7 * * * * /home/fdipzone/php/crontab/crontab.sh

免费.blog域名 已成功撸一年免费.blog域名

Exabytes是来自马来西亚的域名注册商,老牌商家,成立于2001年。前期的活动有新注册.com域名3.99美元/首年优惠活动,兑换后人民币大概是在25元左右,比较划算。今天发布了一个免费域名活动,提供免费一年的.blog域名,适合做博客使用,每个新老账号都可以参与,但只能注册一个免费域名,站长已撸成功了,详细申请免费教程如下。

一、Exabytes官网撸.blog链接:立即前往 ,在首页输入要注册的域名,搜索一下;

二、进入付款页面后,输入优惠码“FREEBLOG-ADD18”价格就变成0了,然后选择PayPal付款即可。一年的免费.blog域名就撸到手了。每个账号优惠码只可以使用一次。

CPUTool – 限制和控制Linux中任何进程的CPU利用率

Linux性能监控的关键领域之一是CPU使用率和系统负载。有几种Linux性能监视工具可以监视系统上的事情。

许多这些工具只需输出系统状态/统计信息,而另外几个工具则为您提供管理系统性能的方法。 一个这样的工具叫做CPUTool 。

CPUTool是一个简单而强大的命令行工具,用于将任何进程的CPU利用率限制和控制到给定的限制,并允许在系统负载超过定义的阈值时中断进程执行。

CPUTool如何工作?

为了限制CPU使用率,cputool将SIGSTOPSIGCONT信号发送到进程,这由系统负载决定。 它依赖于/ proc伪文件系统来读取PID及其CPU使用度量。

如果系统负载超过阈值,则可将其用于将由单个进程或一组进程影响的CPU使用率或系统负载限制为给定的限制和/或暂停进程。

建议阅读: 了解Linux负载平均和监视Linux的性能

安装CPUTool以限制CPU使用和负载平均

CPUTool只能使用软件包管理工具从默认系统存储库安装到Debian / Ubuntu及其衍生产品。

$ sudo apt install cputool

使用CUPTool限制CPU使用

现在看看cputool如何真正的工作。 为了演示这一切,我们将运行一个dd命令 ,这将在后台产生高CPU百分比,并显示其PID。

# dd if=/dev/zero of=/dev/null &

为了监控CPU使用率,我们可以使用顶部或扫视工具,使我们能够查看正在运行的Linux系统进程的实时定期更新状态:

# top
监视dd命令CPU使用情况

监视dd命令CPU使用情况

从上面的输出可以看出, dd命令的CPU时间99.7%)最高为99.7%)现在我们可以使用cputool来限制它,如下所示。

--cpu-limit-c标志用于设置进程或进程组的使用百分比, -p指定PID。 以下命令会将dd命令(PID 8275)限制为使用一个CPU内核的50% :

# cputool --cpu-limit 50 -p 8275 

运行cputool后,我们可以再次检查进程的新CPU使用情况(PID 8275)。 现在, dd进程的CPU使用率应该在( 49.0%-52.0% )之间。

# top
限制处理CPU到50%的使用

限制处理CPU到50%的使用

为了进一步限制dd的CPU使用率达到20% ,我们可以再次运行cputool:

# cputool --cpu-limit 20 -p 8275 

然后立即检查使用顶部的工具或这样的扫视 ( dd的CPU使用率现在应在19.0%-22.0%之间或稍微超过此值):

# top
将CPU使用限制为20%

将CPU使用限制为20%

请注意,在cputool运行时,shell不会指望任何用户输入; 因此没有反应。 要杀死它(这将终止CPU使用限制操作),请按Ctrl + C

重要的是,要指定一个进程组(一个具有多个运行实例的程序,每个具有不同的PID),例如HTTP Web服务器:

# pidof apache2
9592 3643 3642 3641 3640 3638 3637 1780

使用-P标志,如下所示:

# cputool --cpu-limit 20 -P 1780

使用CUPTool限制系统负载

-l选项用于指定系统可能进入的最大负载,以便进程或进程组继续运行。 我们可以使用分数值(例如2.5 )。

下面的示例意味着仅当系统负载不超过3.5时为本地备份运行rsync :

# cputool --load-limit 3.5 --rsync -av /home/howtoing /backup/`date +%Y-%m-%d`/

有关更多信息和用法,请查看CPUTool手册页:

# man cputool

请查看以下有用的指导,以查找CPU信息和CPU性能监视:

  1. 9在Linux上获取CPU信息的有用命令
  2. Cpustat – 通过在Linux中运行进程监视CPU利用率
  3. CoreFreq – 用于Linux系统的强大的CPU监控工具
  4. 通过Linux中最高内存和CPU使用情况查找最高运行进程

总之, CPUTool非常适合Linux性能管理。 请通过下面的反馈表单分享您对这篇文章的想法。