3. 体验黑客

一、前言

从本文开始,我们就开始进入实战,让你切实体会到学习C/C++的乐趣所在。

不知道大家有没有看过关于黑客题材的电影,如果没有,可以给大家推荐一部电影,名为:《我是谁:没有绝对安全的系统》

这部电影有多经典呢?你现在在网上所能搜索到的很大部分黑客图片都来自这部电影!

里面主角就是一位电脑天才,同时也是一名黑客大佬,拿起电脑,几行代码一敲,电脑屏幕字符闪动,就入侵成功了

现实中这可能吗?虽然很难,但确实是可能的。

本文将带你了解那屏幕字符飞速滚动的原理,以及为什么能够只敲几行代码,就能入侵系统。

二、项目准备

还是老规矩,在原解决方案中,新建一个空的控制台项目,名为day3,并设置为启动项目。

具体步骤在第二章,如果忘了,请回头看一下,然后给新项目添加一个源文件main.cpp

image-20231127071056055

三、码代码

main.cpp文件中添加上基本程序结构:

#include<iostream>
int main() {

}

在第二章讲函数的时候我提到过,main本身也是一个函数,上面这种属于其中的无参数写法。

但其实它还有一种带参数的写法:

#include<iostream>
int main(int argc,char** argv) {

}

当然,argcargv这两个名字只是变量名,是可以自己随便改的,真正重要的是这两个参数的类型。

接下来就说说这两个参数的含义:

  • argc:外部传进来的参数个数
  • argv:外部传入的参数

当你看到argv这个参数类型的时候可能有点懵,char*我知道,就指针嘛,存地址的,那char **是什么鬼?

这时候我们就要把char*看成一个整体了,前面说过,char* 在C/C++中一般用来表示一个字符串。

那在字符串类型后面再加一个*是什么意思?那自然就是字符串指针了!

稍稍理解一下就是,一块连续的内存,每块内存都存放的地址,而每个地址指向的位置都是一个字符串的首地址。

char*一样,char**也是可以看作各个字符串连着的,通过地址递增就可以访问下一个。

听上去是不是和数组很像?

前面说过,其实数组的变量名本身就是指针,更加方便的方式是用下标来访问,访问方法:

argv[0]; //访问第一个字符串,等价于*(argv+0)
argv[1]; //访问第二个字符串,等价于*(argv+1)

//类比单个字符串 char* c="yushi-"; c[0]='y',c[1]='s',等价于*(c+0)='y',*(c+1)='s'

//共有argc个字符串

为了切实体验到这两个参数的作用,我们可以用for循环将它们打印出来:

#include<iostream>
int main(int argc,char** argv) {
	std::cout << "一共有" << argc << "个参数\n";

	for (int i = 0; i < argc; i++)
	{
		std::cout << "第" << i + 1 << "个参数为:" << argv[i] << "\n";
	}
}

结果为:

image-20231127071450169

可以看到,默认情况下,好像就只有一个参数,而且这个参数应该是个路径,进入这个目录看看?

image-20231127071541085

看这名字,不就是我们刚建立的项目名嘛,后缀为.exe,说明应该是可以执行的,点击试试?

是不是发现有什么东西一闪而过了?多点几次,你应该能大概看出来,那就是个黑窗口。

那么能不能让它别这么快消失呢?

办法肯定是有的,前面提到过,main函数是系统调用的地方,从后面的{}开始。看到一句就执行一句,执行完了怎么办?那当然就退出了。

所以我们让它别执行完就行了,改为以下代码:

#include<iostream>
int main(int argc,char** argv) {
	std::cout << "一共有" << argc << "个参数\n";

	for (int i = 0; i < argc; i++)
	{
		std::cout << "第" << i + 1 << "个参数为:" << argv[i] << "\n";
	}

	while (true) {

	}
}

while循环的时候说过,当后面判断语句为真就会循环,这里直接设置为true,就会陷入死循环,这样main函数就不会退出了!

先在VS中点击运行一下试试:

image-20231127071647440

如果运行结束了,VS一般都会打印一些东西,而这次除了我们写的输出代码外,就根本没有VS的输出,这说明我们的程序还在运行着。

关掉,重新来到刚才的文件夹中点击day3.exe文件试试!

image-20231127071716892

现在是不是就可以了!那么接下来如何添加我们自定义的参数呢?

答案就是我们先打开控制台,用控制台运行它,直接在当前文件夹中,输入cmd,按Enter

image-20231127071945896

就从控制台进入该文件夹了,可什么是控制台呢?

简单来说,控制台就是一种只能显示字符的平台,它可以让你通过敲入命令来操作你的电脑,这和你用鼠标操作电脑是一样的,可以参考这篇文章进一步学习:程序员必懂常识

而之所以输入的是cmd而不是其它,是因为cmd是控制台程序的名称:

image-20231127072136192

前面显示的就是你当前所在的文件夹,然后输入命令dir查看当前目录有哪些文件:

image-20231127072224085

可以看到,和从文件管理器中看到的是一样的,其中day3.exe就在其中,然后我们输入day3.exe试一试:

image-20231127072255145

看,是不是运行了!只是路径发送了一点变化而已!

但由于我们写的是死循环,程序自己停不下来,这时候就可以按Ctrl+C强制它停止:

image-20231127072325464

然后我们就可以输入参数了!

day3.exe https://www.kucoding.com C++实战入门到精通

结果为:

image-20231127072433689

就可以看到,程序将我们输入在.exe文件的后面字符串,当作参数传给了我们的软件,而且这些参数是以空格分割的!

这样我们就可以在程序中得到这些参数,然后我们根据这些参数执行对应的动作就行了!改代码为如下内容:

#include<iostream>
int main(int argc,char** argv) {
	if (argc == 1) { //没有输入参数
		std::cout << "请输入参数!";
	}
	else if (argc == 2) { //只输入了一个参数
		std::cout << "参数不足!";
	}
	else if (argc == 3) { //输入两个参数
		if (strcmp(argv[1], "attack") == 0) { //比较我们输入的第一个参数是否和attack字符串相同,相同则该函数返回0
			for (int i = 0; i < 5; i++) {				//循环输出5次话
				std::cout << "Attacking " << argv[2] << " ";  //输出一个字符串

				for (int j = 0; j <= i; j++) {  //在上面的字符串后面打印i+1个点
					std::cout << ".";
				}
				std::cout << "\n"; //换行


				int num = 10000000;  //让程序卡在这里一会
				while (num) {
					num--;
				};

			}

			std::cout << "Successful attack!\n"; //输出结束字符串
		}
		else { //输入的第一个参数不是attack
			std::cout << "不存在该命令\n";
		}
	}
	else { //输出超过两个参数
		std::cout << "参数过多!";
	}
}

由于我们当前过于基础,很多东西都还没有讲到,所以暂时也只能这样过一过瘾。

上面的代码我写了很详细的注释,但考虑到结构还是比较复杂,各种嵌套,所以我还是给大家理一理。

首先看最外面的结构,就是一个多次判断的if语句,用来判断我们输入了几个参数:

	if (argc == 1) {
		std::cout << "请输入参数!"; //没有输出参数
	}
	else if (argc == 2) { //只输入了一个参数
		std::cout << "参数不足!";
	}
	else if (argc == 3) { //输入两个参数
	}
	else { //输出超过两个参数
		std::cout << "参数过多!";
	}

注意:程序自己有默认的一个参数,上面提到过,所以只有将argc-1,才是我们输入的参数个数。

当没有输入参数,或者只输入一个参数时,进行提示,输出字符串。

当我们输入的参数个数超过2个时(也就是argc大于3),同样输出字符串进行提示。

只有输入的参数为2时,我们才开始工作:

if (strcmp(argv[1], "attack") == 0) { //比较我们输入的第一个参数是否和attack字符串相同,相同则该函数返回0

} 
else { //输入的第一个参数不是attack
	std::cout << "不存在该命令\n";
}

这里调用了一个函数,strcmpstr(string)表示字符串,cmp(compare)表示比较,结合起来的意思是:字符串之间的比较

一般来说,函数名字要写成该函数要实现功能的英文简写,和这个函数一样,方便我们见名知意!

这里给大家说一下,可能你早就发现了,当你在VS里面敲代码时,旁边总会弹出来一个列表:

image-20231127072756441

其实这就是VS这种IDE高级的地方,能够帮助你联想你想输入的东西,当你输入函数名的前几个字符时,所有符合的函数都会给你列出来。

你就可以用上下箭头按键,来选择你需要的函数,然后按Enter键,就会帮你补全了,是不是特别方便!

但你很快会发现一个弊端,就是如果我鼠标点击了别处,再点击回来,提示就消失了,而且就算你接着敲也不出提示。

这时候你就只能把刚才的内容删除掉,然后重新敲才会出提示,就会非常不便!

作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux