符号执行入坑指南

2024-01-22 21:48
文章标签 指南 入坑 符号执行

本文主要是介绍符号执行入坑指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

0x10 符号执行的定义

符号执行是软件测试行业新型的一个方向,当下较为火热,也给安全测试提供了新的思路,衍生出了诸多安全测试工具。维基百科中定义的符号执行如下

在计算机科学中,符号执行技术指的是通过程序分析的方法,确定哪些输入向量会对应导致程序的执行结果向量的方法。

抛开专业术语的表层,拨云见日,符号执行就是使用符号值代替真实值执行。在计算机程序中,尤其是C语言,符号是指函数名和变量名,在这里,更多的是指变量。符号执行就是说,不给定函数变量(比如入参)特定的值,而是用变量本身来替代,达到模拟输入的效果,即模拟每个路径来产生每一个执行的可能性,将执行语句的结果整合成若干条数学表达式。

符号执行有两篇文章可以参考。其一是2010年David Brumley团队在S&P会议上发表的《All You Ever Wanted to Knowabout Dynamic Taint Analysis and Forward Symbolic Execution (but Might HaveBeen Afraid to Ask)》[1]。这篇文章同时介绍了动态污点分析和前向符号执行的基本概念,作者构造了一种简化的中间语言,用来形式化地描述这两种程序分析技术的根本原理。其二是2011年CristianCadar发表在ACM通讯上的一篇短文《Symbolic execution forsoftware testing: three decades later》[2],以较为通俗的语言和简单的例子阐述了符号执行的基本原理,并介绍了符号执行技术的发展历程和面临挑战。

0x20 符号执行的分类

符号执行技术是一种白盒的静态分析技术。即,分析程序可能的输入需要能够获取到目标源代码的支持。同时,它是静态的,因为并没有实际的执行程序本身,而是分析程序的执行路径。符号执行根据发展状况可以分为传统的符号执行、动态符号执行和选择性符号执行。

  • 传统的符号执行其实是通过解析程序,利用符号值来模拟执行
  • 动态符号执行结合真实执行和传统的模拟执行,两者并行,达到真实值以及符号同时执行的效果
  • 选择性符号执行则是按照手动划分感兴趣的部分,有选择性的进行符号执行,剩下则用真实值进行执行

0x30 控制流程图-符号执行树

我们以实际的例子来说明。函数代码如下

int d(int x){int y = 2*x;y--;if(y==2)x++;elsex--;return x;
}

该代码很简单,有一个if语句的条件分支,下图就是程序的全部路径运行结果,可以选择对应的赋值来到达对应的运行结果在这里插入图片描述

0x40 实际应用

安全客上有一篇文章,讲解的很好:符号执行——从入门到高速。我更欣赏该文章讲解的实际应用方面。我们知道,由于物联网设备逐渐的大众化,嵌入式设备的漏洞挖掘需求更加显著。那么在嵌入式的环境下,使用符号执行技术挖掘漏洞可行吗?答案是肯定的。但是要解决很多问题,需要解决的问题除了类似于KLEE中出现的与外设交互的系统调用建模以外,还需要解决嵌入式设备中经常出现的混合代码情况。即,给出的嵌入式源代码中,有c语言代码、二进制代码(共享库)和汇编代码。面对这种情况应该如何启用符号执行呢?

举个实际的例子,以下是基于ARMv7架构的一段嵌入式代码。学过嵌入式的人都知道,在嵌入式设备中,仅仅使用C语言往往还是不够的,因为汇编有时候能够更好的与硬件交互,以下代码使用了汇编和C语言的混合编程,能够极大提高效率。

unsigned char msg[] = "world";
int index;void uart_send(unsigned char a)
{__asm volatile("SVC #0");__asm volatile("BX LR");
}void os_uart_send(){...}int main(int argc, char* argv[])
{uart_send(msg[index++]);return 0;
}

该代码的主要作用是通过uart发送消息。函数uart_send()中,通过指令“SVC #0”切换成SVC的寄存器模式之后,将实际上调用函数os_uart_send()发送消息。其中,uart_send函数的参数将直接在os_uart_send()函数中使用。上述代码,通过LLVM转换成LLVM-IR的形式,为下面的代码。

@msg = global [6xi8]c"world0"
@index = global i32 0define void @uart_send(i8 zeroext) #0
{entry:call void asm sideeffect "SVC #0",""()call void asm sideeffect "BX LR",""()unreachable
}define void @os_uart_send(){...}define int @main() #1
{entry:%0 = load i32* @index%inc = add nsw i32 %0, 1store i32 %inc, i32* @index%arrayidx = getelementptr inbounds [6xi8]* @msg, i32 0, i32 %0%1 = load i8* %arrayidxcall void @uart_send(i8 zeroext %1)ret i32 0
}

为什么要转换成LLVM-IR的代码呢。LLVM-IR是LLVM的一种中间的语言表达形式,也是一种汇编语言的形式。现有的KLEE工具就是LLVM-IR工具实现的符号执行虚拟机。在解释上面的代码之前,简单介绍几个LLVM-IR的基础语法,以便更清楚的理解。

LLVM-IR的变量有三种,通过前缀@或者%的形式区分,其中@表示全局变量,%表示局部变量:

  1. %或者@接数字,表示的是临时变量,在一个函数中,从0开始编号使用。比如%0,%1,。。
  2. %或者@接字符串,表示有名字的变量,可以任意使用;
  3. 第三类就是立即数
@msg = global [6xi8]c"world0"
@index = global i32 0

上述代码初始化了全局变量msg和index。

entry:
%0 = load i32* @index
%inc = add nsw i32 %0, 1
store i32 %inc, i32* @index

上述代码中,i32表示是32位的类型,i32表示的指向i32类型的指针类型。%0 = load i32 @index表示将全局变量index的值赋值给局部变量%0. Add nsw是有符号的相加。再加完之后,又把数值存储回了全局变量index。

%arrayidx = getelementptr inbounds [6xi8]* @msg, i32 0, i32 %0
%1 = load i8* %arrayidx
call void @uart_send(i8 zeroext %1)

上述代码中,[6xi8] 这种形式表示的是数组类型,这里就是含有6个8位的元素的数组。Getelementptr inbounds即使获得数组对应元素的指针。之后利用语句%1 = load i8* %arrayidx获取指针指向的8位的值,在传入到函数uart_send中执行。

可以发现,如果按照已有的KLEE方法,在转换成LLVM-IR代码之后,由于代码中含有arm架构的汇编,而该arm代码中没有明显的调用参数的代码和方式,使得符号数值的传递再次中断,导致KLEE方法不能执行。因此,我们需要对上述含有混合LLVM-IR代码的内存进行再次转化,使得含有低级语意的arm汇编也能够被KLEE的符号执行虚拟机分析。

通过程序分析的方式,将混合由高级语言和低级语言的代码同时转化成KLEE能够分析的语言,从而执行符号执行的分析。根据Inception里面的思想,上述LLVM-IR转化之后,可以有下面的表达形式:

@msg = global [6xi8]c"world0"
@index = global i32 0; stack is stored in global variables
@R0 = global i32 0, align 4
@SP = global i32 0, align 4 
@_SVC_fe = global i32 0, align 4 
@LR = global i32 0, align 4 
@.stack = global [8202xi4] zeroinitializer define void @uart_send(i8) #0
{; pass the parameters from high level to the low level.entry:%1 = zext i8 %0 to i32store i32 %1, i32* @R0br label %"uart_send+0" ; jmp to the actual code"uart_send+0":%SP1 = load i32* @SP ; load the stack pointerstore i32 0, iew* @_SVC_festore i32 268436580, i32* @PC ; store pointer executioncall void (...)* @_sv_call()call void (...)* os_uart_send()    ; invoke uart send function and  using the value from the R0%LR1 = load i32* @LR1              ;load return addressret void
}define void @os_uart_send(){...}define int @main() #1
{entry:%0 = load i32* @index%inc = add nsw i32 %0, 1store i32 %inc, i32* @index%arrayidx = getelementptr inbounds [6xi8]* @msg, i32 0, i32 %0%1 = load i8* %arrayidxcall void @uart_send(i8 zeroext %1)ret i32 0
}

可以发现,相比于之前的LLVM-IR。函数uart_send中多了一部分内容。更多内容参考之前提到的文章就行了。

0x50 总结

鉴于代码覆盖率越高越好,符号执行的要求如下

  • 尽可能的覆盖所有的可能的执行路径.
  • 速度尽可能快.

因为这样才能达到测试所有代码的目的,当然这是理论上的想法,实际过程中,项目庞大而复杂。例如对于一组分支组成的树形结构,最少有O(n)O(n)O(n)种指派方案可以覆盖到所有路径,也就是说最小的复杂度是线性的,最多有O(2n)O(2^n)O(2 n )种指派方案,是指数增长类型。这就是路径爆炸

所以,需要找到一个平衡点。符号执行实质是通过限制路径搜索空间来进行程序遍历,运行时间与所需要遍历的路径空间大小成正比。利用可靠的程序分析技术来减小路径爆炸的复杂度,利用启发式搜索搜索最佳路径,这些都是符号执行追求的终极目标。

这篇关于符号执行入坑指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/634339

相关文章

使用Java发送邮件到QQ邮箱的完整指南

《使用Java发送邮件到QQ邮箱的完整指南》在现代软件开发中,邮件发送功能是一个常见的需求,无论是用户注册验证、密码重置,还是系统通知,邮件都是一种重要的通信方式,本文将详细介绍如何使用Java编写程... 目录引言1. 准备工作1.1 获取QQ邮箱的SMTP授权码1.2 添加JavaMail依赖2. 实现

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

SQL Server数据库迁移到MySQL的完整指南

《SQLServer数据库迁移到MySQL的完整指南》在企业应用开发中,数据库迁移是一个常见的需求,随着业务的发展,企业可能会从SQLServer转向MySQL,原因可能是成本、性能、跨平台兼容性等... 目录一、迁移前的准备工作1.1 确定迁移范围1.2 评估兼容性1.3 备份数据二、迁移工具的选择2.1

在 Windows 上安装 DeepSeek 的完整指南(最新推荐)

《在Windows上安装DeepSeek的完整指南(最新推荐)》在Windows上安装DeepSeek的完整指南,包括下载和安装Ollama、下载DeepSeekRXNUMX模型、运行Deep... 目录在www.chinasem.cn Windows 上安装 DeepSeek 的完整指南步骤 1:下载并安装

nginx-rtmp-module构建流媒体直播服务器实战指南

《nginx-rtmp-module构建流媒体直播服务器实战指南》本文主要介绍了nginx-rtmp-module构建流媒体直播服务器实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. RTMP协议介绍与应用RTMP协议的原理RTMP协议的应用RTMP与现代流媒体技术的关系2

Spring Boot统一异常拦截实践指南(最新推荐)

《SpringBoot统一异常拦截实践指南(最新推荐)》本文介绍了SpringBoot中统一异常处理的重要性及实现方案,包括使用`@ControllerAdvice`和`@ExceptionHand... 目录Spring Boot统一异常拦截实践指南一、为什么需要统一异常处理二、核心实现方案1. 基础组件

电脑密码怎么设置? 一文读懂电脑密码的详细指南

《电脑密码怎么设置?一文读懂电脑密码的详细指南》为了保护个人隐私和数据安全,设置电脑密码显得尤为重要,那么,如何在电脑上设置密码呢?详细请看下文介绍... 设置电脑密码是保护个人隐私、数据安全以及系统安全的重要措施,下面以Windows 11系统为例,跟大家分享一下设置电脑密码的具体办php法。Windo