- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我非常精通编程,尤其是C ++,但是对于API挂钩和汇编(学习)的概念还是很陌生的。目前,我正在研究dll代理,与link here: ethicalhacker.net上的文章之后的其他方法相比,它应该相当容易。
我设法按照本文中的以下示例代码来使代理dll工作,
__declspec ( naked ) void myGetProcessDefaultLayout(void)
{
HINSTANCE handle;
FARPROC function;
DWORD retaddr;
__asm{
pop retaddr
}
handle = LoadLibraryA("user33.dll");
if(!handle){
MessageBoxA(NULL,"Failed to load user33.dll!","Error",MB_OK | MB_ICONERROR);
ExitProcess(0);
}
function = GetProcAddress(handle,"GetProcessDefaultLayout");
if(!function){
MessageBoxA(NULL,"Failed to load GetProcessDefaultLayout!","Error",MB_OK | MB_ICONERROR);
ExitProcess(0);
}
MessageBoxA(NULL,"GetProcessDefaultLayout called!","Hooked!",MB_OK);
__asm{
call far dword ptr function
push retaddr
retn
}
}
funcA(char* srcBuffer, int srcBuffer_size, char* dstBuffer, int* dstBuffer_size, BOOL AllowCallbacks = TRUE);
最佳答案
任务通常足够复杂,需要一些汇编代码(因此x86 / x64的代码不同)。内联CL汇编器的功能不足以执行此任务(并且不支持x64)-需要使用masm [64]。 Prolog结尾Epilog存根需要在外部asm文件中实现。此存根已调用c ++代码。
x86中的hook 2函数的演示示例(具有__stdcall或__cdecl调用约定。对于__fastcall,还需要在asm存根中保存/恢复ecx,edx)
所以第一个asm代码(编译为ML /c /Cp $(InputName).asm
)
.686p
WSTRING macro name, text
ALIGN 2
name:
FORC arg, text
DW '&arg'
ENDM
DW 0
endm
ASTRING macro name, text
name:
FORC arg, text
DB '&arg'
ENDM
DB 0
endm
BSS segment
imp_CreateFileW DD 0 ; cache original function address
imp_CloseHandle DD 0 ; cache original function address
BSS ends
CONST segment
WSTRING kernel32, <kernel32> ; dllname, share for multiple api
ASTRING CreateFileW, <CreateFileW> ; api name
ASTRING CloseHandle, <CloseHandle> ; api name
CONST ends
_TEXT segment
extern ?CommonStub@@YIPAXPB_WPBDPAPAX2@Z : PROC ; void *__fastcall CommonStub(const wchar_t *,const char *,void **,void **)
?hook_CreateFileW@@YGPAXPB_WKKPAU_SECURITY_ATTRIBUTES@@KKPAX@Z proc
push esp
push offset imp_CreateFileW
mov ecx,offset kernel32
mov edx,offset CreateFileW
call ?CommonStub@@YIPAXPB_WPBDPAPAX2@Z
jmp eax
?hook_CreateFileW@@YGPAXPB_WKKPAU_SECURITY_ATTRIBUTES@@KKPAX@Z endp
?hook_CloseHandle@@YGHPAX@Z proc
push esp
push offset imp_CloseHandle
mov ecx,offset kernel32
mov edx,offset CloseHandle
call ?CommonStub@@YIPAXPB_WPBDPAPAX2@Z
jmp eax
?hook_CloseHandle@@YGHPAX@Z endp
extern ?OnCall@RET_INFO@@QAIHH@Z : PROC ; int __fastcall RET_INFO::OnCall(int)
?retstub@CODE_STUB@@SAXXZ proc
pop ecx
mov edx,eax
call ?OnCall@RET_INFO@@QAIHH@Z
?retstub@CODE_STUB@@SAXXZ endp
_TEXT ends
END
CreateFileW
和
CloseHandle
的函数序言-尽管代码不同-模式对于任何挂钩的api都是通用的(__fastcall除外)-我们将c ++称为序言函数:
PVOID __fastcall CommonStub(PCWSTR DllName, PCSTR FunctionName, void** ppfn, void** Params);
void*
变量的指针,该变量用于保存原始api地址(这是优化,对于调用
LoadLibrary/GetProcAddress
仅一次,然后在顺序调用上使用ready结果),最后指向函数调用堆栈的指针(
Params[0]
是返回地址,
Params[1]
-第一个参数,依此类推)。
CommonStub
必须返回asm存根的原始api地址。第一次调用时,我们用
GetProcAddress
将其保存并保存在
*ppfn
中,然后仅使用保存的值即可。
?retstub@CODE_STUB@@SAXXZ
是常见的返回存根(epilog)。确实,这是函数挂钩中最难的部分。如果我们想在原始api返回后进行控制,则需要它。如果仅在api调用之前有足够的(用于任务)控制,则代码变得更小,更简单。所以对于api返回后的钩子控制-我们显然需要在堆栈中替换返回地址,以获得此控件。但是此后如何返回到原始呼叫者?需要保存原始寄信人地址。但是哪里 ?我们不能为此使用堆栈(没有任何堆栈空间),不能使用非易失性寄存器(如果使用它-在返回原始调用者之前需要保存并恢复-但还是在哪里保存?)。此处唯一的解决方案-分配可执行内存块-在该块中保存原始返回地址(必填),功能参数和名称(可选)-要知道返回时哪个api调用结束,并且在此块中必须是一些与基数无关的小代码-stub-此存根调用我们的asm Epilog-
?retstub@CODE_STUB@@SAXXZ
,并带有指向此可执行内存块的指针。通过使用此指针,我们可以还原原始返回地址,检查api返回值并返回到原始调用方。另请注意-这里我假设单个eax寄存器(x64为rax)中的api返回值对于99%+的api是正确的。但是存在一些返回2个寄存器edx:eax对的api。这种情况当然可以处理,但为简单起见,我在这里不显示(反正代码太大)
#if 1 //0
#define __ASM_FUNCTION __pragma(message(__FUNCDNAME__" proc\r\n" __FUNCDNAME__ " endp"))
#define _ASM_FUNCTION {__ASM_FUNCTION;}
#define ASM_FUNCTION {__ASM_FUNCTION;return 0;}
#define CPP_FUNCTION __pragma(message("extern " __FUNCDNAME__ " : PROC ; " __FUNCSIG__))
#else
#define _ASM_FUNCTION
#define ASM_FUNCTION
#define CPP_FUNCTION
#endif
#if 1
需要在编译时使用以获得c ++装饰的名称(将其粘贴到asm)。并在最终编译构建之前将其替换为
#if 0
。
SLIST_HEADER g_head;
PVOID g_BaseAddress, g_pExport;
class CODE_STUB
{
#ifdef _WIN64
PVOID pad;
#endif
union
{
DWORD code;
struct
{
BYTE cc[3];
BYTE call;
};
};
int offset;
public:
void Init(PVOID stub)
{
code = 0xe8cccccc;// int3; int3; int3; call retstub
offset = RtlPointerToOffset(&offset + 1, stub);
}
PVOID Function()
{
return &call;
}
// implemented in .asm
static void __cdecl retstub() _ASM_FUNCTION;
};
struct RET_INFO
{
union
{
SLIST_ENTRY Entry;
struct
{
PCSTR Name;
PVOID params[7];
};
};
INT_PTR __fastcall OnCall(INT_PTR r);
};
struct RET_FUNC : CODE_STUB, RET_INFO
{
};
#pragma bss_seg(".HOOKS")
RET_FUNC g_rf[1024];//max concurent call count
#pragma bss_seg()
#pragma comment(linker, "/SECTION:.HOOKS,RWE")
class RET_FUNC_Manager
{
SLIST_HEADER _head;
public:
RET_FUNC_Manager()
{
PSLIST_HEADER head = &_head;
InitializeSListHead(head);
RET_FUNC* p = g_rf;
DWORD n = RTL_NUMBER_OF(g_rf);
do
{
p->Init(CODE_STUB::retstub);
InterlockedPushEntrySList(head, &p++->Entry);
} while (--n);
}
RET_FUNC* alloc()
{
return static_cast<RET_FUNC*>(CONTAINING_RECORD(InterlockedPopEntrySList(&_head), RET_INFO, Entry));
}
void free(RET_INFO* p)
{
InterlockedPushEntrySList(&_head, &p->Entry);
}
} g_rfm;
INT_PTR __fastcall RET_INFO::OnCall(INT_PTR r)
{
CPP_FUNCTION;
*(void**)_AddressOfReturnAddress() = *params;
g_rfm.free(this);
return r;
}
PVOID __fastcall CommonStub(PCWSTR DllName, PCSTR FunctionName, void** ppfn, void** Params)
{
CPP_FUNCTION;
//++ optional, hook return
if (RET_FUNC* p = g_rfm.alloc())
{
p->Name = FunctionName;
// memcpy(p->params, Params, sizeof(p->params)); // save original return address and params
PVOID StackBase = reinterpret_cast<PNT_TIB>(NtCurrentTeb())->StackBase;
PVOID ParamsBase = Params + RTL_NUMBER_OF(p->params);
ParamsBase = min(StackBase, ParamsBase);
memcpy(p->params, Params, RtlPointerToOffset(Params, ParamsBase));
*Params = p->Function();// replace return address
}
//-- optional
PVOID pfn = *ppfn;
if (!pfn)
{
if (pfn = GetProcAddress(LoadLibraryW(DllName), FunctionName))
{
*ppfn = pfn;
}
else
{
__debugbreak();
}
}
return pfn;
}
RET_FUNC
(此缓冲区的结构)并在PE主体中进行预分配:
#pragma bss_seg(".HOOKS")
RET_FUNC g_rf[1024];//max concurent call count
#pragma bss_seg()
#pragma comment(linker, "/SECTION:.HOOKS,RWE")
g_rf[1024];
,
SLIST_HEADER
(用于分配条目)和
InterlockedPopEntrySList
(用于免费条目)将
InterlockedPushEntrySList
的数组推入无锁堆栈结构。这是最快,最有效的。
CommonStub
-在这里我们可以在调用和可选的钩子返回(
*Params = p->Function();
)之前检查函数参数。
INT_PTR __fastcall RET_INFO::OnCall(INT_PTR r)
-此处
r
是寄存器大小api返回值(来自eax或rax)。在
RET_INFO
类中,存在有关api调用的所有必需信息。在这里,我们可以查看返回值,api名称,保存调用堆栈。但是,在此演示代码中,我仅执行强制性任务:恢复返回地址
*(void**)_AddressOfReturnAddress() = *params;
(通过此技巧,我们在返回后直接返回到原始api调用者,而不返回到我们的asm存根Epilog)
_AddressOfReturnAddress
是CL固有的(因此不受其他编译器支持,但我想它们具有某些等效功能)。最后我们释放(推入堆栈)已分配的可执行内存块-
g_rfm.free(this);
。函数return
r
-api调用结果(再次注意,我猜api使用单个寄存器)。从
INT_PTR __fastcall RET_INFO::OnCall(INT_PTR r)
返回之后-我们将处于原始调用者代码中,并且正确的堆栈和api返回值以eax(rax)表示。但是,如果需要,我们可以不返回
r
而是另一个值-因此请更改api调用结果。
ml64 /c /Cp /Zd $(InputFileName) -> $(InputName).obj
WSTRING macro name, text
ALIGN 2
name:
FORC arg, text
DW '&arg'
ENDM
DW 0
endm
ASTRING macro name, text
name:
FORC arg, text
DB '&arg'
ENDM
DB 0
endm
BSS segment
imp_CreateFileW DQ 0 ; cache original function address
imp_CloseHandle DQ 0 ; cache original function address
BSS ends
CONST segment
WSTRING kernel32, <kernel32> ; dllname, share for multiple api
ASTRING CreateFileW, <CreateFileW> ; api name
ASTRING CloseHandle, <CloseHandle> ; api name
CONST ends
_TEXT segment
extern ?OnCall@RET_INFO@@QEAA_J_J@Z : PROC ; __int64 __cdecl RET_INFO::OnCall(__int64)
?retstub@CODE_STUB@@SAXXZ proc
pop rcx
mov rdx,rax
call ?OnCall@RET_INFO@@QEAA_J_J@Z
?retstub@CODE_STUB@@SAXXZ endp
extern ?CommonStub@@YAPEAXPEB_WPEBDPEAPEAX2@Z : PROC ; void *__cdecl CommonStub(const wchar_t *,const char *,void **,void **)
?hook_CreateFileW@@YAPEAXPEB_WKKPEAU_SECURITY_ATTRIBUTES@@KKPEAX@Z proc
mov [rsp+32],r9
mov [rsp+24],r8
mov [rsp+16],rdx
mov [rsp+8],rcx
mov r9,rsp
lea r8,imp_CreateFileW
lea rdx,CreateFileW
lea rcx,kernel32
sub rsp,40
call ?CommonStub@@YAPEAXPEB_WPEBDPEAPEAX2@Z
add rsp,40
mov rcx,[rsp+8]
mov rdx,[rsp+16]
mov r8,[rsp+24]
mov r9,[rsp+32]
jmp rax
?hook_CreateFileW@@YAPEAXPEB_WKKPEAU_SECURITY_ATTRIBUTES@@KKPEAX@Z endp
?hook_CloseHandle@@YAHPEAX@Z proc
mov [rsp+32],r9
mov [rsp+24],r8
mov [rsp+16],rdx
mov [rsp+8],rcx
mov r9,rsp
lea r8,imp_CloseHandle
lea rdx,CloseHandle
lea rcx,kernel32
sub rsp,40
call ?CommonStub@@YAPEAXPEB_WPEBDPEAPEAX2@Z
add rsp,40
mov rcx,[rsp+8]
mov rdx,[rsp+16]
mov r8,[rsp+24]
mov r9,[rsp+32]
jmp rax
?hook_CloseHandle@@YAHPEAX@Z endp
_TEXT ends
end
RET_INFO
-
PVOID params[7];
-这允许保存(用于api调用后最多6个参数(在params [0]中将是返回地址))。但是我们可以重新定义
PVOID params[15];
-最多将使用14个参数。
memcpy(p->params, Params, sizeof(p->params));
PVOID StackBase = reinterpret_cast<PNT_TIB>(NtCurrentTeb())->StackBase;
PVOID ParamsBase = Params + RTL_NUMBER_OF(p->params);
ParamsBase = min(StackBase, ParamsBase);
memcpy(p->params, Params, RtlPointerToOffset(Params, ParamsBase));
#if defined(_M_IX86)
#define __movsp __movsd
#elif defined (_M_X64)
#define __movsp __movsq
#else
#error
#endif
__movsp((PULONG_PTR)p->params,
(PULONG_PTR)Params,
RtlPointerToOffset(Params, ParamsBase)/ sizeof(ULONG_PTR));
class CODE_STUB
{
#ifdef _WIN64
PVOID pad;// for what ?
#endif
SLIST_ENTRY
必须对齐16个字节。它使用
DECLSPEC_ALIGN(16)
在winnt.h中声明。结果
RET_INFO
(包含
SLIST_ENTRY
)并从中继承
struct RET_FUNC : CODE_STUB, RET_INFO {}
将对齐16个字节。一定是:
C_ASSERT(__alignof(RET_FUNC)==16);
PVOID pad;
的开头加上和不加上
CODE_STUB
。但是我的代码隐式使用(需要)
C_ASSERT(sizeof(CODE_STUB) == RTL_SIZEOF_THROUGH_FIELD(CODE_STUB, offset));
C_ASSERT(FIELD_OFFSET(RET_FUNC, Entry)==sizeof(CODE_STUB));// !!
CODE_STUB
(
offset
成员的结尾)和
RET_INFO
的开始之间没有填充-在
CODE_STUB
-
call offset
指令和返回地址中,压入堆栈的是..必须指向
RET_INFO
的指针是-我从堆栈中弹出返回地址,并用作调用成员函数
RET_INFO
的指向
RET_INFO::OnCall
的指针:
?retstub@CODE_STUB@@SAXXZ proc
pop rcx ; -> RET_INFO
mov rdx,rax
call ?OnCall@RET_INFO@@QEAA_J_J@Z
?retstub@CODE_STUB@@SAXXZ endp
PVOID pad
-
CODE_STUB
是8字节(3 * 1字节(int 3)+ 5字节相对调用偏移量),但是
RET_INFO
(由于16字节
SLIST_ENTRY Entry;
成员对齐),将从
RET_FUNC
和
CODE_STUB
之间的
CODE_STUB
末尾:
RET_INFO
将。为避免这种情况-需要显式添加8字节填充,但必须以
RET_FUNC : CODE_STUB, /* 8 byte pad/ RET_INFO
开头。这一切都是正确的。请注意,对于替换原始寄信人地址,我们使用
*Params = p->Function()
PVOID Function()
{
return &call;
}
CODE_STUB
中的呼叫偏移指令的返回地址(而不是
CODE_STUB
的地址)-因此,此正确处理开始时的任何填充-我们无论如何都获得了正确的地址或返回存根
关于winapi - 如何准备序言和结语汇编以拦截具有参数的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47839146/
简而言之:我想从可变参数模板参数中提取各种选项,但不仅通过标签而且通过那些参数的索引,这些参数是未知的 标签。我喜欢 boost 中的方法(例如 heap 或 lockfree 策略),但想让它与 S
我可以对单元格中的 excel IF 语句提供一些帮助吗? 它在做什么? 对“BaselineAmount”进行了哪些评估? =IF(BaselineAmount, (Variance/Baselin
我正在使用以下方法: public async Task Save(Foo foo,out int param) { ....... MySqlParameter prmparamID
我正在使用 CodeGear RAD Studio IDE。 为了使用命令行参数测试我的应用程序,我多次使用了“运行 -> 参数”菜单中的“参数”字段。 但是每次我给它提供一个新值时,它都无法从“下拉
我已经为信用卡类编写了一些代码,粘贴在下面。我有一个接受上述变量的构造函数,并且正在研究一些方法将这些变量格式化为字符串,以便最终输出将类似于 号码:1234 5678 9012 3456 截止日期:
MySql IN 参数 - 在存储过程中使用时,VarChar IN 参数 val 是否需要单引号? 我已经像平常一样创建了经典 ASP 代码,但我没有更新该列。 我需要引用 VarChar 参数吗?
给出了下面的开始,但似乎不知道如何完成它。本质上,如果我调用 myTest([one, Two, Three], 2); 它应该返回元素 third。必须使用for循环来找到我的解决方案。 funct
将 1113355579999 作为参数传递时,该值在函数内部变为 959050335。 调用(main.c): printf("%d\n", FindCommonDigit(111335557999
这个问题在这里已经有了答案: Is Java "pass-by-reference" or "pass-by-value"? (92 个回答) 关闭9年前。 public class StackOve
我真的很困惑,当像 1 == scanf("%lg", &entry) 交换为 scanf("%lg", &entry) == 1 没有区别。我的实验书上说的是前者,而我觉得后者是可以理解的。 1 =
我正在尝试使用调用 SetupDiGetDeviceRegistryProperty 的函数使用德尔福 7。该调用来自示例函数 SetupEnumAvailableComPorts .它看起来像这样:
我需要在现有项目上实现一些事件的显示。我无法更改数据库结构。 在我的 Controller 中,我(从 ajax 请求)传递了一个时间戳,并且我需要显示之前的 8 个事件。因此,如果时间戳是(转换后)
rails 新手。按照多态关联的教程,我遇到了这个以在create 和destroy 中设置@client。 @client = Client.find(params[:client_id] || p
通过将 VM 参数设置为 -Xmx1024m,我能够通过 Eclipse 运行 Java 程序-Xms256M。现在我想通过 Windows 中的 .bat 文件运行相同的 Java 程序 (jar)
我有一个 Delphi DLL,它在被 Delphi 应用程序调用时工作并导出声明为的方法: Procedure ProduceOutput(request,inputs:widestring; va
浏览完文档和示例后,我还没有弄清楚 schema.yaml 文件中的参数到底用在哪里。 在此处使用 AWS 代码示例:https://github.com/aws-samples/aws-proton
程序参数: procedure get_user_profile ( i_attuid in ras_user.attuid%type, i_data_group in data_g
我有一个字符串作为参数传递给我的存储过程。 dim AgentString as String = " 'test1', 'test2', 'test3' " 我想在 IN 中使用该参数声明。 AND
这个问题已经有答案了: When should I use "this" in a class? (17 个回答) 已关闭 6 年前。 我运行了一些java代码,我看到了一些我不太明白的东西。为什么下
我输入 scroll(0,10,200,10);但是当它运行时,它会传递字符串“xxpos”或“yypos”,我确实在没有撇号的情况下尝试过,但它就是行不通。 scroll = function(xp
我是一名优秀的程序员,十分优秀!