« | August 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | |
| 公告 |
戒除浮躁,读好书,交益友 |
Blog信息 |
blog名称:邢红瑞的blog 日志总数:523 评论数量:1142 留言数量:0 访问次数:9692702 建立时间:2004年12月20日 |

| |
[rootkit]从connect函数看stack frame  原创空间, 软件技术, 电脑与网络
邢红瑞 发表于 2008/1/17 10:45:58 |
高级语言都使用stack frame传递函数参数,机器码和汇编语言除外,至少CISC的cpu如此,RISC类型cpu寄存器众多,可以不使用stack frame。下面以stdcall调用作为例子,也就是WINDOWS API,参数从右向左入栈,其中EBP作为一个参考指针也入栈.return address是返回地址非返回值!被调用函数负责清栈,函数的最后就是ret 0c。当调用函数(CALL FUN)时,CPU把当时的地址(IP值)压栈,再跳到函数的地址处,函数返回时,CPU再把恢复原来的地址。存ebp的原因是你要使用ebp,而一般函数调用约定不更允许调用函数返回后ebp的值被更改——因为调用者也要使用ebp。另外,Windows的函数调用还隐含规定,索引寄存器edi、esi的值不允许被更改。如果你要用到,必须先保存,用完后再恢复。堆栈段 ss 只能用 esp或ebp寄存器 其他的寄存器eax ebx edx等都不能够用 而 esp永远指向堆栈栈顶 ebp用来在堆栈段里面寻址push 指令是压栈 ESP=ESP-4pop 指令是出栈 ESP=ESP+4 返回地址其实就是调用者call指令的下一条指令地址,当执行call指令时硬件负责将其压栈,在被调函数返回执行ret指令时,由硬件负责从当前栈顶取出返回地址,并转移到返回地址继续执行——除非你明确地知道这是怎么回事,否则不要自己在程序中更改返回地址。500)this.width=500'>
使用od研究一下,winsock的connect函数。以telnet作为例子,打开od,加载telnet,在命令框中输入 bpx connect,如果没有命令框,请装相应的插件。然后运行,输入 open 192.168.31.183,这是一个telnet服务器。此时,程序停留在断点处。71A2406A > 8BFF mov edi,edi71A2406C 55 push ebp71A2406D 8BEC mov ebp,esp71A2406F 83EC 18 sub esp,1871A24072 57 push edi71A24073 8D45 E8 lea eax,dword ptr ss:[ebp-18]71A24076 50 push eax71A24077 8D45 EC lea eax,dword ptr ss:[ebp-14]71A2407A 50 push eax71A2407B FF15 2840A371 call dword ptr ds:[71A34028] ; WS2_32.71A2263671A24081 33FF xor edi,edi71A24083 3BC7 cmp eax,edi71A24085 8945 FC mov dword ptr ss:[ebp-4],eax71A24088 0F85 D9010000 jnz WS2_32.71A2426771A2408E FF75 08 push dword ptr ss:[ebp+8]71A24091 E8 ABEAFFFF call WS2_32.71A22B4171A24096 3BC7 cmp eax,edi71A24098 8945 F0 mov dword ptr ss:[ebp-10],eax
此时,输入 dd esp ,此时的esp已不是进入时的esp了。不要忘了栈里面还有一个返回地址,显示
00A4EE68 010057DA /CALL 到 connect 来自 telnet.010057D400A4EE6C 000000F8 |Socket = F800A4EE70 000AF158 |pSockAddr = 000AF15800A4EE74 00000010 \AddrLen = 10 (16.)
注意 010057DA 就是函数的返回地址,后面的就是connect的参数msdn的说明int connect( SOCKET s, const struct sockaddr FAR* name, int namelen);
因为windows在x86的平台的stack是grow down ,向下增长的,函数是从右向左传递的,namelen最先入栈,最后是s,但由于是向下增长的,大家看到了上面的情况。esp+4 就是socket,我们主要看第二个参数
struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8];};输入 dd [esp+8]显示000AF158 17000002000AF15C B71FA8C0000AF160 00000000000AF164 00000000AF_ 地址族,序号为2,所以最后是2,注意 B71FA8C0 ,转为 ip就是 183.31.168.192,这就是ip地址,后面的参数添0,对齐为8的倍数,防止不同的编译器为了字节对齐,自己添0.大家注意一下,>71A2406A > 8BFF mov edi,edi>71A2406C 55 push ebp>71A2406D 8BEC mov ebp,esp有的机子是
>71A2406C 55 push ebp>71A2406D 8BEC mov ebp,esp前面地址可以不要管,为什麽不同呢?这就是微软的同步码,70%的以上的Windows API都有这个同步码。winsows xp sp2 是5位,多了mov edi,edi, winsows xp sp1 windows 2000和2003各版本都是3位,为什麽呢?因为 jmp XXXXXXXX ,后面是一个32bit的地址,x86是32位的,jmp后面是立即操作数的编码是E9,加上后面的地址,正好是5位,微软主要是为了自己升级补丁或打sp时,OS不用重启,只是一个地址跳转,必要的时候,可以跳回去。如果是3位,麻烦就大了,必须破坏原来的函数的地址,因为一个跳转需要5位,原来的同步码有3位,还要保存原来函数地址空间的数据,否则修改后,再次调用原来的函数时,必须恢复,使用5位同步码,大大降低了黑客程序的难度,不用保存原来地址的数据。MmHotpatchRoutine就是这么做的。各位看明白了没有,其实这位黑客的留下了方便之门,5位的同步码,黑客可以轻而易举的改变connect函数,这个函数作用不大,主要是send和recv。黑客可以先loadlibrary ws2_32.dll,然后得到send和recv的地址,修改他们 ,jmp到自己的dll函数内部,得到数据后,再拆去钩子,调用原来的send和recv,发送。这就是外挂程序的原理,著名外挂WPE也是大同小异。 |
|
|