TLS回调函数
Thread Local Storage(线程局部存储)回调函数常用于反调试。
所谓TLS回调函数是指,每当创建/终止进程的线程时会自动调用执行的函数。有意思的是,创建进程的主线程时,也会自动调用回调函数,且其调用执行先于EP代码。反调试技术利用的就是TLS回调函数的这一特征。
请注意,创建或终止某线程时,TLS回调函数都会自动调用执行,前后共2次(原意即为此)。
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
简单来说就是,把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫回调
如果代码立即被执行就成为同步回调,如果过后再执行,就称之为异步回调。
回调函数例子
1 | int Callback_1(int a) ///< 回调函数1 |
反调试
为了防止程序被调试,保护代码的加固手段
EP代码
EP代码是指程序的入口点,是Windows可执行文件(如EXE、DLL、SYS等)中最先被执行的代码的位置
在Windows操作系统中,每个可执行文件都有一个指定的入口点,它是程序启动时CPU开始执行代码的具体地址。EP代码通常由链接器在编译和链接过程中自动生成,并存储在PE文件的头信息中
程序调试:在软件开发和调试过程中,开发者可以通过设置断点在EP处来开始调试程序的执行流程
加壳与脱壳:在软件保护领域,加壳技术会修改程序的原始入口点(OEP),以隐藏程序真实的执行起始位置。脱壳则是还原或找到程序的真正入口点,以便进行逆向工程或破解
memset
这是一个初始化函数,作用是将某一块内存中的全部设置为指定的值(它是以字节为单位进行赋值的)
1 | void *memset(void *s,int c,size_t n); |
- s指向要填充的内存块
- c是要被设置的值
- n是要被设置该值的字符数
- 返回类型是一个指向存储区s的指针
strncpy&strcpy
***strcpy***(char *dest,const char*src):该函数用来复制字符串
strncpy(char*dest,const char*src,n):该函数用来复制字符串的前n个字符
(注:1、dest 表示字符串起始地址。
2、当src字符串长度小于n时,则拷贝完字符串后,剩余部分将用空字节填充,直到n个。src和dest所指的内存区域不能重叠,且dest必须有足够的空间放置n个字符。
3、不同于strcpy,strncpy不会向dest追加结束标记‘\n’)
strcmp&strncmp
***strcmp***(const char *string1,const char *string2):进行字符串比较,比较字符串大小的函数。返回类型是int,当前者大于后者返回1,前者小于后者返回-1。
(strcmp本质上比较的是第一位不同位的ASCII码值大小。例如字符串A,B,若所有对应的字符都相同,则返回值为0,若第一位不同位的ASCII码值,A大于B,无论B后面有多长,都返回1,反之返回-1。
if ( !strncmp(Str1, Str2, v5) )
strncmp(str1,str2,v5)是把str1与str2两字符串的前v5位数做一个比较,==若str1=str2,返回0==
***strncmp***(const char *string1,const char string2,length):strncmp较strcmp多了一个length(即参数n)。strncmp是用来比较两个字符串前n个字节的大小。
text = (char *)join(key3, v9)
这句话是一个C语言中的字符串操作,意思是将变量key3和变量v9连接起来,然后将结果赋给一个新的字符串指针。在C语言中,字符串操作通常使用char*类型的指针来表示字符串。因此,这句话的意思就是将key3和v9链接成一个新的字符串。
strcat
这是一个字符串连接函数,用于==字符串追加==
1 | strcat(char *dest,const char *src) |
- dest—指向目标数组,该数组包含了一个C字符串,且足够容纳追加后的字符串
- src—指向要追加的字符串,该字符串不会覆盖目标字符串
GetStdHandle
GetStdHandle是一个Windows API函数。它用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄。
1 | GetStdHandle( _In_ DWORD nStdHandle);//包含在头文件windows.h中 |
GetStdHandle函数的参数可以是下列之一
参数 | 含义 |
---|---|
STD_INPUT_HANDLE | 标准输入句柄 |
STD_OUTPUT_HANDLE | 标准输出句柄 |
STD_ERROR_HANDLE | 标准错误句柄 |
注:
1、只有这三种参数。不同的参数用于获取不同标准设备的句柄。
2、GetStdHandle函数是用来获取句柄的,那它的返回值当然是个句柄
sprintf
由于 sprintf 跟 printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。
1 | int sprintf( char *buffer, const char *format [, argument] … ); |
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:格式化字符串上。
1、格式化数字字符串
1 | sprintf(s,“%d”,123); |
2、控制浮点数打印格式
1 | sprintf(s,"%f",3.1415926); |
浮点数使用“ %f ”控制,默认保留小数点后6位数字
3、连接多个字符串
1 | who = 'I' |
4、打印地址信息(查看某些变量或者成员的地址)
1 | sprintf(s,“%p”,&i); |
MessageBoxA函数
- MessageBoxA
是一个函数,用于在Windows系统中显示一个消息框。
1 | MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0); |
hDlg
是对话框的句柄,通常由 CreateDialog
或 DialogBoxParam
等函数返回。
- "U g3t 1T!"
是要在消息框中显示的文本内容。
- "@_@"
是消息框的标题。
- 0
是消息框的类型,其中 0
表示一个标准的模态消息框。
所以,这段代码的意思是:在一个模态的消息框中显示文本 “U g3t 1T!”,并将该消息框的标题设置为 “@_@”。
malloc(动态内存分配)
用于申请一块连续的指定大小的内存块区域,以void类型返回分配的内存区域地址。当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存,且分配的大小就是程序要求的大小
memcpy
memcpy是内存拷贝函数,它可以实现指定内存大小的拷贝。
1 | void * memcpy(void * destination,const void* sourcr,size_t num) |
库函数memcpy在头文件string.h中被声明。
它有三个参数:
第一个参数类型是void,用来接收目的地的首地址;
第二个参数类型是const void,用来接收源头内容的首地址(拷贝时源头的内容不会被改变,用const修饰会更安全);
第三个参数类型是size_t(无符号整型),表示从源头地址开始向后需要拷贝的字节数。
在之前使用qsort函数时我们了解到:void可以接收任意类型的指针变量,指向1个字节的空间。由于这个库函数要能够拷贝任意类型的数据,所以这里使用了void来接受指针变量(上一篇介绍的字符串函数的参数是char*型的,所以只能对字符进行拷贝)
结合第三个参数,就可以实现将从源头首元素地址开始的内存中num个字节内的数据拷贝到目的地首元素地址开始的num个字节的空间中。由于数据都是以二进制的形式存储在内存中的,最小存储单元就是1个字节,所以只要能够将某一内存空间的每一个字节拷贝到另一空间。这样,就可以实现任意数据类型的拷贝。
返回值是void*型的,用来返回目的地的首元素地址。
需要注意的是,memcpy函数在处理从源头地址开始的num个字节的内存空间与从目的地地址开始的num个字节的内存空间有重叠的情况时,可能会出现一些问题导致不能实现正确的拷贝。建议使用memmove函数来处理
fopen
1 | FILE *fopen(const char *filename, const char *mode);` |
- filename: 要打开的文件名字符串
- mode: 访问文件的模式, 它包括:
模式 | 描述 | 文件可否存在 |
---|---|---|
-r | 打开文件仅供读取 | 必须存在 |
-w | 创建新文件仅供写入 | 若存在,则清空后再写入 |
-a | 打开文件附加写入 | 若不存在,则创建新文件写入 |
-r+ | 打开文件供读取并写入 | 必须存在 |
-w+ | 创建新文件供读取并写入 | 若存在,则清空后再写入 |
-a+ | 打开文件读取并附加写入 | 若不存在,则创建新文件写入 |
fclose
关闭当前文件流,此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源
atoi
将输入的字符串转为数字
byte和hibyte
LOWORD( )得到一个32bit数的低16bit
HIWORD( )得到一个32bit数的高16bit
LOBYTE( )得到一个16bit数最低(最右边)那个字节
HIBYTE( )得到一个16bit数最高(最左边)那个字节
exit()函数
用来终止进程
exit(0) 表示程序正常退出;除了0之外,其他参数均代表程序异常退出,如:exit(1),exit(-1)。
exit(1) 和exit(-1) 是分别返回1和-1到主调程序。exit(0)则是返回0。exit(0)表示程序正常退出,非0表示非正常退出。
mpz_init_set_str
int mpz_init_set_str (mpz t rop, const char *str, int base)
//rop = str(转为base进制数)
mpz_powm
void mpz_powm (mpz t rop, const mpz t base, const mpz t exp, const mpz t [Function] mod)
//rop = base^exp%mod
注:看到这个函数要联想到rsa加密