软件安全第一次实验报告

实验目标

1.读懂实验所给的用于密码验证的小程序,改变程序的运行路线,系统:Windows 2000~Windows 11、wsl、Linux皆可;工具:VS、gdb、Ollydbg,x96dbg等。(以上记作实验一)

2.根据在附件/test 目录下的源码;学习用调试工具调试密码验证小程序;通过修改汇编语句来修改程序的判断条件,改变程序的运行路线。(以上记作实验二)

3.根据在附件/crackme目录下的文件,修改程序代码的方式绕过crackme.exe的密码验证逻辑,至少采用2种破解方式方法,并提交破解后的程序。(以上记作实验三)

4.报告要求

请详述修改过程;实验结果需要截图证明;绘出程序运行的结构图,以及修改原理的图示(通过程序运行的结构图)。

实验一步骤与结果

(一)读懂实验所给的用于密码验证的小程序

使用VC打开test.c,对密码验证小程序代码分析如下:

1.程序定义了一个字符串常量PASSWORD,其值为1234567。

图1

2.程序定义了一个verify_password()函数,函数传入参数为一个字符数组,返回值为一个整型变量。

图2

此函数所实现的功能为:将传入的字符串与PASSWORD比较,返回比较结果authenticated。若authenticated=0则字符串不同,验证失败;若authenticated=1则字符串相同,验证成功。

3.主函数中,由while(1)控制不断循环,每次循环中首先输入一个字符串password,再将password作为参数输入verify_password函数进行验证。若验证成功,则输出“Congratulation! You have passed the verification!\n”并跳出循环、结束程序;若验证失败,则输出“incorrect password!\n\n”,开始下一轮循环。

图3

4.生成工程

(1)在VC中,点击File->new->Projects->Win32 Console Application,右上角设置好project的名称和存放路径。

图4

(2)点击OK->An empty project->Finish

图5

(3)之后再new->Files->C++ Source File,右侧设定好添加到的项目(我们刚才新建的项目),文件名和路径。

图6

(4)完成新建文件后,在新建的文件下编写或复制刚才我们看到的main程序, 然点击右上的红色感叹号执行。

图7

5.运行程序,验证以上分析。

图8

图9

由运行结果可知,当所输入的字符串不是PASSWORD的值1234567时,输出“incorrect password!\n\n”并开始下一次循环,直至输入正确密码1234567时才输出“Congratulation! You have passed the verification!\n”并跳出循环、结束程序。

(二)编写程序,改变程序的运行路线

用python编写密码验证小程序,程序代码如下:

图10

同样使用while True不断循环,其中使用了两层循环,分别判断用户名和密码是否正确。外层循环为——若用户名正确,则开始内层循环验证密码;若用户名错误,则用户重新输入用户名。内层循环为——若密码正确,则验证成功跳出循环;若密码错误,则验证失败用户重新输入密码。

2.程序可以实现的功能为:当用户同时输入正确的用户名和正确密码时,才能够通过验证。

3.运行程序,测试结果如下图所示:

图11

输入错误用户名时,输出“incorrect username!\n”并继续进入下一次循环;输入正确用户名时,输出“please input password: ”,用户输入密码。

输入错误密码时,输出“incorrect password!\n”并继续进入下一次循环;输入正确密码时,输出“Congratulation! You have passed the verification!\n”并跳出双层循环、结束程序。由此可知,该程序可以正确完成密码验证。

实验二步骤与结果

(一)使用Ollydbg调试密码验证小程序,修改汇编语句以修改程序判断条件

1.运行Ollydbg.exe,选择我们刚刚编译生成的test.exe文件

图12

图13

2.这个exe文件在我们项目文件夹下的Debug文件夹下

图14

3.点击Ollydbg左上角运行按钮,开始运行程序。

图15

4.尝试输入几个错误密码,如“12”、“123”。

图16

5.可以看到Ollydbg的调试走到了程序判断部分,分析此部分代码:

图17

可以看到“incorrect password!”和“Congratulations! You have passed the verification!” 两个字符串。

由此可知,程序004010F5处语句即为跳转逻辑语句,JE即为相等时跳转。因此,为了破解此密码验证小程序,将此处的跳转条件JE修改为JNE,即可在输入错误密码时通过验证。

图18

6.修改后可以看到,此语句的操作码由74(JE)改为了75(JNE)。

图19

7.验证修改后的程序。修改后运行程序,输入正确密码“1234567”发现程序输出“incorrect password!”,而随意输入错误密码时则通过验证,说明已成功修改程序。

图20

(二)程序运行结构图及修改原理图示

1.程序运行结构图:

图21

2.修改原理图示:

图22

(三)实验结论

简单的判断密码字符串是否相同的跳转逻辑代码对于动态调试的防御能力较弱,可以通过修改汇编语句较容易地改变判断逻辑,实现程序破解。因此我们在设计程序时,要充分考虑如何设计逻辑以抵御动态调试等破解方法,以保证软件安全。

我们只需修改一个小小的跳转条件即可实现对程序的破解,输入错误的密码提示正确,正确的密码反而提示错误。通过这次实验,我对软件安全有了更深刻的认识,也意识到软件存在着极大的安全隐患,软件安全是软件研究中一个重要方面,我们应该通过软件安全这门课程学习更多安全知识,以更好地维护网络安全。在今后的学习和工作中,编写程序时应该考虑到攻击者会进行反汇编以及修改,最好对代码进行一定的混淆。

实验三步骤与结果

(一)运行程序初步尝试

1.首先双击crackme.exe运行程序,程序界面如下:

图23

2.尝试在用户名及密码未输入的情况下点击确定,发现弹出窗口提示“请输入用户名!”“请输入密码!”。由此可知,程序中设置了判断用户名及密码是否为空的代码逻辑。

图24

图25

3.随意输入用户名“222”和密码“222”点击注册后,弹窗提示“注册失败!”。

图26

(二)Ollydbg动态调试crackme.exe

1.在Ollydbg查找字符串,查找到字符串“crackmepassword”。找到后双击字符串,可以找到程序中比较判断部分逻辑代码,如下图所示。由此可知,正确密码即为字符串“crackmepassword”。

图27

2.重新运行程序,尝试输入密码“crackmepassword”,任意输入用户名,即可注册成功。

图28

用户名为“222”

图29

用户名为“admin”

3.由Ollydbg动态调试汇编代码可知,程序判断的关键跳转逻辑在程序的0040161E处,JNZ表示比较结果不同则跳转。下方的call语句均为程序输出部分,负责弹窗输出“注册失败!”及“注册成功!”。

图30

4.因此,字符串“crackmepassword”上方语句即为弹出程序初始窗口、程序判断用户名及密码是否为空并弹窗输出“请输入用户名!”“请输入密码!”的汇编代码。

图31

(三)三种方法破解crackme.exe程序

方法一:将跳转逻辑中JNZ修改为JZ

将程序0040161E处汇编代码中不为零时跳转(JNZ)修改为为零时跳转(JZ),即将此汇编语句中操作码由75修改为74,即可在输入错误密码时注册成功,而在输入正确密码时注册失败。

图32

由图可知,JNZ SHORT crackme.00401637即为注册成功/失败判断跳转。

图33

图34

修改:将JNZ SHORT crackme.00401637改成JE SHORT crackme.00401637。修改后再次运行程序,如下两图可知,输入任意错误密码可以注册成功,成功实现破解crackme.exe程序。

图35

用户名为:“admin”,密码为“crackmepassword”时注册失败

图36

用户名为“admin”,密码为“1”时注册成功

方法二:将密码(或用户名)为空的跳转逻辑修改为跳转至注册成功

1.由(二)中对汇编代码的分析可知,程序004015CE处的代码即为程序判断密码为空时实现跳转到“请输入密码”弹窗。为了验证这个结论,在程序调用MessageBox处(004015DB)双击设置断点后运行程序。

图37

在004015DB处设置断点

图38

输入用户名为“admin”,密码为空

图39

点击注册后,程序运行到断点处

2.由以上结果可知,004015CE处为程序判断密码是否为空的跳转逻辑代码。为了修改相应弹窗内容,要使这部分入栈及调用MessageBox代码全部替换为“注册成功”弹窗的入栈及调用MessageBox代码。

因此,将程序004015CE跳转逻辑由“当不为零(密码不为空)时跳转至0015E2进行密码字符串的比较”修改为“当为零(密码为空)时跳转至0040161A处进行注册成功弹窗”。即将汇编语句修改为JZ SHORT 0040161A。

图40

3.修改后运行程序,输入用户名为admin,密码为空时即可显示“注册成功!”弹窗。

图41

输入用户名为“admin”,密码为空时显示注册成功

图42

输入用户名为“admin”,密码为正确密码“crackmepassword”显示请输入密码

图43

输入用户名为“admin”,密码为错误密码“1”显示请输入密码

4.同理可知,也可以将程序跳转逻辑修改为“当用户名为空时,跳转到显示注册成功弹窗的代码”。即将程序004015B2处汇编语句修改为JZ SHORT 0040161A。修改完成后运行程序,用户名为空时不进行输入直接点击注册,显示弹窗“注册成功”。

图44

图45

用户名为空时显示注册成功

至此实现破解crackme.exe程序。

方法三:将正确密码字符串修改为“mycrackpassword”

1.找到密码字符串“crackmepassword”,在下方HEX数据框中根据ASCII码将密码字符串修改为“mycrackpassword”。

图46

图47

2.修改完成后点击运行程序,任意输入用户名,输入密码“mycrackpassword”,注册成功;输入原始密码“mycrackpassword”时,注册失败。成功实现破解crackme.exe程序。

图48

用户名为“admin”,密码为“mycrackpassword”时注册成功

图49

用户名为“admin”,密码为“crackmepassword”时注册失败

附件:crackme破解程序

crackme1.exe:输入任意错误密码时显示注册成功,输入正确密码crackmepassword时则显示注册失败。

crackme2.1.exe:输入任意用户名,密码为空时显示注册成功;输入任意密码则显示“请输入密码!”

crackme2.2.exe:用户名为空时,点击注册即显示注册成功。

crackme3.exe:输入任意用户名,密码为修改后密码mycrackpassword时显示注册成功;输入正确密码或其他错误密码时则注册失败。

实验结论

通过本次实验,我对软件安全中常见的身份验证绕过机制有了更加深入和直观的认识。crackme.exe 作为一个典型的口令验证程序,其验证逻辑在未加保护的情况下暴露出多处可被篡改的脆弱点。实验中我采用的三种破解方法:修改关键跳转条件、利用空输入绕过验证、篡改密码明文数据。分别从控制流、输入校验和数据完整性三个层面揭示了软件在设计上的安全风险。

首先,验证逻辑中的条件跳转(JNZ/JZ)直接决定了程序的分支走向,而这一机制若暴露于可修改的内存环境中,极易被攻击者利用,从而完全颠覆原有的安全判断。其次,对用户输入的有效性检查本应是防御的第一道防线,但若检查逻辑本身存在缺陷或被篡改,反而可能成为绕过验证的入口。最后,程序中硬编码的敏感信息(如密码字符串)以明文形式存储,缺乏必要的混淆或加密措施,使得攻击者可通过静态或动态分析直接定位并修改关键数据。

这次实验也让我进一步认识到,软件安全并非仅在开发完成后通过外部防护手段来保障,而是需要在编码、编译、乃至分发前的各个环节中,系统性地融入安全思维。作为《软件安全》课程的一次实践,本实验不仅锻炼了我使用调试工具进行动态分析的能力,更深化了我对“安全在于细节”这一理念的理解——任何微小的逻辑疏漏或实现上的不慎,都可能在恶意分析面前被放大为严重的安全漏洞。今后在参与软件开发时,我将更加注重代码的健壮性、敏感信息的保护机制以及整体架构的安全性设计,从而在根源上降低此类风险的发生概率。

实验报告持续更新中…