位运算:
优先级和结合顺序, 我到现在还没整明白,搞不定就上括号,最高是括号,最低的是逗号
b=2;c=3;
x=b>c;相当于x=0
x=b<c;相当于x=1
a=b++ + c++;
a结果为5
a=b++ + ++c;
a结果为6
a=++b + c++;
a结果为6
a=++b + c++;
a结果为7
短路运算符:&& 左边为假则为假 ||左边为真则为真,
复合运算符:+=,-=,/=,*=原来这样写是为了让编译器编译出来的程序运行效率更高一点,现在编译器已经很高级了,无论是i=i+1还是i+=1都是一样的
条件运算符(三目运算符):
a>b?c=1:c=2表示a>b则c=1,否则c=2
按二进制位进行的运算,在底层编程中经常会用到位运算
解析pe文件、内核编程、加密算法、驱动编程
按位与:将某个数清零或取某几位
按位或:将某一个数的某一位设为1
按位异或:两边一样就是0(抵消),特定位的反转,与0异或保留原值,交换两个数,可以用于加密,数据的备份和存储
a=b^c则a、b、c两两异或(比如你有两块4T的硬盘,你要备份就需要再准备两块4T的硬盘全部拷贝进去,但是如果你用异或,就只需要一块4T的硬盘,里面存放原来两块硬盘异或的结果,如果有其中一块硬盘坏了,则可以通过其他两块恢复)
左移<<:
右移>>:最高位
左移的结果是x2,右移无论是逻辑右移还是算数右移都是除以2
两个数位数不一样作位运算要先对齐
位段:
struct STU
{
char IsDll:1;
char Config:3;
char OldOep:4; (多出4位则保留)
int d:;
int size;
}xiaoming;
最好让位段占用的空间是你的类型的倍数
脚本(script):不需要编译的计算机语言,源代码写完了并改成指定的后缀名,一双击就能运行
批处理是更弱的脚本,一大堆dos命令的集合保存到文本文档里,把后缀名改为.bat一双击就会运行
宏(define):所谓的易语言就是用宏替代了相关的英文 只是对vc6.0简单修改让其支持unicode的宏定义,竟然被成为“中国人自己的语言”
汉芯造假事件(找摩托罗拉的芯片改了一下logo)、龙芯只会老老实实搞研究
一行代码,十个出错,考虑宏的问题
undef反宏定义:让宏定义失效
无参宏:可以嵌套
有参宏:替换之后才会发生错误
功能宏:#ifdef
比较大的公司对于自己的软件效率高会对库函数进行重写,为了兼容性其库的名称,函数的命名甚至参数都和c语言的库一模一样,只是里面实现的逻辑不一样
条件编译:
#include<iostream>
using namespace std;
#define MAX 100
#define MIN 0
#define MID 50
#ifndef MAX //如果没有定义MAX #ifdef 如果定义了宏
int cnt = 1;
#elif (MIN == 0) //相当于else if
int cnt = 2;
#elif MID == 50
int cnt = 3;
#endif
int main()
{
cout << "cnt = " << cnt << endl;
return 0;
}
这些个代码会执行吗?它会在编码器正式编译程序之前根据条件来决定这一段程序参与编译还是这一段程序参与编译,不参与的会被删掉
通过宏可以判断当前编译器以什么版本(x64、x86)以什么方式(debug、release)进行编译
#include<iostream>
using namespace std;
#if defined(_WIN32)
int main()
{
cout << "hello world" << endl;
return 0;
}
#else
int main()
{
cout << "HELLO WORLD" << endl;
return 0;
}
#endif
快速定位bug:攒自己的函数:void MyErrorInfo()
学完了SDK和MFC之后有心的记住:封装好自己的的函数,建立自己的代码库
C语言strerror()函数:返回错误原因的描述字符串
C语言perror()函数:打印最近一次系统错误信息
C语言ferror()函数:检查文件流是否有错误发生
但是程序发布的时候某些打印错误信息及log的代码需要删掉,这样就可以把发布时不需要的代码放到debug下,以debug方式编译时有提示日志及报错信息,而release则不会编译这些
函数:所有计算机语言的灵魂,函数的类型就是函数的返回值,局部变量是属性,参数也是属性
参数、局部变量都是属性,有什么区别?
函数指针
啥是函数?
函数的本质是啥?
(从技术角度来看为啥有信息安全问题(cpu没办法(没能力)区分数据和代码,缓冲区溢出是所有漏洞的根源,缓冲区是存数据的地,溢出来的数据当作代码执行了,web安全非常著名的攻击手段:sql注入,渗透里面经常用到xss(跨站脚本 ),留言本来是文本但是写了某些代码就会被执行,拖走库之后登录网站后台,webshell本地开3389端口,服务器搞定,再域控,整个内网拿下)?一个int、数组、函数有啥不一样的地方?在底层上没区别,只有在操作系统层有区别,如代码段、数据段、堆栈段,基于处理器————->以后做任何事的时候,只要符合处理器的游戏规则,这些个玩意都可以无视,以后做溢出的时候,想让哪是栈就是栈,哪有堆就有个堆)
就是一个数据结构,叫做栈帧
在cpu的世界,游戏规则非常开放
从操作系统层面来讲,一个函数之所以被称之为函数,有两个因数:
1.有一个栈帧去描述它
2.代码在操作系统的代码段里边,代码段的特点:可读可执行能不能写(操作系统所作的限制,内存属性一该,加壳写标志,就可以随便改了,函数提溜过来直接当数组用,数组直接拎过来当函数去用———非常神奇、不可思议的程序)
从计算机底层的角度函数、变量区分不严格
知道创宇副总,万物科技创始人:没能对用户的输入进行筛选
typedef int(*FUN)(int); *FUN函数指针
int main()
{
long long nMagic = 0xC30FC0830424448B;
int nNum = 0;
printf("%d",((FUN)&nMagic)(nNum)); 把nMagic当作函数去使用了
//(FUN)&nMagic函数的标识符,是一个整体
return 0;
}.
指针传参记住这个:
参数的本质就是函数的局部变量,不同的是它的初始值是实参
递归调用缺点:
大量的占用栈空间 (3环下边的栈基本就2M,0环也就是内核层下边的栈才200多kb,而这个栈和线程息息相关,线程是内核当中里边的一个基本管理单位,如果栈满了就蓝屏死机了)
平衡二叉树用循环写是一个非常复杂的过程,推荐用递归去写
一个栈帧描述一个函数,代码一样其实函数已经不一样了(多个函数共用一份代码而已,这就是递归)
内联函数:在函数前面加了一个关键词inline,普通函数是通过调用再执行然后return,内联函数
没有跳转到函数体再返回的过程,直接拷贝到代码中取,
缺点:大 优点:快
普通函数缺点:慢 优点:小
特别简洁的内联函数编译的时候才会触发内联
内联钩子:inline hook 本身简单,做到兼容性很难,不过这个技术被一个国外的大牛写出来了《rootkit灰色地带潜伏者》,好在大多数人都是二货,不知道这事,同学们可以记一下,对系统内核安全感兴趣的话
游戏实时汉化器
有一种这样神奇的木马:没文件、没进程、没线程、不开端口、不建立网络链接,然后你还能连上控制对方的电脑
依据目前计算机素养这样的程序是不存在的,因为不能没有线程,想与外界通讯,那么开端口、建立网络联接就是必须的
但是这木马真的存在,它用了inline hook技术的处理把这些个所有的行为都隐藏了,你在3环通过所有的工具去查看你都查不到,实际在内存里面这玩意有 inline hook在第二阶段讲,但是最牛叉的还是三环的inline hook,一度被称之为安全厂商的必杀器、核心竞争力,360后期为啥这么牛原因就是:mg0011给它开发出了一套非常稳定的基于内核层的inline hook的这一技术
最后,讲个函数收尾:
c语言的语法咋规定的?有且仅有一个main函数且程序总是从main函数开始执行
int Magic(),g_fun=Magic()
int main()
{
return 0;
}
int Magic()
{
printf("aaa");
system("pause");
return 0;
}
首先运行的是Magic函数
全局函数在定义时首先给它一个值,
#include<stdio.h>
void Magic()
{
int i, nNum[2] = { 0 };
for (int i=0;i<=2;i++)
{
nNum[i + i] = 1;
}
}
int main()
{
Magic();
printf("hello word");
unsigned int a= -2;
printf("%d", a);
}
结果:白板(死循环)
数组越界,数组下标为4的位置正好是int i所在的内存位置,i=2之后,nNum[4]将i改为1,陷入死循环
总结: 局部变量都是存在一起的,给你个数组,你算好了想改谁就该谁,而且不单是本函数内部的能改,
别人函数的也能改。
数组作为函数参数————-数组名作为实参 ,数组元素作为实参就和普通的变量一样
c语言传参方式:变量 指针 常量 c语言中所有符号名的本质都是地址,而且都是常量地址
数组名在内存中(保存数组名的)有没有地址?没有 传数组名就是把数组名所在的本身的地址传过去
数组名就是个常量