C和C++安全编码笔记:指针诡计

2023-11-01

指针诡计(pointer subterfuge)是通过修改指针值来利用程序漏洞的方法的统称

可以通过覆盖函数指针将程序的控制权转移到攻击者提供的外壳代码(shellcode)。当程序通过函数指针执行一个函数调用时,攻击者提供的代码将会取代原本希望执行的代码而得到执行。

对象指针也可以被修改,从而执行任意代码。如果一个对象指针用作后继赋值操作的目的地址,那么攻击者就可以通过控制该地址从而修改内存其它位置中的地址。

3.1 数据位置:

static int GLOBAL_INIT = 1; // 数据段,全局
static int global_uninit; // BSS段,全局
int test_secure_coding_3_1() // 栈,局部
{
	int local_init = 1; // 栈,局部
	int local_uninit; // 栈,局部
	static int local_static_init = 1; // 数据段,局部
	static int local_static_uninit; // BSS段,局部
	// buff_ptr的存储空间是栈,局部;分配的内存是堆,局部
	int* buff_ptr = (int*)malloc(32);
	free(buff_ptr);

	return 0;
}

UNIX可执行文件包含data段和BSS段。data段包含了所有已初始化的全局变量和常数。BSS段包含了所有未初始化的全局变量。将已初始化和未初始化变量分开是为了让汇编器不将未初始化的变量内容(BSS段)写入目标文件中。

3.2 函数指针:

void good_function(const char* str) {} // 栈
// 一个有漏洞的程序,其BSS段中的函数指针可以被覆写
void test_secure_coding_3_2(int argc, char* argv[]) // 栈
{
	const int BUFFSIZE = 10; // 栈
	static char buff[BUFFSIZE]; // BSS段
	static void(*funPtr)(const char* str); // BSS段
	funPtr = &good_function;
	// 当argv[1]的长度大于BUFFSIZE的时候,就会发生缓冲区溢出,这个缓冲区溢出漏洞
	// 可以被利用来将函数指针值覆写为外壳代码的地址,从而将程序的控制权转移到任意的代码
	// 当执行由funPtr标识的函数时,外壳代码将会取代good_function()得以执行
	strncpy(buff, argv[1], strlen(argv[1])); 
	(void)(*funPtr)(argv[2]);
}

虽然栈溢出(连同很多基于堆的攻击)不可能发生于数据段(data segment)中,但是覆写函数指针在任何内存段中都会发生。

3.3 对象指针:

// 一个有漏洞的程序,可以被利用来实现任意内存写,修改对象指针
void test_secure_coding_3_3(void* arg, size_t len)
{
	char buff[100];
	long val = 1;
	long* ptr = &val;
	// 一个无界内存复制,在溢出缓冲区后,攻击者可以覆写ptr和val
	// 当执行*ptr=val时,就会发生任意内存写
	memcpy(buff, arg, len);
	*ptr = val;
}

C和C++中的对象指针用于指向动态分配的结构、函数的引用参数、数组以及其它对象。这些对象指针可能会被攻击者修改,比如当利用一个缓冲区溢出漏洞的时候。如果一个指针接下来被用作一个赋值操作的目的地址,那么攻击者就可以通过控制地址达到修改其它内存位置内容的目的,这种技术也称为”任意内存写”(arbitrary memory write)。

3.4 修改指令指针:攻击者要想在x86-32架构上成功地执行任意代码,必须利用某种方式修改指令指针,使其指向外壳代码。指令指针寄存器(eip)存储了将要执行的下一条指令在当前代码段内的偏移量。eip寄存器不能被软件直接访问。它在顺序执行代码时由一个指令边界步进到下一条指令,也可以由控制转移指令(例如jmp、jcc、call和ret等)、中断以及异常间接修改。

以call指令为例,它首先将返回信息存储于栈中,然后将控制权转移到由目标操作数指定的被调用函数处。目标操作数指定了被调用函数中的第一条指令的地址。该操作数可以是一个立即数(immediate value)、一个通用寄存器或一个内存位置。

3.5 全局偏移表:Windows和Linux在库函数的链接和控制转移方面使用了类似的机制。从安全的角度来看,二者主要的区别在于Linux使用的方法是可被利用的,而Windows则不然。

Linux使用的默认二进制格式称为可执行和链接格式(Executable and Linking Format, ELF)。ELF最初由UNIX系统实验室(UNIX System Laboratories, USL)作为二进制应用程序接口(Application Binary Interface, ABI)的一个部分开发并发布。后来将ELF标准作为多种x86-32操作系统上的可移植目标文件格式。

任何ELF的二进制文件的进程空间中,都包含一个称为全局偏移表(Global Offset Table, GOT)的区。GOT存放绝对地址,从而使得地址可用,并且不会影响位置独立性和程序代码的可共享性。要使得动态链接的进程能够工作,这个表是必不可少的。该表的实际内容和形式取决于处理器的型号。

程序使用的每一个库函数在GOT中都拥有一个入口项,GOT中包含有实际函数的地址。这使得很容易在进程内存中对库函数进行重定位。在程序首次使用一个函数之前,该入口项包含有运行时链接器(RunTime Linker, RTL)的地址。如果该函数被程序调用,则程序的控制权被转移到RTL,然后函数的实际地址被确定且被插入到GOT中。接下来就可以通过GOT中的入口项直接调用函数,而跟RTL就无关了。

在ELF可执行文件中GOT入口项的地址是固定的。这就导致对任何可执行进程映像而言GOT入口项都位于相同的地址。可以利用objdump命令查看某一个函数的GOT入口项的位置,如下图所示:为每一个R_X86_64_JUMP_SLOT重定位记录指定的偏移量,包含了指定函数(或RTL链接函数)的地址。

攻击者可以利用任意内存写将一个函数的GOT入口项覆写为外壳代码的地址。这样,当程序调用对应于被改写的GOT入口项的函数时,程序的控制权就被转移到外壳代码。例如,每一个编写良好的C程序最后都会调用exit()函数,因此,只要覆写了exit()的GOT入口项,就可以在exit()被调用时将程序的控制权转移到指定的地址。ELF过程链接表(Procedure Linkage Table, PLT)具有类似的问题。

Windows PE(Portable Executable, 可移植的可执行)文件格式扮演着与ELF格式相似的角色。PE文件中包含一个数据结构数组,每一项对应一个导入的DLL。每一项都包含有导入的DLL的名称以及一个指向函数指针数组的指针(即导入地址表,Import Address Table, IAT)。每一个被导入的API在IAT中都有自己的保留槽,由Windows载入器为其填充导入函数的地址。一旦一个模块被载入,IAT就保存了需要调用的导入函数的地址。IAT的入口项是写保护的,因此它们在运行时无需修改。

3.6 .dtors区:

#ifndef _MSC_VER
static void create(void) __attribute__((constructor));
static void destroy(void) __attribute__((destructor));

static void create(void)
{
	fprintf(stdout, "create called.\n");
}

static void destroy(void)
{
	fprintf(stdout, "destructor called.\n");
}
#endif

void test_secure_coding_3_6()
{
#ifndef _MSC_VER
	fprintf(stdout, "create: %p.\n", create);
	fprintf(stdout, "destroy: %p.\n", destroy);
	exit(0);
#endif
}

任意内存写攻击的另外一个目标是覆写由GCC生成的可执行文件的.dtors区中的函数指针。GNU C允许程序员利用__attribute__关键字后跟一个包含于双括号中的属性修饰符来声明函数的属性。属性修饰符包括constructor和destructor。constructor属性指示函数在main()之前被调用,destructor属性则表示函数将在main()执行完成后或exit()被调用后进行调用。

构造函数和析构函数分布存储于生成的ELF可执行映像的.ctors和.dtors区中。.ctors和.dtors区映射到进程地址空间后,默认属性为可写。漏洞利用程序从未利用过构造函数,因为它们都在main()函数之前执行。结果,攻击者的兴趣都集中到了析构函数和.dtors区上。攻击者可以通过覆写.dtors区中的函数指针的地址从而将程序控制权转移到任意的代码。如果攻击者能够读取到目标二进制文件,那么通过分析ELF映像,很容易就能确定要覆写的确切位置。

注:在GCC高版本中好像用.init_array、.fini_array取代了.ctors、.dtors。如下图所示:

3.7 虚指针:在C++中可以定义虚函数(virtual function)。虚函数就是用virtual关键字声明的类成员函数。该函数可以由派生类中的同名函数重写。一个指向派生类对象的指针可以被赋给基类指针,并且通过该指针来调用函数。如果没有虚函数,则调用的是基类的函数,因为它和指针的静态类型相关联。当使用虚函数时,调用的则是派生类的函数,因为该函数和对象的动态类型相关联。

大多数C++编译器使用虚函数表(Virtual Function Table, VTBL)实现虚函数。VTBL是一个函数指针数组,用于在运行时派发虚函数调用。在每一个对象的头部,都包含一个指向VTBL的虚指针(Virtual Pointer, VPTR)。VTBL含有指向虚函数的每一个实现的指针。

覆写VTBL中的函数指针或者改变VPTR使其指向其它任意的VTBL都是可能的,可以通过任意内存写或者利用缓冲区溢出直接写入对象实现这一操作。通过对对象的VTBL和VPTR的覆写,攻击者可以使函数指针执行任意的代码。

3.8 atexit()和on_exit()函数:

char* glob;

void test(void)
{
	fprintf(stdout, "%s", glob);
}

int test_secure_coding_3_8()
{
	atexit(test);
	glob = "Exiting.\n";

	return 0;
}

atexit()是C标准定义的一个通用工具函数。atexit()可以注册无参函数,并在程序正常结束后调用该函数。C要求实现支持至少32个函数的注册。SunOS上的on_exit()函数具有类似的功能。libc4、libc5和glibc也提供了这样的函数。

atexit()通过向一个退出时将被调用的已有函数的数组中添加指定的函数完成工作。当exit()被调用时,数组中的每一个函数都以”后进先出”(Last-in, First-out, LIFO)的顺序被调用。由于atexit()和exit()都要访问该数组,因此它被分配为一个全局性的符号(在Linux操作系统中是__exit_funcs)。可以通过对__exit_funcs结构采用任意内存写或缓冲区溢出手段将程序的控制权转移到任意的代码。

3.9 longjump()函数:

int test_secure_coding_3_9()
{
	jmp_buf env;
	int val;

	val = setjmp(env);

	fprintf(stdout, "val is %d\n", val);

	if (!val) longjmp(env, 1);

	return 0;
}

C标准定义了setjmp()宏、longjmp()函数,以及jmp_buf类型,它们可以用来绕过正常的函数调用和返回规则。

setjump()宏为稍后将会调用的longjmp()函数保存其调用环境。longjmp()则恢复最后一次由setjmp()宏保存的调用环境。可以通过将jmp_buf缓冲区中PC(Program Counter, 程序计数器)的值覆写为外壳代码的起始地址的方法来利用longjmp()函数。任意内存写或者直接针对jmp_buf结构的缓冲区溢出都能达到这个目的。

3.10 异常处理:

int test_secure_coding_3_10()
{
	try {
		//throw 10;
		throw "overflow";
	}
	catch(int x) {
		fprintf(stderr, "exception value: %d\n", x);
	}
	catch (const char* str) {
		fprintf(stderr, "exception value: %s\n", str);
	}

	return 0;
}

异常就是函数操作中发生的意外情况。例如,被除0将会产生一个异常。很多程序员采取实现异常处理程序的方式来处理这些特殊情况,以避免非预期的程序中止。另外,异常处理程序被串在一起并以一定的顺序被调用,直到其中一个能够处理异常为止。

Microsoft Windows操作系统提供了三种形式的异常处理程序。操作系统按给定的顺序调用它们,直到其中某一个被成功执行:(1).向量化异常处理(Vectored Exception Handling, VEH):首先调用以重写结构化异常处理程序。(2).结构化异常处理(Structured Exception Handling, SEH):这种方式被实现为每函数(per-function)或每线程(per-thread)的异常处理程序,即每一个函数或每一个线程都有自己的异常处理程序。(3).系统默认异常处理:这是一个全局异常过滤器和处理器,用于处理整个进程的异常情况。如果上面两个异常处理程序都无法处理异常,那么它将会被调用。

结构化异常处理:SHE通常在编译器级别通过try…catch语句实现。try块中引发的任何异常都将被匹配的catch块处理。如果catch块无法处理异常,那么它将被传回之前的范围块。__finally关键字是微软对C/C++语言的扩展,用于表示一个代码块,该代码块被调用来清理由try块说明的任何东西。不管try块如何退出,该关键字都被调用。

对结构化异常处理而言,Windows为每线程的异常处理程序提供了特殊支持。编译器产生的代码将一个指向EXCEPTION_REGISTRATION结构的指针的地址,写入fs段寄存器所引用的地址。因为异常处理程序地址紧跟在局部变量之后,因此,如果一个栈变量发生缓冲区溢出,那么异常处理程序地址就可以被覆写为任意值。除了覆写单独的函数指针外,还可以替换线程环境块(Thread Environment Block, TEB)中的指针,已注册的异常处理程序的列表就是由该指针所引用的。

系统默认异常处理:未处理异常过滤器函数利用SetUnhandledExceptionFilter()函数进行设置。该函数作为进程的最后一级异常处理程序而被调用。然而,如果攻击者利用任意内存写技术覆写了某特定内存地址,则未处理异常过滤器可以被重定向去执行任意代码。

3.11 缓解策略:防止指针诡计的最佳方式就是消除允许内存被不正确地覆写”的漏洞。覆写对象指针、常见的动态内存管理错误、字符串格式化漏洞都可能导致指针诡计的发生。消除这些漏洞来源是消除指针诡计的最佳方式。

栈探测仪:仅对那些预通过溢出栈缓冲区来覆写栈指针或者其它受保护区域的漏洞利用有效。栈探测仪并不能防止对变量、对象指针或者函数指针进行修改的漏洞利用。栈探测仪不能阻止包括栈段在内的任何位置发生缓冲区溢出。

W^X:此策略意思是说一段内存区域要么可写要么可执行,但不可同时两者兼备。这种策略不能防止类似于atexit()这样的同时需要运行时写入和可执行的目标覆写。

对函数指针编码和解码:程序可以存储一个指针的加密版本,而不是存储该指针。攻击者需要破解加密的指针才能重定向到其它代码。提议在C11标准中加入encode_pointer()和decode_pointer()函数,后未被采纳。这两个函数与Microsoft Windows的两个函数(EncodePointer()和DecodePointer())的目的类似,但细节略有不同,后者被Visual C++的C运行时库所使用。

3.12 小结:就像栈溢出攻击被用于覆写返回地址一样,缓冲区溢出可被用于覆写对象指针或函数指针。覆写函数指针或对象指针的能力取决于缓冲区溢出发生的地址和目标指针之间距离的远近,不过一般而言,同一个内存段内都存在这样的机会。

攻击函数指针使得攻击者能够直接将程序的控制权转移到由其提供的任意代码。对对象指针进行修改并赋值的能力创建了任意内存写技术。不管环境如何,任意内存写技术都有很多机会将程序的控制权转移给任意的用于任意内存写技术的代码。其中一些目标是C标准特性的结果,另外一些则特定于编译器或操作系统。

GitHubhttps://github.com/fengbingchun/Messy_Test

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C和C++安全编码笔记:指针诡计 的相关文章

  • C++11中std::condition_variable的使用

  • C++/C++11中头文件algorithm的使用

  • C++11中模板类std::enable_shared_from_this的使用

    C 11中的模板类template
  • C和C++安全编码笔记:动态内存管理

    4 1 C内存管理 C标准内存管理函数 1 malloc size t size 分配size个字节 并返回一个指向分配的内存的指针 分配的内存未被初始化为一个已知值 2 aligned alloc size t alignment siz
  • 程序员的自我修养--链接、装载与库笔记:可执行文件的装载与进程

    可执行文件只有装载到内存以后才能被CPU执行 1 进程虚拟地址空间 程序和进程有什么区别 程序 或者狭义上讲可执行文件 是一个静态的概念 它就是一些预先编译好的指令和数据集合的一个文件 进程则是一个动态的概念 它是程序运行时的一个过程 很多
  • 概率论中高斯分布(正态分布)介绍及C++11中std::normal_distribution的使用

    高斯分布 最常用的分布是正态分布 normal distribution 也称为高斯分布 Gaussian distribution 正态分布N x 2 呈现经典的 钟形曲线 的形状 其中中心峰的x坐标由 给出 峰的宽度受 控制 正态分布由
  • C++/C++11中引用的使用

    引用 reference 是一种复合类型 compound type 引用为对象起了另外一个名字 引用类型引用 refer to 另外一种类型 通过将声明符写成 d的形式来定义引用类型 其中d是声明的变量名 一 一般引用 一般在初始化变量时
  • C和C++安全编码笔记:文件I/O

    C和C 程序通常会对文件进行读写 并将此作为它们正常操作的一部分 不计其数的漏洞正是由这些程序与文件系统 其操作由底层操作系统定义 交互方式的不规则性而产生的 这些漏洞最常由文件的识别问题 特权管理不善 以及竞争条件导致 8 1 文件I O
  • Effective C++改善程序与设计的55个具体做法笔记

    Scott Meyers大师Effective三部曲 Effective C More Effective C Effective STL 这三本书出版已很多年 后来又出版了Effective Modern C More Effective
  • 提高C++性能的编程技术笔记:引用计数+测试代码

    引用计数 reference counting 基本思想是将销毁对象的职责从客户端代码转移到对象本身 对象跟踪记录自身当前被引用的数目 在引用计数达到零时自行销毁 换句话说 对象不再被使用时自行销毁 引用计数和执行速度之间的关系是与上下文紧
  • 程序员的自我修养--链接、装载与库笔记:Linux共享库的组织

    共享库 Shared Library 概念 其实从文件结构上来讲 共享库和共享对象没什么区别 Linux下的共享库就是普通的ELF共享对象 由于共享对象可以被各个程序之间共享 所以它也就成为了库的很好的存在形式 很多库的开发者都以共享对象的
  • 内存检测工具Dr. Memory的使用

    Dr Memory是一个内存调试工具 它是一个开源免费的内存检测工具 它能够及时发现内存相关的编程错误 比如未初始化访问 内存非法访问 数组越界读 写 以及内存泄露等 它可以在Linux Windows Mac OS和Android操作系统
  • C++中的封装、继承、多态

    封装 encapsulation 就是将抽象得到的数据和行为 或功能 相结合 形成一个有机的整体 也就是将数据与操作数据的源代码进行有机的结合 形成 类 其中数据和函数都是类的成员 封装的目的是增强安全性和简化编程 使用者不必了解具体的实现
  • C++中typeid的使用

    RTTI Run TimeType Information 运行时类型信息 它提供了运行时确定对象类型的方法 在C 中 为了支持RTTI提供了两个操作符 dynamic cast和typeid The typeid operator pro
  • C++中nothrow的介绍及使用

    在C中 使用malloc等分配内存的函数时 一定要检查其返回值是否为 空指针 并以此作为检查内存操作是否成功的依据 这种Test for NULL代码形式是一种良好的编程习惯 也是编写可靠程序所必需的 在C 中new在申请内存失败时默认会抛
  • C++11中std::shared_future的使用

    C 11中的std shared future是个模板类 与std future类似 std shared future提供了一种访问异步操作结果的机制 不同于std future std shared future允许多个线程等待同一个共
  • 开源库jemalloc简介

    jemalloc是通用的malloc 3 实现 它强调避免碎片和可扩展的并发支持 它的源码位于https github com jemalloc jemalloc 最新稳定版本为5 2 1 glibc的内存分配算法是基于dlmalloc实现
  • C++中std::sort/std::stable_sort/std::partial_sort的区别及使用

    某些算法会重排容器中元素的顺序 如std sort 调用sort会重排输入序列中的元素 使之有序 它默认是利用元素类型的 lt 运算符来实现排序的 也可以重载sort的默认排序 即通过sort的第三个参数 此参数是一个谓词 predicat
  • C++/C++11中变长参数的使用

    C C 11中的变长参数可以应用在宏 函数 模板中 1 宏 在C99标准中 程序员可以使用变长参数的宏定义 变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号 而预定义宏 VA ARGS 则可以在宏定义的实现部分替换省略号所代表的
  • 提高C++性能的编程技术笔记:单线程内存池+测试代码

    频繁地分配和回收内存会严重地降低程序的性能 性能降低的原因在于默认的内存管理是通用的 应用程序可能会以某种特定的方式使用内存 并且为不需要的功能付出性能上的代价 通过开发专用的内存管理器可以解决这个问题 对专用内存管理器的设计可以从多个角度

随机推荐

  • php mysql utf 8_PHP+MySQL中对UTF-8,UTF8(utf8),set names gbk 的理解

    问题一 在我们进行数据库操作时会发现 数据库中表的编码用的是utf 8 但是在进行dos命令是要使用set names gbk 一 Mysql中默认字符集设置有四级 服务器级 数据库级 表级 和字段级 前三种都是默认设置 并不代表你的字段最
  • mysql删除表数据 MySQL清空表内容 3种命令方法及比较

    一 MySQL清空表数据命令 truncate SQL语法 truncate table 表名 注意 不能与where一起使用 truncate删除数据后是不可以rollback的 truncate删除数据后会重置Identity 标识列
  • Spring Boot 学习系列(09)—自定义Bean的顺序加载

    此文已由作者易国强授权网易云社区发布 欢迎访问网易云社区 了解更多网易技术产品运营经验 Bean 的顺序加载 有些场景中 我们希望编写的Bean能够按照指定的顺序进行加载 比如 有UserServiceBean和OrderServiceBe
  • 薪酬问题手册

    薪酬问题手册 所有离职人员的缺勤扣款都不对 考勤报表 自由制的考勤日历工作时长要显示实际工作时长 考勤报表 自由制 计算月工作时长 解决在接受页面数据CompAtteMonth对象时的absenteeismTime 调整后的缺勤时长 问题
  • GiftWrapping算法求最小凸包的简单实现

    目录 前言 问题简介 基本知识 算法简介 算法简单实现过程 源代码 结语 前言 本篇文章是基于哈工大软件构造的实验一写出的 源代码也只是TurtleSoup类中一个方法 虽然不能直接使用 但其思想还是有一定的参考价值 问题简介 一组平面上的
  • Intel lock前缀指令的屏障能力

    Intel lock前缀指令除了单操作原子性的能力之外 还具备可见性和有序性 对于Intel lock前缀指令的单操作原子性和可见性 参见下面两个链接 其实本质就是锁总线或锁缓存 加上缓存一致性协议 Intel LOCK前缀指令https
  • wsl ubuntu18.04LTS 网络连接设置

    修改 etc resolv conf可以自己设置 dns 但重启 WSL 以后 手动设置的 DNS 就会被重置为默认的 细心看了一下默认的文件以后发现了问题的关键 WSL 自动在启动时自动根据系统的虚拟交换机WSL生成 etc resolv
  • 一位计算机准PhD的大四和博零

    最新个人信息可见 Home Zhuoning Guo 完整版请见 知乎 攻读PhD 大一开始有读博念头 大二计划去香港 理由 学制短 奖学金高 环境 导师和同学 容易适应 海外麻烦也申不到特别好的 如美帝Top10 牛剑 大三基于校内实验室
  • k8s1.26.6 安装gitlab

    Gitlab官方提供了 Helm 的方式在 Kubernetes 集群中来快速安装 但是在使用的过程中发现 Helm 提供的 Chart 包中有很多其他额外的配置 所以我们这里使用自定义的方式来安装 也就是自己来定义一些资源清单文件 Git
  • SimpleDateFormat多线程下的异常

    今天在生产上碰到一个怪异的问题 之前一直跑的很好的xml转object程序 在日期转化的过程中报错的 经过排查原因 原来是由于SimpleDateFormat在多线程下运行造成的结果 demo例子如下 import java text Pa
  • Ubuntu环境下安装ffmpeg

    1 创建安装 录 sudo mkdir p usr local ffmpeg lib 2 下载ffmpeg源码 Download FFmpeg 3 解压源文件 4 到指定ffmpeg目录进行配置 cd ffmpeg 4 3 2 配置 con
  • Mybatis——增删改查的实现

    注意 增删改时一定要提交事务 代码 提交事务 sqlSession commit 1 namespace 命名空间 namespace中的全限定名 包名 类名 要和Dao Mapper接口的全限定名 包名 类名 一致 2 select 选择
  • 灌电流和拉电流简介

    灌电流 sink current 对一个端口而言 如果电流方向是向其内部流动的则是 灌电流 比如一个IO通过一个电阻和一个LED连接至VCC 当该IO输出为逻辑0时能不能点亮LED 去查该器件手册中sink current参数 拉电流 so
  • Flask后端部署到云服务器

    1 本地写好代码 2 码云创建仓库 上传本地代码到创库 git init git remote add origin https gitee com 自己的仓库 git git pull origin master git add git
  • 1 在 Linux 下开机自动重启脚本(亲测)

    etc rc local 开机启动程序 把需要开机自动运行的程序写在这个脚本里 etc init d 这个目录存放的是一些脚本 一般是linux以rpm包安装时设定的一些服务的启动脚本 要重新启动 sendmail 的话 而且你的 send
  • Tensorflow的Bazel编程(二)

    转自 http blog csdn net langb2014 article details 54312697 安装官网 https bazel build versions master docs tutorial Java html
  • springboot整合fisco

    Spring Boot连接Fisco Bcos区块链 使用spring boot连接Fisco Bcos 在Fisco Bcos的官方提供了Java Sdk工具用于连接 Java SDK 提供了访问 FISCO BCOS 节点的Java A
  • 飞机降落(dfs+贪心思想)

    飞机降落 dfs 贪心思想 原题链接 4957 飞机降落 AcWing题库 思路分析 通过读题易知 题目可以翻译为 已知有 n 条线段 每条线段都可以在一定的区域内滑动 需要我们来判断是否可以找到一种线段的分布方案 使得每条线段都不相交 首
  • stl::(8)set容器API

    set根据元素键值自动被排序 迭代器不能修改键值 键值唯一不重复 set 构造函数 set
  • C和C++安全编码笔记:指针诡计

    指针诡计 pointer subterfuge 是通过修改指针值来利用程序漏洞的方法的统称 可以通过覆盖函数指针将程序的控制权转移到攻击者提供的外壳代码 shellcode 当程序通过函数指针执行一个函数调用时 攻击者提供的代码将会取代原本