CS-Shellcode分析入门 第三课

admin 2021年7月25日22:14:36评论107 views字数 5384阅读17分56秒阅读模式

点击蓝字

CS-Shellcode分析入门 第三课

关注我们



声明

本文作者:Gality

本文字数:3100

阅读时长:20~30分钟

附件/链接:点击查看原文下载

本文属于【狼组安全社区】原创奖励计划,未经许可禁止转载


由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,狼组安全团队以及文章作者不为此承担任何责任。

狼组安全团队有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经狼组安全团队允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。






前言


        本文是CS的shellcode分析的第三篇文章,该系列文章旨在帮助具有一定二进制基础的朋友看懂cs的shellcode的生成方式,进而可以达到对shellcode进行二进制层面的改变与混淆,用于免杀相关的研究。


实现的一个免杀加载器 https://github.com/wgpsec/CS-Avoid-killing


CS-Shellcode分析系列 第一课

CS-Shellcode分析入门 第二课



一、

Shellcode分析

同样是接上文,上文提到726774C是函数LoadLibraryA的特征值,这一点我们也可以在动态调试中验证

CS-Shellcode分析入门 第三课

可以看到rsi指向的就是LoadLibraryA这个函数名,此时我们已经有了函数地址,可以进行函数调用了,我们看接下来的操作

CS-Shellcode分析入门 第三课

将之前的栈顶的值pop到rax中,也就是dll的导出表的地址,[rax+24]则是AddressOfNameOrdinals的值,该值是储存函数序号的RVA,那么同样,加rdx(dll基地址)得到实际内存中的地址,我们之前说过AddressOfNameOrdinals表的元素宽度为2,所以[r8+rcx*2]取到了LoadLibraryA对应的序号,至于为什么要取到函数序号呢,这里要讲一下导出函数在dll中是什么存储的

引用博客:https://blog.csdn.net/Apollon_krj/article/details/77337333

导出表有三个子表,也就是AddressOfFunctions, AddressOfNames和AddressOfNameOrdinals这三个,AddressOfFunctions中存储的是导出函数的地址表,每一项为4字节,AddressOfNames中储存的是函数名字符串的地址,每一项也是4字节,AddressOfNameOrdinals中存储的是函数序号的地址,每一项为2字节,这三个子表中存储的地址都是RVA,也就是说要加上dll的基地址才是在内存中的真实地址。这三个子表中的名字表和序号表是相互对应的,但是地址表和另外两个不一定是对应的,导出函数一定有地址,但是不一定有名字(声明为noname的),但一个函数也可以对应多个名字,不过一般情况下,名字表均不会大于地址表。

比如我们自定义一个dll库,在导出时采用.def文件的方式导出:

//dll.def
EXPORTS
Plus @1
Sub @3 NONAME
div @5 NONAME
mul @6

导出的函数有两个函数我们声明为noname,故其在导出表中不存在名字。则其导出表的NumberOfNames = 2,NumberOfFunctions = (6-1+1) = 6。即地址表长度为6宽度为4,Size为24;名字表长度为2,宽度为4,Size为8;序号表长度为2,宽度为2,Size为4。对应的内存图如下(名字表的地址是按照从小到大排的,地址表有与我们.def中指定了序号,因此是乱序的,如果不指定,编译器自动分配的一般也是有序的)

![这里写图片描述]CS-Shellcode分析入门 第三课

有一点需要注意AddressOfNameOrdinals指向的序号表中的值是非准确的,应该均加上Base才是真正的序号(Base等于序号表中最小的值)

取到了函数序号后,就可以找到函数的真实地址了,但还要找到地址表的地址,故mov r8d, [rax+1Ch]取到地址表RVA后,再add r8, rdx得到真实地址,后面再mov eax, [r8+rcx*4]LoadLibraryA的地址(RVA)放入eax中,add rax, rdx将函数的实际地址存入rax

接着是一连串的pop,这里的操作其实就是在布置函数参数了,因为后面jmp rax相当于一个函数调用,只需要根据x64的调用约定及函数定义将参数手动布置好,就相当于是完成一次函数调用了。

在x64中,参数的传递法则为:第一个参数放rcx,第二个参数放rdx,然后依次为r8r9, 更多的参数放堆栈

那我们看他传递的参数是什么,我们只需反向找push就可以了,中间只有push和pop操作会影响到栈,这个师傅们可以自行寻找,我直接说了,这里的pop和上面最开始的push是一一对应的,由于LoadLibraryA其实只需一个参数,所以真正有用的其实就是rcx寄存器,对应wininet的字符串,所以此时相当于是执行LoadLibraryA('wininet'), 将wininet.dll 加载进内存, r10中存储的是返回到CS-Shellcode分析入门 第三课

的返回地址,这一步push+jmp相当于是手动call了,所以LoadLibraryA执行完会返回到箭头处,而我们可以发现,到call rbp这一部分的代码和上面的其实差不多,同样是遍历dll,不同的是这次遍历的dll中多了wininet.dll, 这次寻找的函数的特征值是0A779563Ah, 对应着InternetOpenA函数,函数原型如下:

void InternetOpenA(  LPCSTR lpszAgent,  DWORD  dwAccessType,  LPCSTR lpszProxy,  LPCSTR lpszProxyBypass,  DWORD  dwFlags);

传递的参数值均为0, 根据微软的描述,该函数是应用程序调用的第一个WinINet函数。它告诉Internet DLL初始化内部数据结构,并为将来来自应用程序的调用做准备。当应用程序完成Internet功能使用后,应调用InternetCloseHandle释放句柄和所有相关资源。

如果成功调用,则返回传递给后续WinINet函数的有效句柄,否则返回NULL,执行完成后返回到箭头位置,返回值通过rax返回

CS-Shellcode分析入门 第三课

再然后就是一路跳转到这里

CS-Shellcode分析入门 第三课

大眼一看,这不是类似的套路嘛,我们看这次的参数,pop rdx是刚刚call过来的返回地址,其实就是我们shellcode中设置的IP地址

CS-Shellcode分析入门 第三课

rcx对应刚刚InternetOpenA执行后返回的资源句柄,r8d是20020(端口号),栈上是0,0,3,0,0执行函数的特征值是0C69F8957h,该特征值对应的函数为InternetConnectW,函数原型如下:

void InternetConnectW(  HINTERNET     hInternet,  LPCWSTR       lpszServerName,  INTERNET_PORT nServerPort,  LPCWSTR       lpszUserName,  LPCWSTR       lpszPassword,  DWORD         dwService,  DWORD         dwFlags,  DWORD_PTR     dwContext);

返回值:如果连接成功,则返回会话的有效句柄,否则返回NULL

传递的参数为:

rcx: 返回的句柄
rdx: IP地址
r8: 20020
r9: 0
rsp+28:0
rsp+30:3
rsp+38:0
rsp+40:0

(这里是从动态调试中看的,至于为什么是从rsp+28开始布置栈上参数没有找到相关的资料= =有知道的师傅请务必留言赐教Orz)

CS-Shellcode分析入门 第三课

同样的套路,布置参数,通过特征值找到函数并调用,布置的参数我们后面一起说

3B2E55EBh对应的函数为HttpOpenRequestW,函数原型为:

void HttpOpenRequestW(  HINTERNET hConnect,  LPCWSTR   lpszVerb,  LPCWSTR   lpszObjectName,  LPCWSTR   lpszVersion,  LPCWSTR   lpszReferrer,  LPCWSTR   *lplpszAcceptTypes,  DWORD     dwFlags,  DWORD_PTR dwContext);

该函数用于创建一个http请求句柄,返回值:如果成功为http请求句柄,否则为NULL

HttpOpenRequest函数创建一个新的HTTP请求句柄并将指定的参数存储在该句柄中。HTTP请求句柄保存要发送到HTTP服务器的请求,并包含要作为请求的一部分发送的所有RFC822 / MIME / HTTP标头。

传递的参数为:

1: rcx InternetConnectW返回的HTTP session句柄
2: rdx 0000000000000000 0的话默认使用GET请求
3: r8 0000021003970186 "/qq6E" 一个描述你请求资源的字符串,当请求一个默认页面时令这个参数指向一个空串
4: r9 0000000000000000 HTTP 版本,这个参数为 NULL 时,默认使用""HTTP/1.1""
5: [rsp+28] 0000000000000000
6: [rsp+30] 0000000000000000
7: [rsp+38] FFFFFFFF84400200
8: [rsp+40] 0000000000000000

然后就是下面的代码:

CS-Shellcode分析入门 第三课

add rbx, 50h之后,rbx就指向了user-agent:

CS-Shellcode分析入门 第三课

然后常规操作不再细说,7B18062Dh对应着HttpSendRequestA ,函数原型如下:

BOOLAPI HttpSendRequestA(  HINTERNET hRequest,  LPCSTR    lpszHeaders,  DWORD     dwHeadersLength,  LPVOID    lpOptional,  DWORD     dwOptionalLength);

该函数将之前的请求发送到http服务器上,成功则返回True,失败返回False,布置的参数为:

1: rcx 0000000000CC000C 
2: rdx 00000210039701D6 "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)rn"
3: r8 FFFFFFFFFFFFFFFF
4: r9 0000000000000000
5: [rsp+28] 00000210039701D6 "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)rn"

然后就是根据是否成功发送请求进行分支跳转,如果 没有成功发送请求,则执行如下代码重试,最多重试10次,然后就会进这里

CS-Shellcode分析入门 第三课

我们看由于没有给r10赋值,所以会触发异常,程序就中断了

而如果成功执行的话,会执行如下代码:

CS-Shellcode分析入门 第三课

这里看这个参数,如果做过手动加载shellcode的师傅估计一眼就能看出来这是virtualAlloc函数的参数,该函数的函数原型如下:

LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);

在调用进程的虚拟地址空间中保留,提交或更改页面区域的状态。此功能分配的内存将自动初始化为零。如果成功执行,则返回申请的空间页的基地址,否则返回NULL

函数参数为:

1: rcx 0000000000000000 
2: rdx 0000000000400000
3: r8 0000000000001000
4: r9 0000000000000040

申请过空间后,就是

CS-Shellcode分析入门 第三课

调用InternetReadFile函数,原型:

BOOLAPI InternetReadFile(
HINTERNET hFile,
LPVOID lpBuffer,
DWORD dwNumberOfBytesToRead,
LPDWORD lpdwNumberOfBytesRead
);

从InternetOpenUrl,FtpOpenFile或HttpOpenRequest函数打开的句柄中读取数据,成功则返回True,失败则为false

参数为:

1: rcx 之前的资源句柄
2: rdx 000001C58E180000
3: r8 0000000000002000
4: r9 000000822B32F250

然后就是执行复制出来的代码了

CS-Shellcode分析入门 第三课

整个cs自动生成的shellcode分析就结束了,我们可以发现其实这段shellcode只是完成了从cs的teamserver上下载代码执行的功能,真正的恶意代码还需要接着分析,当然通过已有的分析,我们已经可以做到对shellcode做到二进制层面的修改混淆,甚至可以加任意的无关代码,想怎样就怎么,极大程度的规避杀软通过特征码识别恶意程序的查杀方式,至于真正的恶意代码,我们后续在接着分析。


后记



有想一起研究免杀技术或者二进制技术的师傅萌,简历请砸[email protected],欢迎各位师傅一起交流学习


作者



CS-Shellcode分析入门 第三课

Gality

过去的很久没更新了,未来会补上




扫描关注公众号回复加群

和师傅们一起讨论研究~


WgpSec狼组安全团队

微信号:wgpsec

Twitter:@wgpsec


CS-Shellcode分析入门 第三课
CS-Shellcode分析入门 第三课


本文始发于微信公众号(WgpSec狼组安全团队):CS-Shellcode分析入门 第三课

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年7月25日22:14:36
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CS-Shellcode分析入门 第三课http://cn-sec.com/archives/343089.html

发表评论

匿名网友 填写信息