Updates from 十月, 2015 Toggle Comment Threads | 键盘快捷键

  • jinzihao pm1:58 on 2015年10月20日 链接地址 | 回复  

    有时候从Windows上拷过来的文本文件在Linux下的某些程序(例如nmap)里无法正确解析,这是因为Windows和Linux换行符不同,此时可以用dos2unix工具转换一下。

     
  • jinzihao pm7:02 on 2015年10月16日 链接地址 | 回复  

    在OJ上做输入输出量很大的题时,在main()函数开头加两句:(在C中改用malloc(1 << 20)效果相同)

    	setvbuf(stdin, new char[1 << 20], _IOFBF, 1 << 20);
    	setvbuf(stdout, new char[1 << 20], _IOFBF, 1 << 20);
    

    通过创建一个足够大的输入输出缓冲区,可以减少IO操作的频率,提高IO性能。在实际测试中最多达到了300%的性能提升。
    这样看起来似乎有内存泄漏,但由于缓冲区的使命完成之时便是程序结束之时,程序结束时该段内存自然被释放,所以并不必担心内存泄漏。实际上,如果提前对该块内存进行delete[] 操作,是会造成运行时错误的。

     
  • jinzihao pm11:09 on 2015年10月13日 链接地址 | 回复  

    阳光长跑app(天天课)协议分析  

    我校历史悠久的阳光长跑活动这学期有了一种新形式——用手机app记录跑步路径,过去傍晚时分紫操排队刷卡的盛况大概不会出现了,不光避免了拥挤,时间地点灵活了跑步也能多些新鲜感…

    但似乎这app比以往的刷卡机还不靠谱,在地图上瞬间移动几百米的情况常常出现,瞬移的少还能偷些懒,瞬移得多跑步记录恐怕就要作废了…

    这样不靠谱的app,大概找出点漏洞也不会很难吧…于是拿Wireshark在app上传跑步记录时抓了个包…

    (具体的,用装有Wireshark的一台电脑(我这里用的raspberry pi)开热点分享网络,手机通过该热点上传跑步数据,上传过程即可被开热点的电脑上的抓包工具抓到;本想通过同一局域网下的其他电脑,在混杂模式下抓包,但是抓不到…求高人讲解…)

    ttk1

    竟然是没有加密的http…明文的…接下来就导出看一看吧

    这是登录时发送的请求:

    POST /index.php?app=interface&mod=User&act=loginByTypeAndId HTTP/1.1
    Content-Length: 198
    Content-Type: application/x-www-form-urlencoded
    Host: 182.92.227.205
    Connection: Keep-Alive
    Accept-Encoding: gzip
    Referer: http://182.92.227.205
    
    platform=android&appver=1.1.0&devid=865056026131165&username=********&net_env=WIFI&password=********&devname=XiaomiMI+3_4.4.4&screen_size=1920x1080&type=login&mac=14%3Af6%3A5a%3A8b%3A42%3Aae

    (用户名和密码就是登录app时输入的用户名和密码,此处隐去)

    返回如下:

    HTTP/1.1 200 OK
    Server: nginx/1.0.15
    Date: Tue, 13 Oct 2015 14:37:57 GMT
    Content-Type: text/html; charset=utf-8
    Connection: close
    X-Powered-By: PHP/5.3.3
    Set-Cookie: PHPSESSID=bucg5de9e******lbtng1mco77; path=/
    Expires: Thu, 19 Nov 1981 08:52:00 GMT
    Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
    Pragma: no-cache
    Server: nginx-niugeng
    Content-Length: 951
    
    {"token":"YjlhN2YyOGI4Mj**************YjZlNjk1N2UwYWE=","user":{"uid":"120082","email":"","nick":"u****u****u****","nick_pinyin":"","card":"","idNumber":"","name":"u****、u****u****","pinyin":"","address":"","birthday":"0","telephone":"","logo":"http://182.48.103.186/public/user_logo.jpg","type":"2","is_first_login":"1","intro":"","sex":"0","semester_id":"78","bank_name":"","bank_card":"","school":[{"id":"154","name":"u6e05u534eu5927u5b66","type":"1","description":"","create_time":"1413124157","tag":"","logo":"","memberCount":"21555","owner":"0","school_id":"28","class_id":"0"}],"class":[{"id":"263","name":"u5927u5b66u4e00u5e74u7ea7u8ba1u7b97u673au7cfb","type":"2","description":"","create_time":"1413271717","tag":"","logo":"","memberCount":"304","owner":"0","school_id":"0","class_id":"203"}],"permission":{"school_announcement":1,"class_announcement":1}},"complete_flag":"0","error_msg":"u6210u529f","error_code":"0"}

    (关键部分打码… u****表示一个utf-8编码的中文字符)

    拿着token和PHPSESSID,就可以进一步访问其他接口了,例如列出跑步记录,上传照片、加好友,甚至可以上传跑步记录…具体的接口请自行抓取吧,似乎找到了一个作弊的办法…

    ttk2

    一不小心跑步次数就从2次试成10次了…不幸的是没有删除记录的接口…体育老师不要打我…

    另外,app内上传的图片(跑步路径/跑后自拍)可以在没有登录的情况下访问到,例如:http://182.92.227.205/index.php?app=interface&mod=Resource&act=image&md=bf4a32d699561be2ba1225ad830528df

    会不会有隐私泄露的风险呢?这个md=后面的一串似乎是md5,但似乎查不出明文…不过当图床似乎还是可以的…

    另外,该apk似乎并没有进行某种加密/混淆,因此可以反编译…没搞过Android开发,就去下载了一个onekey-decompile-apk,成功一键反编译…所有用到的接口都在这个文件里:AppRequestClient,可以当作API文档来看了…

     
  • jinzihao pm5:12 on 2015年10月10日 链接地址 | 回复  

    对于浮点数a、b、c,if (a > b * c) 和 if (a / b > c)两种写法等价,但前者明显快于后者。

     
  • jinzihao pm5:40 on 2015年10月9日 链接地址 | 回复  

    在C++的algorithm库中有一个random_shuffle()函数,可以对任意两个迭代器(iterator)之间的序列随机打乱,例:

    srand(time(0));
    int array[100];
    for (int i = 0; i < 100; ++i) {
    	array[i] = i;
    }
    std::random_shuffle(array, array + 100);
    for (int i = 0; i < 100; ++i) {
    	printf("%d ", array[i]);
    }
    

    输出的即为0~99的一个随机排列。
    注意:和rand()函数一样,random_shuffle()在使用前需要用srand()设置随机数种子。

     
  • jinzihao pm4:44 on 2015年10月9日 链接地址 | 回复  

    在C++中如果需要移动数组,首选string.h里面的memcpy;如果源数组和目标数组有重叠,则改用string.h里面的memmove;尽量不要手写for循环来移动数组,性能上很难超过内置函数。

     
  • jinzihao pm2:01 on 2015年10月9日 链接地址 | 回复  

    (接前一条状态)另外,考虑功能相同的两种写法:

    s2Bit = ((((((((s2[k - 8] * 10 + s2[k - 7]) * 10 + s2[k - 6]) * 10) + s2[k - 5]) * 10 + s2[k - 4]) * 10 + s2[k - 3]) * 10 + s2[k - 2]) * 10 + s2[k - 1]) * 10 + s2[k]; 
    

    s2Bit = s2[k - 8] * 100000000 + s2[k - 7] * 10000000 + s2[k - 6] * 1000000 + s2[k - 5] * 100000 + s2[k - 4] * 10000 + s2[k - 3] * 1000 + s2[k - 2] * 100 + s2[k - 1] * 10 + s2[k];
    

    在性能上后者更有优势,但差别有限(与程序的其他部分分摊后大约节省了14%的时间)

     
  • jinzihao pm1:55 on 2015年10月9日 链接地址 | 回复  

    顺着前一个状态的思路,又写了一个Ver 5,这次效率提升非常明显 (Ver 1在一组数据下的总时间为3.4s,Ver 4为2.9s,而Ver5只有1.8s):
    (注:在此处没有附上的外层循环代码中,变量k从n开始每次向下减9,而n在题目中可以大到5000,故绝大多数情况下k – 8 > 0,执行if {}部分,只有极少数情况执行else {}部分,即退化为Ver 4)

    //Ver 5
    if (k - 8 > 0) {
    	s2Bit = s2[k - 8] * 100000000 + s2[k - 7] * 10000000 + s2[k - 6] * 1000000 +
    		s2[k - 5] * 100000 + s2[k - 4] * 10000 + s2[k - 3] * 1000 +
    		s2[k - 2] * 100 + s2[k - 1] * 10 + s2[k];
    }
    else {
    	for (l = 0; l < k; ++l) {
    		s2Bit = (s2Bit + s2[l]) * 10;
    	}
    	s2Bit += s2[l];
    }
    
     
  • jinzihao pm12:49 on 2015年10月9日 链接地址 | 回复  

    以下四个版本的代码均摘自一道关于高精度乘法的题,作用均为把9位0~9的数字合并为一个长整数。其中llpow为一个预定义的数组,llpow[i]为10的i次方。
    实际测试发现,Ver 4的性能最优,Ver 2次之,Ver 1较差,Ver3最差…
    思考:
    Ver2相比Ver1,发现for循环的判断部分(第二个参数)一定要精简,判断部分要被执行n次,而初始化部分(第一个参数)只被执行1次,这里只不过循环9次,都有可以检测到的性能差别。
    Ver4相比Ver3,发现修改s2Bit是个很费时的操作,表达式能一步完成尽量不要分两步
    疑问:
    Ver4中(s2Bit + s2[l])的结果难道不要存到临时变量中吗?这个操作效率很高?还是编译器做了优化?还是处理器做了优化?
    Ver2慢于Ver4是否因为数组(llpow10)的寻址浪费了时间呢?还是不然?

    //Ver 1
    for (l = k; l > ((k - 9 > -1) ? k - 9 : -1); --l) {
    	s2Bit += s2[l] * llpow10[k - l];  
    }
    
    //Ver 2
    for (l = ((k - 8 > 0) ? k - 8 : 0); l < k; ++l) {
    	s2Bit += s2[l] * llpow10[k - l];  
    }
    s2Bit += s2[l];
    
    //Ver 3
    for (l = ((k - 8 > 0) ? k - 8 : 0); l < k; ++l) {
    	s2Bit += s2[l];
    	s2Bit *= 10;
    }
    s2Bit += s2[l];
    
    // Ver 4
    for (l = ((k - 8 > 0) ? k - 8 : 0); l < k; ++l) {
    	s2Bit = (s2Bit + s2[l]) * 10;
    }
    s2Bit += s2[l];
    
     
  • jinzihao pm9:11 on 2015年10月8日 链接地址 | 回复  

    在OJ上遇到结果错误(wrong answer),有时是选取的数据类型上限不够大,而上限不够大不一定会在输入数据的时候暴露出问题…有可能输入的数据在char范围内,但在程序中相乘/累加之后直接超出了long long的上限…这时可以根据需要采用取模(mod)或取符号(sgn)来控制数据范围…

     
c
写新的
j
下一篇文章/下一个回复
k
前一篇文章/以前的回复
r
回复
e
编辑
o
显示/隐藏 回复
t
回到顶部
l
go to login
h
show/hide help
shift + esc
取消