周一,又是个无比忧伤的日子。学长学姐忙着毕业,倍儿开心,然而我们却要开始继续日复一日年复一年的烟酒僧生活,damn it!
早上来了就开始忙着看学长的代码,真是难受!但是昨晚好像做了个春梦,感觉爽爽的……
早上主要看的代码是之前虚拟机跑僵尸程序那块,大体是读取一个input文件,得到僵尸程序可执行文件,然后使用shellexecute调用可执行文件,通过抓取僵尸程序运行过程中产生的流量并保存。该文件主要用来得到黑名单,从而用于僵尸网络检测系统黑名单匹配模块。因为之前就能正常运行,但是有些不能解决的bug,所以要从头仔细慢慢看。卧槽,不看不知道,一看吓一跳。各种不靠谱,各种不严谨,各种乱七八糟。我真是曰了狗了,他们之前的实验是怎么做的,我在想。下面主要就自己解决的几个问题来总结下。
1、首先,因为虚拟机进程不能正常kill。之前师兄老是说虚拟机运行一段时间后就会死机,这就是会死机的原因啊!你进程一直kill不掉,到一定时间机器怎么能hold住?!换一个人一直长时间负荷运转,也吃不消啊……之前学长的代码是这样进行进程的kill的:
hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,cpList[i]);
if(hProcess==NULL)
{
printf("error opening process!\n");
return ;
}
if(!TerminateProcess(hProcess,0))
{
printf("error killing process!\n");
return ;
}
其中OpenProcess的原型及用法有:
http://baike.baidu.com/link?url=zVaohX3FXM3NWgbO0j2g54WzFcmhtADfNlUiWTmcplACmE39bm0wQbqqZS4yds6yyK5ZJUyd__Y9bf_OzW4Cu_
获取到进程号之后,然后进行进程的kill。上网查了方法,这种方法是可行的,但是为什么进程就是kill不掉呢?!为了直观地显示进程是否kill,我比较了hellexecute调用可执行代码的前与后的进程序列,发现真正地没有对多出来的进程进行kill。那为什么在VS上调试可运行并且起作用,但到了这里就不可以了呢?!第一感觉就是,虚拟机环境的问题。然后直接上网百度了,发现确实存在这样的问题。相关链接为:
https://social.msdn.microsoft.com/Forums/zh-CN/20e3fc99-1d21-43b5-9bc2-03a52a721dc5/openprocess?forum=visualcpluszhchs
把上面第一部分的代码加入到之前我们的代码中,发现程序可以按照我们预想的那样去运行。此外,之前运行的system指令也可以正常运行了。原来虚拟机上的处理与实体机处理还是不相同的,存在着权限提取问题。
所添加的这部分代码如下,以后可能还会用到。
void EnableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if ( ! OpenProcessToken( GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
{
printf("提权失败。\n");
return;
}
if ( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) )
{
CloseHandle( hToken );
printf("提权失败。\n");
return;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )
{
printf("提权失败。\n");
CloseHandle( hToken );
}
else
printf("提权成功!\n");
}
至此,虚拟机那边调用僵尸可执行文件的程序可以按照我预想的那样正常运行,算了屌了一把……忍不住要夸自己就是个天才!
下午的时候,潘博过来找人写一个组流的程序,讲了大致要实现的功能。因为研三的那些人都忙着给老板提交演示,没人有时间搭理他。但是听了潘博所要求的功能后,土豪忽然回忆起来之前他有叫我写过一个类似的程序。然后我就试着给潘博改代码。因为类似,很快就改好了……但是,不能用!因为之前只要80端口的流量,所以做map匹配的时候很简单,但是现在是要求五元组用来做map的key,原理其实差不多,只是有些细节要处理。首先想着用map
struct mapvalue
{
pcap_dumper_t *pc; //pcap文件的句柄
time_t tm_sec; //时间,因为需要判断流超时
};
但是程序跑起来之后,不能正常地区分不同的char *。就是对一个pcap文件进行处理后,最后得到的还是一个文件,这肯定不对!想了好一会,不知道原因。后来,换用
map
void my_itoa(u_int32_t value,string &dst,int radix)
{
char tmp[32]={'\0'};
u_int32_t tmpval=value;
int i,j;
for(i=0;i<32;i++)
{
tmp[i]=(tmpval%radix)+'0';
tmpval=tmpval/radix;
if(tmpval==0)
break;
}
for(;i>=0;--i)
{
dst=dst+tmp[i];
}
}
很弱,不值得一提。下面我就可以使用连接符+从而获得能够标示一段流的map的key的string。
这个map的功能实现后,下面主要就是读取pcap包,并将其按照五元组分别放到不同的pcap包中。由于涉及到超时问题,所以就需要一个时间标记来注明,在之前的struct mapvalue中也予以说明。这里需要说明的是,之前一直以为map的值不能够使用结构体,经过这次之后,发现这种想法是不对的。完全可以使用结构体作为map的second值!
经过上面的修改后,程序便可以正常运行,但是得到的结果文件中数量太大,而且很多都是小文件。跟潘博商量程序结果后,便决定将一些比较小的文件予以删除。对pcap包的处理,需要对pcap文件格式有很好的了解,这对学网络数据包处理来说,是必须要去掌握的。此外,还需要对pcap data里面各层数据的解析需要对以太层、IP层以及各种上层协议的理解。总的来说,主要用到了两个知识点,根据网上的一些代码,整理如下:
1、Linux C读取文件夹下所有文件(包括子文件夹)的文件名
Linux C 下面读取文件夹要用到结构体struct dirent,在头#include
#include
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /*offset to this dirent在目录文件中的偏移 */
unsigned shortd_reclen;/* length of this d_name 文件名长 */
unsigned chard_type;/* the type of d_name 文件类型 */
char d_name[NAME_MAX+1];/*file name (null-terminated) 文件名,最长255字符 */
}
其中d_type表明该文件的类型:文件(8)、目录(4)、链接文件(10)等。
下面程序,递归读取某文件夹及其子文件夹下所有文件名:
1 #include
2 #include
3 #include <string.h>
4 #include
5 #include
6 int readFileList(char*basePath)
7 {
8 DIR *dir;
9 structdirent *ptr;
10 char base[1000];
11
12 if ((dir=opendir(basePath)) == NULL)
13 {
14 perror("Open dir error...");
15 exit(1);
16 }
17
18 while ((ptr=readdir(dir)) != NULL)
19 {
20 if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0) ///current dir OR parrentdir
21 continue;
22 else if(ptr->d_type== 8) ///file
23 printf("d_name:%s/%s\n",basePath,ptr->d_name);
24 else if(ptr->d_type== 10) ///link file
25 printf("d_name:%s/%s\n",basePath,ptr->d_name);
26 else if(ptr->d_type== 4) ///dir
27 {
28 memset(base,'\0',sizeof(base));
29 strcpy(base,basePath);
30 strcat(base,"/");
31 strcat(base,ptr->d_name);
32 readFileList(base);
33 }
34 }
35 closedir(dir);
36 return 1;
37 }
38
39 int main(void)
40 {
41 DIR *dir;
42 char basePath[1000];
43
44 ///get the current absoultepath
45 memset(basePath,'\0',sizeof(basePath));
46 getcwd(basePath,999);
47 printf("the current dir is : %s\n",basePath);
48
49 ///get the file list
50 memset(basePath,'\0',sizeof(basePath));
51 strcpy(basePath,"./XL");
52 readFileList(basePath);
53 return 0;
54 }
执行输出 :
2、linux下C获取文件的大小
获取文件大小这里有两种方法:
方法一、
范例:
[cpp] view plaincopyprint?
此种以打开文件的方法取得文件的大小,不适合大文件,并且可能会出现访问冲突(比如正在下载的文件),效率也比较低
方法二、
范例:
[c-sharp] view plaincopyprint?
需要注意的是:filesize的单位是字节!
此种使用读取文件属性的方法得到文件的大小,效率较高,也较稳定
下面将stat的详细信息粘贴出来:
stat(取得文件状态)
相关函数 fstat,lstat,chmod,chown,readlink,utime
表头文件 #include
#include
定义函数 int stat(const char * file_name,struct stat *buf);
函数说明 stat()用来将参数file_name所指的文件状态,复制到参数buf所指的结构中。
下面是struct stat内各参数的说明
struct stat
{
dev_t st_dev; /*device*/
ino_t st_ino; /*inode*/
mode_t st_mode; /*protection*/
nlink_t st_nlink; /*number of hard links */
uid_t st_uid; /*user ID of owner*/
gid_t st_gid; /*group ID of owner*/
dev_t st_rdev; /*device type */
off_t st_size; /*total size, in bytes*/
unsigned long st_blksize; /*blocksize for filesystem I/O */
unsigned long st_blocks; /*number of blocks allocated*/
time_t st_atime; /* time of lastaccess*/
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last change */
};
st_dev 文件的设备编号
st_ino 文件的i-node
st_mode 文件的类型和存取的权限
st_nlink 连到该文件的硬连接数目,刚建立的文件值为1。
st_uid 文件所有者的用户识别码
st_gid 文件所有者的组识别码
st_rdev 若此文件为装置设备文件,则为其设备编号
st_size 文件大小,以字节计算
st_blksize 文件系统的I/O 缓冲区大小。
st_blcoks 占用文件区块的个数,每一区块大小为512 个字节。
st_atime 文件最近一次被存取或被执行的时间,一般只有在用mknod、utime、read、write与tructate时改变。
st_mtime 文件最后一次被修改的时间,一般只有在用mknod、utime和write时才会改变
st_ctime i-node最近一次被更改的时间,此参数会在文件所有者、组、权限被更改时更新先前所描述的st_mode 则定义了下列数种情况
S_IFMT 0170000 文件类型的位遮罩
S_IFSOCK 0140000 scoket
S_IFLNK 0120000 符号连接
S_IFREG 0100000 一般文件
S_IFBLK 0060000 区块装置
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符装置
S_IFIFO 0010000 先进先出
S_ISUID 04000 文件的(set user-id on execution)位
S_ISGID 02000 文件的(set group-id on execution)位
S_ISVTX 01000 文件的sticky位
S_IRUSR(S_IREAD) 00400 文件所有者具可读取权限
S_IWUSR(S_IWRITE)00200 文件所有者具可写入权限
S_IXUSR(S_IEXEC) 00100 文件所有者具可执行权限
S_IRGRP 00040 用户组具可读取权限
S_IWGRP 00020 用户组具可写入权限
S_IXGRP 00010 用户组具可执行权限
S_IROTH 00004 其他用户具可读取权限
S_IWOTH 00002 其他用户具可写入权限
S_IXOTH 00001 其他用户具可执行权限
上述的文件类型在POSIX 中定义了检查这些类型的宏定义
S_ISLNK (st_mode) 判断是否为符号连接
S_ISREG (st_mode) 是否为一般文件
S_ISDIR (st_mode)是否为目录
S_ISCHR (st_mode)是否为字符装置文件
S_ISBLK (s3e) 是否为先进先出
S_ISSOCK (st_mode) 是否为socket
若一目录具有sticky 位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名。
返回值 执行成功则返回0,失败返回-1,错误代码存于errno
错误代码 ENOENT 参数file_name指定的文件不存在
ENOTDIR 路径中的目录存在但却非真正的目录
ELOOP 欲打开的文件有过多符号连接问题,上限为16符号连接
EFAULT 参数buf为无效指针,指向无法存在的内存空间
EACCESS 存取文件时被拒绝
ENOMEM 核心内存不足
ENAMETOOLONG 参数file_name的路径名称太长
范例 #include
#include
mian()
{
struct stat buf;
stat (“/etc/passwd”,&buf);
printf(“/etc/passwd file size = %d /n”,buf.st_size);
}
执行 /etc/passwd file size = 705
PS:晚上的德天肥牛吃的我拉肚子怎么说……哭!