1.4 编写简易ShellCode弹窗
在前面的章节中相信读者已经学会了使用Metasploit
工具生成自己的ShellCode
代码片段了,本章将继续深入探索关于ShellCode
的相关知识体系,ShellCode 通常是指一个原始的可执行代码的有效载荷,攻击者通常会使用这段代码来获得被攻陷系统上的交互Shell的访问权限,而现在用于描述一段自包含的独立的可执行代码片段。ShellCode代码的编写有多种方式,通常会优先使用汇编语言实现,这得益于汇编语言的可控性。
ShellCode 通常会与漏洞利用并肩使用,或是被恶意代码用于执行进程代码的注入,通常情况下ShellCode
代码无法独立运行,必须依赖于父进程或是Windows
文件加载器的加载才能够被运行,本章将通过一个简单的弹窗(MessageBox)来实现一个简易版的弹窗功能,并以此来加深读者对汇编语言的理解。
(资料图)
1.4.1 寻找DLL库函数地址在编写ShellCode
之前,我们需要查找一个函数地址,由于我们需要调用MessageBoxA()
这个函数,所以需要获取该函数的内存动态地址,根据微软的官方定义可知,该函数默认放在了User32.dll
库中,为了能够了解压栈时需要传入参数的类型,我们还需要查询一下函数的原型;
在微软定义中MessageBoxA
函数的原型如下:
int MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
参数说明:
hWnd:消息框的父窗口句柄。lpText:消息框中显示的文本。lpCaption:消息框的标题栏文本。uType:消息框的类型,可以指定消息框包含的按钮以及图标等。需要注意的是,由于我们调用的是MessageBoxA
,而此函数为ASCII模式,需要读者自行修改解决方案,在配置属性的常规选项卡,修改字符集(使用多字节字符集)即可,如下图所示;
读者可以通过编写一段简单的代码来获取所需数据,首先通过LoadLibrary
函数加载名为user32.dll
的动态链接库,并将其基地址存储在HINSTANCE
类型的变量LibAddr
中。然后,使用GetProcAddress
函数获取 MessageBoxA
函数的地址,并将其存储在MYPROC
类型的变量ProcAddr
中。最后输出所需结果;
#include #include typedef void(*MYPROC)(LPTSTR);int main(int argc, char *argv[]){ HINSTANCE LibAddr,KernelAddr; MYPROC ProcAddr; // 获取User32.dll基地址 LibAddr = LoadLibrary("user32.dll"); printf("user32.dll 动态库基地址 = 0x%x \n", LibAddr); // 获取kernel32.dll基地址 KernelAddr = LoadLibrary("kernel32.dll"); printf("kernel32.dll 动态库基地址 = 0x%x \n", KernelAddr); // 获取MessageBox基地址 ProcAddr = (MYPROC)GetProcAddress(LibAddr, "MessageBoxA"); printf("MessageBoxA 函数相对地址 = 0x%x \n", ProcAddr); // 获取ExitProcess基地址 ProcAddr = (MYPROC)GetProcAddress(KernelAddr, "ExitProcess"); printf("ExitProcess 函数相对地址 = 0x%x \n", ProcAddr); system("pause"); return 0;}
上方的代码经过编译运行后会得到两个返回结果,如下图所示,其中User32.dll
的基地址是0x75a40000
而该模块内的MessageBoxA
函数在当前系统中的地址为0x75ac0ba0
,当然这两个模块地址在每次系统启动时都会发生幻化,读者电脑中的地址肯定与笔者不相同,这都是正常现象,之所以会出现这种情况是因为,系统中存在一种ASLR机制。
扩展知识:ASLR(Address Space Layout Randomization)机制的核心是用于随机化系统中程序和数据的内存地址分布,从而增加攻击者攻击系统的难度,在启用了ASLR机制的系统下,每次运行程序时,程序和系统组件(例如DLL、驱动程序等)都会被分配不同的内存地址,而不是固定的内存地址。这样可以使得攻击者难以利用已知的内存地址漏洞进行攻击,因为攻击者需要先找到正确的内存地址才能利用漏洞。ASLR的随机化是根据操作系统的一些随机因素进行计算的,例如启动时间、进程 ID 等等。
由于如上机制的存在,导致user32.dll
模块地址不确定,也就会导致其地址内部的API函数地址也会发生一定的变化,下图仅作为参考图;
在获取到MessageBoxA
函数的内存地址以后,我们接着需要获取一个ExitProecess
函数的地址,这个API函数的作用是让程序正常退出,这是因为我们注入代码以后,原始的堆栈地址会被破坏,堆栈失衡后会导致程序崩溃,所以为了稳妥起见我们还是添加一行正常退出为好。函数ExitProcess
的原型如下:
VOID WINAPI ExitProcess( UINT uExitCode);
其中参数uExitCode
指定了进程的退出代码,表示进程成功退出或者发生了错误。如果uExitCode
为0,表示进程成功退出,其他的非0值则表示进程发生了错误,不同的非0值可以用于表示不同的错误类型。
既然获取到了相应的内存地址,那么接下来就需要通过汇编来编写可执行代码片段了,在编写这段代码之前,先来了解一下汇编语言的调用约定,在汇编语言中,要想调用某个函数,需要使用CALL语句,而在CALL语句的后面,要跟上该函数在系统中的地址,前面我们已经获取到了相应的内存地址了,所以在这里就可以通过CALL相应的地址来调用相应的函数。
我们以32位应用程序为例,在32位应用程序内通常使用STDCALL
调用约定,它定义了函数在被调用时,参数传递、返回值传递以及栈的使用等方面的规则,该调用约定的规则如下所示:
总之,stdcall调用约定将参数按照从右到左的顺序压入栈中,由被调用者清理栈,返回值存储在EAX寄存器中,函数调用者和被调用者都需要遵循一定的栈使用规则。这种约定的好处是参数传递简单,可读性高,并且在函数返回时栈已经被清理,不需要额外的清理工作。
在实际的编程中,一般还是先将地址赋值给eax
寄存器,然后再CALL
调用相应的寄存器实现调用,比如现在笔者有一个lyshark(a,b,c,d)
函数,如果我们想要调用它,那么它的汇编代码就应该编写为:
push dpush cpush bpush amov eax,AddressOflyshark // 获取偏移地址call eax // 间接调用
根据上方的调用方式,我们可以写出ExitProcess()
函数的汇编版调用结构,如下;
xor ebx, ebxpush ebxmov eax, 0x76c84100call eax
接着编写MessageBox()
这个函数调用。与ExitProcess()
函数不同的是,这个API函数包含有四个参数,当然第一和第四个参数,我们可以赋给0值,但是中间两个参数都包含有较长的字符串,这个该如何解决呢?我们不妨先把所需要用到的字符串转换为ASCII码值,转换的方式有许多,如下代码则是通过Python实现的转换模式;
import os,sysfrom LyScript32 import MyDebug# 字符串转asciidef StringToAscii(string): ref = [] for index in range(0,len(string)): hex_str = str(hex(ord(string[index]))) ref.append(hex_str.replace("0x","\\x")) return refif __name__ == "__main__": # 输出MsgBox标题 title = StringToAscii("alert") for index in range(0,len(title)): print(title[index],end="") print() # 输出MsgBox内容 box = StringToAscii("hello lyshark") for index in range(0,len(box)): print(box[index],end="")
当Python
程序被运行,则用户即可得到两串通过编码后的字符串数据。
MsgBox标题:alert \x61\x6c\x65\x72\x74\x21MsgBox内容:hello lyshark \x68\x65\x6c\x6c\x6f\x20\x6c\x79\x73\x68\x61\x72\x6b
由于我们使用的是32位汇编,所以上方的字符串需要做一定的处理,我们分别将每四个字符为一组,进行分组,将不满四个字符的,以空格0x20
进行填充,这是因为我们采用的存储字符串模式为栈传递,而一个寄存器为32位,所以就需要填充满4字节才可以平衡;
-------------------------------------------------------------填充 alert-------------------------------------------------------------\x61\x6c\x65\x72\x74\x21\x20\x20-------------------------------------------------------------填充 hello lyshark-------------------------------------------------------------\x68\x65\x6c\x6c\x6f\x20\x6c\x79\x73\x68\x61\x72\x6b\x20\x20\x20
上方的空位置之所以需要以0x20
进行填充,而不是0x00
进行填充,是因为strcpy
这个字符串拷贝函数,默认只要一遇到0x00
就会认为我们的字符串结束了,就不会再拷贝0x00
后的内容了,所以这里就不能使用0x00
进行填充了,这里要特别留意一下。
接着我们需要将这两段字符串分别压入堆栈存储,这里需要注意,由于我们的计算机是小端序
排列的,因此字符的入栈顺序是从后往前不断进栈的,上面的字符串压栈参数应该写为:
小提示:小端序(Little Endian)是一种数据存储方式,在汇编语言中,小端序的表示方式与高位字节优先(Big Endian)相反。例如,对于一个16位的整数0x1234,它在小端序的存储方式下,将会被存储为0x340x12(低位字节先存储);而在高位字节优先的存储方式下,将会被存储为0x120x34(高位字节先存储)。
-------------------------------------------------------------压入字符串 alert-------------------------------------------------------------push 0x20202174push 0x72656c61-------------------------------------------------------------压入字符串 hello lyshark-------------------------------------------------------------push 0x2020206bpush 0x72616873push 0x796c206fpush 0x6c6c6568
既然字符串压入堆栈的功能有了,那么下面问题来了,我们如何获取这两个字符串的地址,从而让其成为MessageBox()
的参数呢?
其实这个问题也不难,我们可以利用esp
指针,因为它始终指向的是栈顶的位置,我们将字符压入堆栈后,栈顶位置就是我们所压入的字符的位置,于是在每次字符压栈后,可以加入如下指令,依次将第一个字符串基地址保存至eax
寄存器中,将第二个基地址保存至ecx
寄存器中。
xor ebx,ebx // 清空寄存器push 0x20202174 // 字符串 alert push 0x72656c61mov eax,esp // 获取第一个字符串的地址push ebx // 压入00为了将两个字符串分开push 0x2020206b // 字符串 hello lysharkpush 0x72616873push 0x796c206fpush 0x6c6c6568mov ecx,esp // 获取第二个字符串的地址
上方汇编指令完成压栈以后,接下来我们就可以调用MessageBoxA
函数了,其调用代码如下。
push ebx // push 0push eax // push "alert"push ecx // push "hello lyshark !"push ebx // push 0mov eax,0x75ac0ba0 // 将MessageBox地址赋值给EAXcall eax // 调用 MessageBox
1.4.3 ShellCode提取与应用通过上方的实现流程,我们的ShellCode
就算开发完成了,接下来读者只需要将上方ShellCode
整理成一个可执行文件并编译即可。
#include int main(int argc, char *argv[]){ _asm { sub esp, 0x50 // 抬高栈顶,防止冲突 xor ebx, ebx // 清空ebx push ebx push 0x20202174 push 0x72656c61 // 字符串 "alert" mov eax, esp // 获取栈顶 push ebx // 填充00 截断字符串 push 0x2020206b push 0x72616873 push 0x796c206f push 0x6c6c6568 // 字符串 hello lyshark mov ecx, esp // 获取第二个字符串的地址 push ebx push eax push ecx push ebx mov eax, 0x75ac0ba0 // 获取MessageBox地址 call eax // call MessageBox push ebx mov eax, 0x76c84100 // 获取ExitProcess地址 call eax // call ExitProcess } return 0;}
接下来就是需要手动提取此处汇编指令的特征码,本案例中我们可以通过x64dbg
中的LyScript
插件实现提取,首先载入被调试进程,然后寻找到如下所示的特征位置,当遇到Call
时,则通过F7进入到内部,如下图所示;
如下图中所示,就是我们所需要的汇编指令集,也就是我们自己的ShellCode
代码片段,内存地址为0x002D12A0
转换为十进制为2953888
通过LyScript插件并编写如下脚本,并将EIP位置设置为eip = 2953888
运行这段代码;
from LyScript32 import MyDebugif __name__ == "__main__": dbg = MyDebug() dbg.connect() ShellCode = [] eip = 2953888 for index in range(0, 100 - 1): read_code = dbg.read_memory_byte(eip + index) ShellCode.append(str(hex(read_code))) for index in ShellCode: print(index.replace("0x","\\x"),end="") dbg.close()
则可输出如下图所示的完整特征码,读者可自行将此处特征码格式化;
当然读者通过在_asm
指令位置设置F9
断点,并通过F5
启动调试,如下图所示;
当调试器被断下时,通过按下Ctrl+Alt+D
跳转至反汇编代码位置,并点击显示代码字节,同样可以实现提取,如下图所示;
我们直接将上方的这些机器码提取出来,从而编写出完整的ShellCode,最终测试代码如下。
#include #include #include #pragma comment(linker,"/section:.data,RWE")unsigned char shellcode[] = "\x83\xec\x50""\x33\xdb""\x53""\x68\x74\x21\x20\x20""\x68\x61\x6c\x65\x72""\x8b\xc4""\x53""\x68\x6b\x20\x20\x20""\x68\x73\x68\x61\x72""\x68\x6f\x20\x6c\x79""\x68\x68\x65\x6c\x6c""\x8b\xcc""\x53""\x50""\x51""\x53""\xb8\xa0\x0b\xac\x75""\xff\xd0""\x53""\xb8\x00\x41\xc8\x76""\xff\xd0";int main(int argc, char **argv){ LoadLibrary("user32.dll"); __asm { lea eax, shellcode call eax } return 0;}
上方代码经过编译以后,运行会弹出一个我们自己DIY
的MessageBox
提示框,输出效果图如下所示;
标签:
推荐文章
- 研究人员最新发现 单个细胞可同时处理成百上千个信号
- 陆军第73集团军某旅 创新升级模拟训练器材
- 长期暴露在光照下性能退化 科学家发现钙钛矿太阳能电池最大缺陷
- 宁夏启动双百科技支撑行动 构建高水平产业创新体系
- 陆军炮兵防空兵学院 毕业学员综合战术演习现地备课工作圆满完成
- 国内首颗以茶叶冠名遥感卫星 安溪铁观音一号发射成功
- 区域特色产业转型升级 四川屏山以“3+”模式推进科技创新工作
- 激发创新动能促进产业发展 无锡滨湖走出产业转型“绿色”路
- 绥化全域低风险!黑龙江绥化北林区一地调整为低风险
- 走访抗美援朝纪念馆:长津湖的寒冷,与战斗一样残酷
- 节后第一天北京白天晴或多云利于出行 夜间起秋雨或再上线
- 走近网瘾少年们:他们沉迷网络的病根何在?
- “双减”后首个长假:亲子游、研学游需求集中释放
- 获2021年诺奖的蛋白,结构由中国学者率先解析
- 他从一窍不通的“门外汉”,到重装空投“兵专家”
- 升旗、巡岛、护航标、写日志,他们一生守护一座岛
- 中国故事丨“沉浸式”盘点今年的教育好声音!
- 农业农村部:确保秋粮丰收到手、明年夏季粮油播种
- “双减”出台两个月,组合拳如何直击减负难点?
- 《山海情》里“凌教授”的巨菌草丰收啦
- 且看新疆展新颜
- 天山脚下,触摸丝路发展新脉动
- 160万骑手疑似“被个体户”?平台不能当甩手掌柜
- 网游新政下,未成年人防沉迷的“主战场”在哪?
- “辱华车贴”商家及客服被行拘,处罚要不放过每一环
- 沙害是自然界的恶魔,而他是荒沙碱滩的征服者
- 面对婚姻,“互联网世代”的年轻人在忧虑什么?
- IP类城市缘何吸引力强?玩法创新带动游客年轻化
- 国庆主题花坛持续展摆至重阳节
- 都市小资还是潮流乐享?花草茶市场呈爆发性增长
- 从1.3万元降到700元,起诉书揭秘心脏支架“玄机”
- 北京国庆7天接待游客超861万人次 冬奥线路受青睐
- 陈毅元帅长子忆父亲叮嘱:你们自己学习要好,就可以做很多事儿
- 报告显示:这个国庆假期,粤川浙桂赣旅游热度最高
- 中国科技人才大数据:广东总量第一,“北上”这类人才多
- 嘉陵江出现有记录以来最强秋汛
- 全国模范法官周淑琴:为乡村群众点燃法治明灯
- 线上教学模式被盯上,网络付费刷课形成灰色产业链
- 云南保山:170公里边境线,4000余人日夜值守
- 警方查处故宫周边各类违法人员12人
- 农业农村部:确保秋粮丰收到手、明年夏季粮油播种
- 受南海热带低压影响 海南海口三港预计停运将持续到10日白天
- 多地网友投诉遭遇旅游消费骗局,呼吁有关部门严查乱象
- 神经科学“罗塞塔石碑”来了:迄今为止最完整的大脑细胞图谱
- 汾河新绛段发生决口
- 陕西支援14省份采暖季保供用煤3900万吨
- 这场红色故事“云比拼”,穿越时空为我们指引方向
- 受琼州海峡封航影响 10月7日、8日进出海南岛旅客列车停运
- 辽宁省工信厅发布10月8日电力缺口橙色预警
- 广州10月8日至20日对所有从省外来(返)穗人员实施核酸检测
- 假期怎么过得这么快?国庆5.15亿人次出游,你咋过的?
- 国庆假期全国道路交通总体安全平稳有序
- 哈尔滨市南岗区爱达88小区将调整为低风险地区
- 新疆霍尔果斯市2例无症状感染者新冠病毒均为德尔塔变异株
- 百闻不如一见——北京大学留学生参访新疆
- 看,生机勃勃的中国
- 国庆假期中国预计发送旅客4.03亿人次
- 新疆兵团可克达拉市:195名密接者已全部隔离医学观察
- 山西平遥消防4天29次救援:拖着腿走路也要完成任务
- 国庆假期北京接待游客861.1万人次
- 冷空气自西向东影响中国大部地区 气温将下降4℃至6℃
- 新疆哈密市巴里坤县发生4.3级地震 震源深度9千米
- 国庆假期中国国内旅游出游5.15亿人次
- 公安部交管局:国庆假期日均出动警力18万余人次,5位交警辅警牺牲
- 受南海热带低压影响广东将暂别高温天气
- “数说”杭州无障碍改造:触摸城市“爱的厚度”
- 新疆霍尔果斯无症状感染者新冠病毒属德尔塔变异株 未发现高度同源的基因组序列
- 新疆伊犁州:妥善做好滞留旅客安置返回工作
- 国庆假期广西累计接待游客逾3611万人次 实现旅游消费272.41亿元
- 2021年MAGIC3上海市青少年三对三超级篮球赛落幕
- 新疆兵团第四师可克达拉市1名无症状感染者为餐饮从业人员
- 哥伦比亚遇上广州:洋茶人“云上”喫茶 传播中国茶“味道”
- 厦门同安区四区域调整为低风险 全市无中高风险地区
- 直径2米“面气球”亮相 山西首届“寿阳味道”美食大赛启幕
- 世界第一埋深高速公路隧道大峡谷隧道出口端斜井掘进完成
- 浙南沿海村村发展有妙招 搭乘共富快车打造“海上花园”
- 新疆霍尔果斯两例无症状感染者新冠病毒均属德尔塔变异株
- 南沙港铁路国庆假期不停工 力争今年年底开通
- 添加陌生人为好友 内蒙古两女子被骗126万
- 中国国庆假期出行热:数字改变“关键小事”
- 水能载物亦能“生金” 浙江遂昌山村以水为媒奔共富
- 铁路人国庆雨中巡查排险记:一身雨衣、一把铁锹保安全畅通
- 铁路迎返程高峰 西安局集团公司加开79趟高铁列车
- 受热带低压影响 琼州海峡北岸等待过海车辆排长龙
- 哈尔滨市学校有序恢复线下教学
- 哈尔滨一地风险等级调整为低风险
- 从进“培训班”到看《长津湖》
- 安徽黄山国庆假期迎客12万余人 旅游市场稳步复苏
- 山西解除持续近90小时的暴雨四级应急响应
- 科学拦峰错峰削峰 嘉陵江洪水过境重庆中心城区“有惊无险”
- 粤高速大湾区路段假期车流集中 跨珠江口通道尤甚
- 千年街区“非遗”风催热国庆假期本地游
- “颜值担当”里的中国,映照“万物和谐”新气象
- 杭州“十一”假期后初中取消统一早读
X 关闭
资讯
X 关闭