(C++/ASM)不使用API获得所有加载DLL的函数入口
老技术了,基本上加密壳人手一个吧……学习结束后暂时告一段落,把这一段程序扔上来记录一下。也方便以后用主要使用的是c++ + 内联汇编。其实我是想用纯c++的,但是ntdef.h总是和windows.h/winnt.h冲突,只能引用一个太麻烦了。最后就还是用汇编写了。
这个功能的主要实现方法是fs:永远指向TEB(线程环境块),通过读取TEB的0x30处,可以获得当前进程的进程环境块(PEB),PEB的0xC是进程的LDR,保存了所有加载过的DLL信息。
LDR中有三个字段指向了LDR_DATA_TABLE的双向链表,通过双向链表可以得到所有dll的加载基址和dll名称;
得到dll加载基址后,再根据加载入内存中相应dll的PE头结构,可以获取dll的输出表信息。对输出表进行遍历,就可以获得所有函数的入口VA和名称了。
这一部分程序忽略了只能按序号索引的地址,所以……就这样吧。东西都是老东西了,写出来练练手。
我说的比较粗糙,对相关知识比较迷茫的同学可以参考一下这几篇文章,我感觉写得非常好
https://www.52pojie.cn/thread-684432-1-1.html
https://www.52pojie.cn/thread-685272-1-1.html
最后放代码。内联汇编为主,就染色成ASM了。可惜这个染色器不支持//注释
#include <ntdef.h>;
#include <iostream>;
#include <string>;
void GetAllFuncs(unsigned int* intEntry);
int main() {
_UNICODE_STRING* lpBaseDllName = {};
unsigned int* intBaseAddr = {};
__asm {
mov eax, fs: //TEB -> PEB
add eax, 0ch
mov eax, //PEB -> LDR
add eax, 01ch
mov eax, // LDR -> InInitializationOrderModuleList
mov edx, eax //Save the first entry
LIST_ENTRY_RET:
mov ecx, //DllBase(DOS HEADER)
mov intBaseAddr, ecx
lea ecx,
mov lpBaseDllName, ecx //*BaseDllName *Note, normally kernel32.dll is at the second turn.
mov eax,
cmp eax, edx
je RETNOW
pushad
}
std::wcout << "DllName: " << lpBaseDllName->Buffer << std::hex << ", Entry: " << intBaseAddr << std::endl;
GetAllFuncs(intBaseAddr);
_asm {
popad
jmp LIST_ENTRY_RET
RETNOW:
}
}
void GetAllFuncs(unsigned int *intEntry) {
char* lpNameOfFunction = {};
unsigned int* intAddrOfFunction = {};
__asm {
mov eax, intEntry
mov ebx, //DOS_HEADER.e_lfanew
add eax, ebx
//now eax points to PE
mov edx, 078h // PE + 078h = DataDirectory in x86(See IMAGE_NT_HEADERS32 struct)
mov bx, //Magic, 010Bh for x86, 020Bh for x64 (x86/64 for the HEADER only)
cmp bx, 020Bh
jnz x86_next
add edx, 010h //IMAGE_NT_HEADERS64 has more 10bytes than HEADERS32
x86_next :
mov eax, //Export Table DATA_DIRECTORY
//mov eax, //RVA of Export Table
mov ebx, intEntry
add ebx, eax //ebx points to IMAGE_EXPORT_DIRECTORY
//mov ecx, //NumberOfNames
//mov eax, //AddressOfFunctions
//mov edx, //AddressOfNameOrdinals
// //AddressOfName
xor edi, edi
GO_ON_EXPORT :
imul eax, edi, 4
mov edx, //RVA Address
add edx, intEntry
mov esi, //AddressOfName
add esi, intEntry
mov lpNameOfFunction, esi
imul eax, edi, 2
mov edx,
add edx, intEntry
movzx esi, word ptr //AddressOfNameOrdinals
imul esi, esi, 4
mov edx,
add edx, intEntry
mov ecx,
add ecx, intEntry
mov intAddrOfFunction, ecx
pushad
}
std::cout << "Name: " << lpNameOfFunction << ", Address: " << std::hex << intAddrOfFunction << std::endl;
__asm{
popad
inc edi
cmp edi,
jnz GO_ON_EXPORT
}
}
q2510908331 发表于 2020-6-30 11:57
内联汇编完全可以使用较少的汇编来获取主要结构
https://www.nirsoft.net/kernel_struct/vista/PEB. ...
谢谢!我也觉得定义一下会好看很多……一时脑抽(
没有使用结构主要是当时想用ntdef.h里面的_UNICODE_STRING读取dllname结构。如果引用包含有PE结构的<windows.h>的话,就重定义了……单独拎出来定义又懒(现在想来费力不讨好,就一个结构cp一下又不是很难)
其实真要偷懒的话不用_UNICODE_STRING,直接用wchar_t去读UNICODE_STRING后面的那个串是更好的策略 本帖最后由 q2510908331 于 2020-6-30 11:59 编辑
内联汇编完全可以使用较少的汇编来获取主要结构
https://www.nirsoft.net/kernel_struct/vista/PEB.html
https://www.nirsoft.net/kernel_struct/vista/TEB.html
https://www.nirsoft.net/kernel_struct/vista/LDR_DATA_TABLE_ENTRY.html
定义好结构在定义PE结构多好.你这样写可读性差.不过如果学习内联汇编写法还行. 真正的要使用还是定义为结构.
遍历结构进行操作. 你这种作为ShellCode还是不错 学习了,支持发帖,支持原创 竟然是前排,仰望大佬 厉害,谢谢分享。 前排支持。用到回来看。 不愧是大神,直接用汇编写了,我都没写过汇编 太吊了,完全看不懂 风扫春残雪 发表于 2020-6-30 12:09
谢谢!我也觉得定义一下会好看很多……一时脑抽(
没有使用结构主要是当时想用ntdef.h里面的_UNICODE_ ...
是的呀哈哈.你这样做的话 做ShellCode还行. 真要以后自己用自己都不爱维护的.
页:
[1]
2