use ncrack to test rdp login

前言

给同学写个小程序,测试给定的rdp ip,port, user,pwd,是否为正确的登录信息。
开始他找了个基于x11的开源程序(还是个服务程序, 不能直接看到rdp登录的效果),让我抽出rdp登录的实现,做个工程。

看了一下,牵扯的还挺多,最重要的是编译不过(开源的工程各种依赖),无法看到实际运行的效果。如果要研究编译过,工作流程的实现,需要时间。如果要从他找的x11开源程序中抽取出rdp登录的实现,这周末时间也不够啊:)

对于这种友情任务,非正式的工程,抓到老鼠,就是好猫。谁会关心是咋实现的:)

我去找了freerdp,编译完,可以运行。看了登录那块,还不是在最开始就登录(开始就是去连接一下,只能确定ip, port是否正确)。也是各种类嵌套的很紧。freerdp是可以调试,也能追到登录那。不过,要想抽取出来,不带x11, 时间上也不允许。

我换了个思路在想这事。同学要的就是rdp登录验证,网上有很多协议登录暴力破解的程序。和登录验证不同之处,就是暴力破解程序不知道登录信息中的user/pwd, 我这场景知道user/pwd. 就这点区别。我找个暴力破解登录信息的程序,改一下,只登录一次,知道登录成功/失败即可。

按照这个思路,我找到了ncrack, 研究了一天(命令行的使用,登录成功和失败的实现在哪里),发现ncrack有个bug, 如果给出的是错误的用户名和口令,ncrack不退出。我将这bug修复了。按照ncrack的使用场景,这不算是bug(本来就要登录不成功,就要换登录信息重试的)。

改完之后的效果:在ncrack命令行给定rdp登录信息(ip, port, user, pwd)和日志名称, 登录一次,不管登录成功或失败,都要退出。然后看日志,如果日志中出现"Discovered credentials on rdp",说明找到了rdp登录的凭据有效,否则为登录失败。

后续我再写个小程序来调用改版的ncrack, 等ncrack执行完,去查日志,找登录成功或失败的字符串标记,如果成功,程序退出码为EXIT_SUCCESS;如果失败,程序退出码为其他值。这样,就完成了验证rdp登录信息是否正确的任务。

实验

本实验目的:ncrack的下载,编译,修改,验证单次登录。

ncrack的下载

github上有ncrack的master分支,这个分支编译不过的。要去下载ncrack的发布版本.
https://nmap.org/ncrack/dist/ncrack-0.6.tar.gz

如果要想学习其他发布版本,去https://nmap.org/ncrack/dist/可以看到
use ncrack to test rdp login

编译环境

正好换了硬盘,重新装了一个debian8.8的虚拟机环境。刚装完,就做了一个快照,就从这个快照开始做实验。

su - root
mkdir /home/dev
chmod 777 -R /home/dev
cd /home/dev

下载ncrack

wget https://nmap.org/ncrack/dist/ncrack-0.6.tar.gz
ls -l
总用量 3824
-rw-r--r-- 1 root root 3913259 10月 30  2017 ncrack-0.6.tar.gz
root@debian8-8-bits64:/home/dev# tar -xzvf ./ncrack-0.6.tar.gz
ls -l
总用量 3828
drwxrwxr-x 10 root root    4096 10月 12  2017 ncrack-0.6.0
-rw-r--r--  1 root root 3913259 10月 30  2017 ncrack-0.6.tar.gz
cd ncrack-0.6.0/

修改软件源

cat /etc/apt/sources.list
# 

# deb cdrom:[Debian GNU/Linux 8.8.0 _Jessie_ - Official amd64 DVD Binary-1 20170506-14:13]/ jessie contrib main

# deb cdrom:[Debian GNU/Linux 8.8.0 _Jessie_ - Official amd64 DVD Binary-1 20170506-14:13]/ jessie contrib main

deb http://mirrors.163.com/debian/ jessie main
deb-src http://mirrors.163.com/debian/ jessie main

deb http://security.debian.org/ jessie/updates main contrib
deb-src http://security.debian.org/ jessie/updates main contrib

# jessie-updates, previously known as 'volatile'
deb http://mirrors.163.com/debian/ jessie-updates main contrib
deb-src http://mirrors.163.com/debian/ jessie-updates main contrib

更新软件源

aptitude update

安装依赖环境

aptitude install build-essential checkinstall libssl-dev libssh-dev
aptitude install autoconf

编译ncrack

./configure
这个配置是release版,如果要调试程序,需要自己将每个Makefile中去掉 -O2选项,加上-g选项

用find先将所有的Makefile都找出来
find . -name 'Makefile'
./modules/Makefile
./mswin32/Makefile
./macosx/Makefile
./opensshlib/Makefile
./Makefile
./nsock/examples/Makefile
./nsock/src/Makefile
./nsock/tests/Makefile
./nbase/Makefile
./docs/Makefile

拿主Makefile来做例子,看看要改哪里。如果不改,用gdb调试时,看不到源码。
use ncrack to test rdp login
use ncrack to test rdp login
每次执行./configure后,都会覆盖所有的Makefile.
将所有的Makefile都修改成debug版之后,make clean 后,删除*.log, 用winscp传回本地svn目录存档,如果在同一个环境下编译工程,以后就不需要再执行./configure. 只需要改代码,上传,编译.
为了winscp能上传覆盖/home/dev/ncrack-0.6.0(编译的时候用的root用户,winscp是其他用户),需要修改/home/dev/ncrack-0.6.0的权限。

chmod 777 -R /home/dev

将改过Makefile的工程上传后,编译一下

make

不用安装了,直接运行程序就行。

命令行参数列表

root@debian8-8-bits64:/home/dev/ncrack-0.6.0# ./ncrack --help
Ncrack 0.6 ( http://ncrack.org )
Usage: ncrack [Options] {target and service specification}
TARGET SPECIFICATION:
  Can pass hostnames, IP addresses, networks, etc.
  Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254
  -iX <inputfilename>: Input from Nmap's -oX XML output format
  -iN <inputfilename>: Input from Nmap's -oN Normal output format
  -iL <inputfilename>: Input from list of hosts/networks
  --exclude <host1[,host2][,host3],...>: Exclude hosts/networks
  --excludefile <exclude_file>: Exclude list from file
SERVICE SPECIFICATION:
  Can pass target specific services in <service>://target (standard) notation or
  using -p which will be applied to all hosts in non-standard notation.
  Service arguments can be specified to be host-specific, type of service-specific
  (-m) or global (-g). Ex: ssh://10.0.0.10,at=10,cl=30 -m ssh:at=50 -g cd=3000
  Ex2: ncrack -p ssh,ftp:3500,25 10.0.0.10 scanme.nmap.org google.com:80,ssl
  -p <service-list>: services will be applied to all non-standard notation hosts
  -m <service>:<options>: options will be applied to all services of this type
  -g <options>: options will be applied to every service globally
  Misc options:
    ssl: enable SSL over this service
    path <name>: used in modules like HTTP ('=' needs escaping if used)
    db <name>: used in modules like MongoDB to specify the database
    domain <name>: used in modules like WinRM to specify the domain
TIMING AND PERFORMANCE:
  Options which take <time> are in seconds, unless you append 'ms'
  (miliseconds), 'm' (minutes), or 'h' (hours) to the value (e.g. 30m).
  Service-specific options:
    cl (min connection limit): minimum number of concurrent parallel connections
    CL (max connection limit): maximum number of concurrent parallel connections
    at (authentication tries): authentication attempts per connection
    cd (connection delay): delay <time> between each connection initiation
    cr (connection retries): caps number of service connection attempts
    to (time-out): maximum cracking <time> for service, regardless of success so far
  -T<0-5>: Set timing template (higher is faster)
  --connection-limit <number>: threshold for total concurrent connections
  --stealthy-linear: try credentials using only one connection against each specified host 
    until you hit the same host again. Overrides all other timing options.
AUTHENTICATION:
  -U <filename>: username file
  -P <filename>: password file
  --user <username_list>: comma-separated username list
  --pass <password_list>: comma-separated password list
  --passwords-first: Iterate password list for each username. Default is opposite.
  --pairwise: Choose usernames and passwords in pairs.
OUTPUT:
  -oN/-oX <file>: Output scan in normal and XML format, respectively, to the given filename.
  -oA <basename>: Output in the two major formats at once
  -v: Increase verbosity level (use twice or more for greater effect)
  -d[level]: Set or increase debugging level (Up to 10 is meaningful)
  --nsock-trace <level>: Set nsock trace level (Valid range: 0 - 10)
  --log-errors: Log errors/warnings to the normal-format output file
  --append-output: Append to rather than clobber specified output files
MISC:
  --resume <file>: Continue previously saved session
  --save <file>: Save restoration file with specific filename
  -f: quit cracking service after one found credential
  -6: Enable IPv6 cracking
  -sL or --list: only list hosts and services
  --datadir <dirname>: Specify custom Ncrack data file location
  --proxy <type://proxy:port>: Make connections via socks4, 4a, http.
  -V: Print version number
  -h: Print this help summary page.
MODULES:
  SSH, RDP, FTP, Telnet, HTTP(S), POP3(S), IMAP, SMB, VNC, SIP, Redis, PostgreSQL, MySQL, MSSQL, MongoDB, Cassandra, WinRM, OWA
EXAMPLES:
  ncrack -v --user root localhost:22
  ncrack -v -T5 https://192.168.0.1
  ncrack -v -iX ~/nmap.xml -g CL=5,to=1h
SEE THE MAN PAGE (http://nmap.org/ncrack/man.html) FOR MORE OPTIONS AND EXAMPLES

选择合适的命令行参数做测试

命令行参数应该包括ip, port, user,pwd, 超时时间,日志名称
输入正确的参数后,应该能登录rdp服务器才对。

/home/dev/ncrack-0.6.0/ncrack -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,to=3

上述命令行参数的含义

-oN 指定日志名称
--user 指定用户名
--pass 指定口令
rdp://192.168.2.61:3389 指定协议为rdp, ip和port
to=3 指定单次登录超时时间,过了这个超时时间,本次登录就放弃。

值得注意的是to参数必须跟在ip,port之后,前面有‘,’符号
看ncrack的--help说明中的例子,可以看出如果是-x参数, 就可以写在前面。如果不带-的参数,就必须写在ip,port的后面

如果能编译过了,能正常运行,就可以用si建立看代码的工程,用gdb去调试,看看程序的实现思路和细节。

如果测试出bug,先看看命令行参数是否指定的不合适(因为不是自己写的程序,有可能命令行参数组合的复杂,暂时指定的不对,或真的没有参数符合自己的应用),如果真的不是命令行指定的错误或者真的不符合自己的应用,这时才需要去改工程。

写个脚本来测试编译出的ncrack

写个脚本可以使测试的更方便,还可以在脚本中记录笔记.

#!/bin/sh
# @file rdp_test.sh
# @brief test how to use ncrack to test rdp protocol connect

fn_print_empty_line() {
	# $1 is parameter 1
	
	for i in $(seq 1 1 $1)
	do
		echo
	done

	return 0
}

fn_test_ncrack() {
	# 不好使
	# config project to debug version
	# ./configure --enable-debug
	
	# ok
	# /home/dev/ncrack-0.6.0/ncrack --help > /home/dev/ncrack.help
	# cat /home/dev/ncrack.help
	
	# /home/dev/ncrack-0.6.0/ncrack -T1 -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 192.168.2.61:3389 >& /home/dev/run_result.log
	# /home/dev/ncrack-0.6.0/ncrack -T1 -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 192.168.2.61:3389 > /home/dev/run_result.log
	# /home/dev/ncrack-0.6.0/ncrack --help > /home/dev/run_result.log
	# /home/dev/ncrack-0.6.0/ncrack -T1 -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389 > /home/dev/run_result.log

	# 不能重定向到文件, 会有报错: Text: irrelevant message
	# /home/dev/ncrack-0.6.0/ncrack -T1 -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389 > /home/dev/run_result.log

	# ok
	# /home/dev/ncrack-0.6.0/ncrack -T1 -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389

	# ok
	# /home/dev/ncrack-0.6.0/ncrack -T1 -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,at=0,cl=1

	# ok
	# /home/dev/ncrack-0.6.0/ncrack -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,to=3

	# ok 这是信息输出最少的命令行参数
	/home/dev/ncrack-0.6.0/ncrack -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,to=3

	# fn_print_empty_line 6
	# cat /home/dev/run_result.log

	fn_print_empty_line 6
	# 在 /home/dev/scan.log 中找字符串 "Discovered credentials on rdp"
	# 如果找到, 说明登录成功
	cat /home/dev/scan.log

	return 0
}

main() {
	# clear screen
	clear

	# print 25 empty lines
	fn_print_empty_line 25

	# test ncrack
	fn_test_ncrack
}

main
exit 0
chmod 777 /home/dev/*.sh
/home/dev/rdp_test.sh

根据单次登录的场景修改代码

测试的结果为:

  • 如果工程未修改,给的登录参数对,程序执行完,就退出,并生成日志
  • 如果工程未修改,给的登录参数不对,程序运行起来,始终不会退出。
    加了-vv -d10参数,发现了工程实现中有死循环的地方。作者的调试思路真细致。

用gdb单步,看看在哪改合适

/home/dev/ncrack-0.6.0# chmod 777 -R ./*
将自己改过Makefile为-g的版本上传, 然后再make一次, 不要运行./configure
aptitude install gdb
gdb -tui --args /home/dev/ncrack-0.6.0/ncrack -vv -d10 -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,to=3

通过日志信息和单步的结果,看到应该改/ncrack-0.6.0/ncrack.cc中的ncrack(ServiceGroup *SG),
针对我的场景(正规登录一次),改这里好使。

static int
ncrack(ServiceGroup *SG)
{
	bool is_process_continue = true; // 是否继续处理
  /* nsock variables */
  struct timeval now;
  enum nsock_loopstatus loopret;
  list <Service *>::iterator li;
  nsock_pool nsp;
  int nsock_timeout = 3000;
  int err;

  /* create nsock p00l */
  if (!(nsp = nsock_pool_new(SG)))
    fatal("Can't create nsock pool.");

  if (o.proxychain) {
    if (nsock_pool_set_proxychain(nsp, o.proxychain) == -1)
      fatal("Unable to set proxychain for nsock pool");
  }

  gettimeofday(&now, NULL);
  nsock_set_loglevel(o.nsock_loglevel);

#if HAVE_OPENSSL
  /* We don't care about connection security, so cast Haste */
  nsock_pool_ssl_init(nsp, NSOCK_SSL_MAX_SPEED);
#endif

  SG->findMinDelay();
  /* We have to set the nsock_loop timeout to the minimum of the connection
   * delay, since we have to check every that time period for potential new
   * connection initiations. If the minimum connection delay is 0 however, we
   * don't need to do it, since that would make nsock_loop return immediately
   * and consume a lot of CPU.
   */
  if (SG->min_connection_delay != 0)
    nsock_timeout = SG->min_connection_delay;

  /* Initiate time-out clocks */
  startTimeOutClocks(SG);

  /* initiate all authentication rate meters */
  SG->auth_rate_meter.start();
  for (li = SG->services_all.begin(); li != SG->services_all.end(); li++)
    (*li)->auth_rate_meter.start();

  /*
   * Since nsock can delay between each event due to the targets being really
   * slow,  we need a way to make sure that we always poll for interactive user
   * input regardless of the above case. Thus we schedule a special timer event
   * that happens every KEYPRESSED_INTERVAL milliseconds and which reschedules
   * itself every time its handler is called.
   */
  nsock_timer_create(nsp, status_timer_handler, KEYPRESSED_INTERVAL, NULL);

  /*
   * We do the same for checking pending signals every SIGNAL_CHECK_INTERVAL
   */
  nsock_timer_create(nsp, signal_timer_handler, SIGNAL_CHECK_INTERVAL, NULL);

  ncrack_probes(nsp, SG);

  /* nsock loop */
  do {

    loopret = nsock_loop(nsp, nsock_timeout);

  	switch (loopret) {
		case NSOCK_LOOP_TIMEOUT: {
			is_process_continue = false;
			err = nsock_pool_get_error(nsp);
			error("nsock_loop timeout : Error code %d (%s)",
				err, strerror(err));
			}
			break;
		case NSOCK_LOOP_ERROR: {
			is_process_continue = false;
			err = nsock_pool_get_error(nsp);
			error("nsock_loop error : Unexpected, Error code %d (%s)",
				err, strerror(err));
			}
			break;
		case NSOCK_LOOP_QUIT: {
			is_process_continue = false;
			err = nsock_pool_get_error(nsp);
			error("nsock_loop quit : Error code %d (%s)",
				err, strerror(err));
			}
			break;
		default: {
			err = nsock_pool_get_error(nsp);
			error("nsock_loop unknow return code(%d) : Error code %d (%s)",
				(int)loopret,
				err, strerror(err));
			}
			break;
  	}

    ncrack_probes(nsp, SG);

	// 如果nsock_loop返回结果是(超时,错误,退出),就不尝试登录了
	// 但是也不能强行exit, 按照正常流程走
	if (!is_process_continue) {
		break;
	}
	
	// if task was not finished, continue
  } while (SG->services_finished.size() != SG->total_services);

  nsock_pool_delete(nsp);

  if (o.debugging > 4)
    log_write(LOG_STDOUT, "nsock_loop returned %d\n", loopret);

  return 0;
}

修改后的效果

不管登录是否成功,拿登录参数去登录都只登录一次,就退出。
然后看日志中是否有登录成功的信息"Discovered credentials on rdp", 如果找到,说明登录成功,否则说明登陆失败。

后续的工作

同学跟我说的需求是搞一个程序,给定登录参数在命令行,如果登录成功,程序返回0;如果登录失败,程序返回错误码。

ncrack实验成功后,那我需要再写个小程序,按照命令行给定的登录参数来拼装调用ncrack的命令行(登录信息,超时时间,日志名称),尝试将屏幕信息都屏蔽掉(e.g. > /dev/null)。在ncrack阻塞执行完成后,去读取指定的ncrack日志,看看日志中是否有登录成功的字符串信息。

调用ncrack和分析日志的小程序

分2个程序,一个C程序干活(调用ncrack+分析日志),一个bash脚本来调用C程序的ELF文件来测试。
测试过了好使

// @file main.cpp
// @brief call ncrack modify version by me, and parse the log file gen by ncrack, to besure login ok or failed

#include <stdlib.h>
#include <stdio.h>

#include <string.h>
#include <unistd.h>

#include <syslog.h>
#include <signal.h>
#include <limits.h>
#include <libgen.h>

#include <sys/stat.h>
#include <sys/wait.h>

#include <string>

#ifndef MYLOG_D
#define MYLOG_D(fmt, ...) \
	do { \
		syslog(LOG_INFO, "[%s : %s.%d : %s()] : " fmt, "LS_LOG", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); \
	} while (0);
	
#endif // #ifndef MYLOG_D

void init(const char* psz_log_owner_name);
void uninit();
void proc_sig_term(int num);
int fn_test(int argc, char** argv);

std::string get_pathname_from_pid(pid_t i_pid);
std::string get_path_from_pathname(const char* psz_pathname);
bool is_file_exist(const char* psz_file_pathname);
int parse_ncrack_log_file(const char* psz_log_file_pathname);

int main(int argc, char** argv)
{
	int i_rc = EXIT_SUCCESS;
    char sz_buf[1024] = {'\0'};

#ifdef MAKE_FILE_MACRO__BIN_NAME
    sprintf(sz_buf, "%s", MAKE_FILE_MACRO__BIN_NAME);
    init(sz_buf);
    MYLOG_D("MAKE_FILE_MACRO__BIN_NAME = [%s]", MAKE_FILE_MACRO__BIN_NAME);
#else
    init(NULL);
#endif // #ifdef MAKE_FILE_MACRO__BIN_NAME

    i_rc = fn_test(argc, argv);
    uninit();

    MYLOG_D("THE END");
    return i_rc;
}

void init(const char* psz_log_owner_name)
{
    int i = 0;

    // daemon(0, 0);
    openlog(((NULL != psz_log_owner_name) ? psz_log_owner_name : "my_syslog"), LOG_NOWAIT | LOG_PID, LOG_LOCAL1);

    // clear screen (print 25 empty line)
    for (i = 0; i < 25; i++) {
        MYLOG_D("");
    }

    signal(SIGTERM, proc_sig_term);
}

void uninit()
{
	closelog();
}

void proc_sig_term(int num)
{
    MYLOG_D("SIGTERM = %d, num = %d", SIGTERM, num);
    MYLOG_D("maybe can do some clean task before quit");
    exit(1);    
}

int fn_test(int argc, char** argv)
{
	// 本程序被调用格式的命令行格式
	// rdp_login_verify -ip 192.168.2.61 -port 3389 -user administrator -pwd 111111

	// 实际执行的程序命令行格式
	// ./ncrack -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,to=3

	int i_rc = -1;
	int i_fn_rc = -1;
	int i = 0;
	std::string str_my_pathname = "";
	std::string str_my_path = "";
	std::string str_ncrack_path = "";

	std::string str_ip = "";
	std::string str_port = "";
	std::string str_user = "";
	std::string str_pwd = "";
	std::string str_log_file_pathname = "";
	char sz_cmd_line[0x1000] = {'\0'};

	do {
		str_my_pathname = get_pathname_from_pid(getpid());
		// /home/dev/rdp_login_verify/rdp_login_verify
		MYLOG_D("str_my_pathname = %s", str_my_pathname.c_str());

		str_my_path = dirname((char*)str_my_pathname.data());
		// /home/dev/rdp_login_verify
		MYLOG_D("str_my_path = %s", str_my_path.c_str());

		str_ncrack_path = str_my_path.c_str();
		str_ncrack_path += "/ncrack";
		MYLOG_D("str_ncrack_path = %s", str_ncrack_path.c_str());

		if (!is_file_exist(str_ncrack_path.c_str())) {
			MYLOG_D("%s not exist...", str_ncrack_path.c_str());
			break;
		}

		// rdp_login_verify -ip 192.168.2.61 -port 3389 -user administrator -pwd 111111
		if (9 != argc) {
			break;
		}

		for (i = 1; i <=8; i += 2) {
			if (0 == strcmp(argv[i], "-ip")) {
				str_ip = argv[i + 1];
			} else if (0 == strcmp(argv[i], "-port")) {
				str_port = argv[i + 1];
			} else if (0 == strcmp(argv[i], "-user")) {
				str_user = argv[i + 1];
			} else if (0 == strcmp(argv[i], "-pwd")) {
				str_pwd = argv[i + 1];
			}
		}

		MYLOG_D("rdp connect info = %s:%s %s/%s",
			str_ip.c_str(),
			str_port.c_str(),
			str_user.c_str(),
			str_pwd.c_str());

		memset(sz_cmd_line, 0, sizeof(sz_cmd_line));
		snprintf(sz_cmd_line, sizeof(sz_cmd_line),
			"/tmp/rdp_login_scan_%d.log",
			getpid());
		str_log_file_pathname = sz_cmd_line;

		// 拼命令行
		// ./ncrack -oN /home/dev/scan.log --user administrator --pass 111111 rdp://192.168.2.61:3389,to=3
		memset(sz_cmd_line, 0, sizeof(sz_cmd_line));
		snprintf(sz_cmd_line, sizeof(sz_cmd_line),
			"%s -oN %s --user %s --pass %s rdp://%s:%s,to=3",
			str_ncrack_path.c_str(),
			str_log_file_pathname.c_str(),
			str_user.c_str(),
			str_pwd.c_str(),
			str_ip.c_str(),
			str_port.c_str());

		MYLOG_D("call [%s]", sz_cmd_line);

		// 执行命令
		i_fn_rc = system(sz_cmd_line);
		if (-1 == i_fn_rc) {
		  MYLOG_D("shell could not be run");
		} else {
		  MYLOG_D("result of running command is %d", WEXITSTATUS(i_fn_rc));
		}

		// 分析日志文件
		i_rc = parse_ncrack_log_file(str_log_file_pathname.c_str());

		// 删除日志文件
		unlink(str_log_file_pathname.c_str());
	} while (0);

	MYLOG_D("rdp login %s", (EXIT_SUCCESS == i_rc) ? "ok" : "failed");

    return i_rc;
}

int parse_ncrack_log_file(const char* psz_log_file_pathname)
{
	int i_rc = EXIT_FAILURE;
	FILE* pf = NULL;
	char sz_buf[0x1000] = {'\0'};

	do {
		if (NULL == psz_log_file_pathname) {
			break;
		}
		
		if (!is_file_exist(psz_log_file_pathname)) {
			break;
		}

		pf = fopen(psz_log_file_pathname, "r");
		if (NULL == pf) {
			break;
		}

		
        memset(sz_buf, 0, sizeof(sz_buf));
		while (NULL != fgets(sz_buf, sizeof(sz_buf), pf)) {
			if (NULL != strstr(sz_buf, "Discovered credentials for rdp on")) {
				MYLOG_D("%s", sz_buf);
				i_rc = EXIT_SUCCESS;
				break;
			}
			
	        memset(sz_buf, 0, sizeof(sz_buf));
		}
	} while (0);

	if (NULL != pf) {
		fclose(pf);
		pf = NULL;
	}

	return i_rc;
}

std::string get_pathname_from_pid(pid_t i_pid)
{
	std::string str_path;

	char path[PATH_MAX] = {'\0'};
	char dest[PATH_MAX] = {'\0'};
	
	memset(dest, 0, sizeof(dest)); // readlink does not null terminate!
	
	// struct stat info;
	sprintf(path, "/proc/%d/exe", i_pid);
	
	if (readlink(path, dest, PATH_MAX) == -1) {
		// perror("readlink");
	} else {
		str_path = dest;
	}

	// /home/dev/rdp_login_verify/rdp_login_verify
	return str_path;
}

bool is_file_exist(const char* psz_file_pathname)
{
	bool b_rc = false;
	struct stat buf;

	do {
		if (NULL == psz_file_pathname) {
			break;
		}

		if (-1 == stat("psz_file_pathname", &buf)) {
		} else {
			b_rc = true;
		}

		b_rc = true;
	} while (0);

	return b_rc;
}


#!/bin/sh
# @file test_case.sh
# @brief test how to call rdp_login_verify

fn_print_empty_line() {
	# $1 is parameter 1
	
	for i in $(seq 1 1 $1)
	do
		echo
	done

	return 0
}

fn_test() {
	./rdp_login_verify -ip 192.168.2.61 -port 3389 -user administrator -pwd 111111
	# print function's return value
	echo $?
	return 0
}

main() {
	# clear screen
	clear

	# print 25 empty lines
	fn_print_empty_line 25

	# test
	fn_test
}

main
exit 0

上一篇:FreeRDP简介


下一篇:本地计算机无法复制文件到远程计算机