我如何 运行 字符串变量中的 ASM 操作码或将其转换为字节?
How can i run ASM opcode in a string variable Or convert it to bytes?
我在 字符串属性中有这段 asm 代码:
mov [ecx+ebx*4+10],ff
jmp client.dll+3A8D96
在进入我想要实现的目标之前,首先我会将 client.dll+... 更改为它的最终地址。
现在我正在努力实现的是:
将那段 asm 代码转换成它的 等效字节 这样我就可以在使用 winapi 更改它们后将这些字节写回到内存中..
或者以某种方式将 字符串的内容 写入分配的内存中,如果不将其转换为字节,这似乎是不可能的..idk
不知道可不可以,我也有这个想法:
- 用 c++ 创建一个 dll,它将具有可以从我的 c# 应用程序导入的函数
- 通过传递 asm 操作码和分配的内存地址作为参数(不是 c++ 专家)使用任何可能的方法来实现我的目标,但我知道内联 asm 在 c++ 中是可能的。
请记住,此 post 中给出的 ASM 代码只是为了演示我正在尝试做的事情,它实际上会发生很大变化,我希望你能理解。
如果您使用的是 c#,我建议您使用包装器来写入进程内存并导入必要的 dll 等,并获取您的 asm 操作码的原始字节并根据您自己的意愿操作它们
如果您使用的是 C++,则可以直接使用函数而无需获取包装器等;大多数函数都在 C++ 库中
如果你正在寻找像作弊引擎代码洞穴(带有跳跃和回跳的 aob 注入)之类的东西,我会考虑你花一些时间来学习一些关于内存操作/分配内存/asm 通常如何工作的东西,之后你可以安心乱记
对于使用 C++ 的内部 dll,您也需要使用原始字节,如果我没记错的话,他们从 visual studio 2010 年删除了代码洞等的 asm 功能,并将其限制为企业版或其他版本
如果你考虑按原样使用这个操作码
jmp client.dll+3A8D96
如果你知道 asm,那将是一个非常糟糕的主意,如果它是近距离跳跃,这没什么大不了的,但这些偏移量可能会不时改变
"\xFE\xC8\x5B\x23\xC1"
这只是我如何将字节用于我的代码的示例,youtube 上有一些很好的教程,您应该查看它们
这远不是您的完整问题 (C#...),但它可以为您提供一些线索。
此示例是在 Linux-64 上使用 gcc 在 C 中完成的;你可能需要适应它
你的平台。
第一步是用一些内联汇编(gnu-asm
语法)以查看它与 objdump 的外观(它从未被调用
实际上)。
跳转到常量地址的技巧是在这里找到的:
之后我们必须创建一个可执行内存段并填充
它的字节灵感来自上一个函数(感谢 objdump)。
如果唯一可能改变的是地址,那么我们就覆盖
前面代码中的这个地址。
在这个例子中,另一个函数的地址用于此目的。
那么这段可执行内存段可以认为是一个函数
(嗯...希望)我们通过函数指针使用它。
而且它似乎有效!
我能看到的唯一问题是指令movb [=11=]xff,0xa(%ecx,%ebx,4)
这做了坏事,因为我不知道寄存器到底是什么
应该包含。
我决定用六个 nop
代替这条指令来占据同一个地方
并保持此示例与原始问题相似。
(我想在你的问题的背景下,这些寄存器会有一个
相关值)。
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#undef __STRICT_ANSI__ // for MAP_ANONYMOUS
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
void
target_function(void)
{
printf("~~~~ %s ~~~~\n", __func__);
}
void
inline_asm_example(void)
{
__asm__ __volatile__(
"\tnop\n"
"\tmovb [=10=]xff,0xa(%ecx,%ebx,4)\n"
"\tjmpq *0x0(%rip)\n"
".quad 0xAA00BB11CC22DD33\n");
// the jump relative to rip is inspired from
//
}
int
main(void)
{
// create an executable page
void *page=mmap(NULL, 6+6+8,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(page==MAP_FAILED)
{
fprintf(stderr, "cannot allocate memory page\n");
return 1;
}
// initialise code pattern
// objdump --disassemble=inline_asm_example prog
char *code_pattern=(char *)page;
#if 0 // this instruction causes something wrong
memcpy(code_pattern+0,
"\x67\xc6\x44\x99\x0a\xff", 6); // movb [=10=]xff, 10(%ecx,%ebx,4)
#else // use some useless instructions instead
memcpy(code_pattern+0,
"\x90\x90\x90\x90\x90\x90", 6); // 6x nop
#endif
memcpy(code_pattern+6,
"\xff\x25\x00\x00\x00\x00", 6); // jmpq *0x0(%rip)
// insert into the pattern the address we want to jump to
ptrdiff_t target_address=(ptrdiff_t)target_function;
memcpy(code_pattern+6+6, &target_address, sizeof(target_address));
// consider the code pattern as a function
void (*fnct_ptr)(void)=NULL;
memcpy(&fnct_ptr, &code_pattern, sizeof(code_pattern));
// here we go
printf("about to do something I will probably regret...\n");
fnct_ptr();
printf("ah? it was not so painful after all!\n");
// let's forget everything about that
munmap(code_pattern, 6+6+8);
return 0;
}
使用Keystone's C# bindings 将程序集转换为字节。它非常容易使用,你给它一个字符串,它给你一个字节数组,代表你输入的汇编代码:
using Keystone;
using (Engine keystone = new Engine(Architecture.X86, Mode.X32) { ThrowOnError = true })
{
ulong address = 0;
keystone.ResolveSymbol += (string s, ref ulong w) =>
{
if (s == "_j1")
{
w = 0x1234abcd;
return true;
}
return false;
};
EncodedData enc = keystone.Assemble("xor eax, eax; jmp _j1", address);
enc.Buffer.ShouldBe(new byte[] { 0x00 });
enc.Address.ShouldBe(address);
enc.StatementCount.ShouldBe(3);
}
要将作弊引擎代码注入脚本转换为 C# 代码,您需要进行外部绕行。使用 VirtualAllocateEx()
在目标进程中获取 space,使用 WriteProcessMemory
将您使用 KeyStone 创建的 shell 代码写入内存,然后执行绕行到绕行执行流程到您的 shell你注入的代码。
您需要在绕行函数中手动解决相对跳转和所有相对地址。
或者,您可以使用 CreateRemoteThread()
在新线程中执行目标进程中的代码,而不是绕道而行,具体取决于您想要做什么。
我在 字符串属性中有这段 asm 代码:
mov [ecx+ebx*4+10],ff
jmp client.dll+3A8D96
在进入我想要实现的目标之前,首先我会将 client.dll+... 更改为它的最终地址。
现在我正在努力实现的是:
将那段 asm 代码转换成它的 等效字节 这样我就可以在使用 winapi 更改它们后将这些字节写回到内存中..
或者以某种方式将 字符串的内容 写入分配的内存中,如果不将其转换为字节,这似乎是不可能的..idk
不知道可不可以,我也有这个想法:
- 用 c++ 创建一个 dll,它将具有可以从我的 c# 应用程序导入的函数
- 通过传递 asm 操作码和分配的内存地址作为参数(不是 c++ 专家)使用任何可能的方法来实现我的目标,但我知道内联 asm 在 c++ 中是可能的。
请记住,此 post 中给出的 ASM 代码只是为了演示我正在尝试做的事情,它实际上会发生很大变化,我希望你能理解。
如果您使用的是 c#,我建议您使用包装器来写入进程内存并导入必要的 dll 等,并获取您的 asm 操作码的原始字节并根据您自己的意愿操作它们
如果您使用的是 C++,则可以直接使用函数而无需获取包装器等;大多数函数都在 C++ 库中
如果你正在寻找像作弊引擎代码洞穴(带有跳跃和回跳的 aob 注入)之类的东西,我会考虑你花一些时间来学习一些关于内存操作/分配内存/asm 通常如何工作的东西,之后你可以安心乱记
对于使用 C++ 的内部 dll,您也需要使用原始字节,如果我没记错的话,他们从 visual studio 2010 年删除了代码洞等的 asm 功能,并将其限制为企业版或其他版本
如果你考虑按原样使用这个操作码
jmp client.dll+3A8D96
如果你知道 asm,那将是一个非常糟糕的主意,如果它是近距离跳跃,这没什么大不了的,但这些偏移量可能会不时改变
"\xFE\xC8\x5B\x23\xC1"
这只是我如何将字节用于我的代码的示例,youtube 上有一些很好的教程,您应该查看它们
这远不是您的完整问题 (C#...),但它可以为您提供一些线索。
此示例是在 Linux-64 上使用 gcc 在 C 中完成的;你可能需要适应它 你的平台。
第一步是用一些内联汇编(gnu-asm
语法)以查看它与 objdump 的外观(它从未被调用
实际上)。
跳转到常量地址的技巧是在这里找到的:
之后我们必须创建一个可执行内存段并填充
它的字节灵感来自上一个函数(感谢 objdump)。
如果唯一可能改变的是地址,那么我们就覆盖
前面代码中的这个地址。
在这个例子中,另一个函数的地址用于此目的。
那么这段可执行内存段可以认为是一个函数
(嗯...希望)我们通过函数指针使用它。
而且它似乎有效!
我能看到的唯一问题是指令movb [=11=]xff,0xa(%ecx,%ebx,4)
这做了坏事,因为我不知道寄存器到底是什么
应该包含。
我决定用六个 nop
代替这条指令来占据同一个地方
并保持此示例与原始问题相似。
(我想在你的问题的背景下,这些寄存器会有一个
相关值)。
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#undef __STRICT_ANSI__ // for MAP_ANONYMOUS
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
void
target_function(void)
{
printf("~~~~ %s ~~~~\n", __func__);
}
void
inline_asm_example(void)
{
__asm__ __volatile__(
"\tnop\n"
"\tmovb [=10=]xff,0xa(%ecx,%ebx,4)\n"
"\tjmpq *0x0(%rip)\n"
".quad 0xAA00BB11CC22DD33\n");
// the jump relative to rip is inspired from
//
}
int
main(void)
{
// create an executable page
void *page=mmap(NULL, 6+6+8,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if(page==MAP_FAILED)
{
fprintf(stderr, "cannot allocate memory page\n");
return 1;
}
// initialise code pattern
// objdump --disassemble=inline_asm_example prog
char *code_pattern=(char *)page;
#if 0 // this instruction causes something wrong
memcpy(code_pattern+0,
"\x67\xc6\x44\x99\x0a\xff", 6); // movb [=10=]xff, 10(%ecx,%ebx,4)
#else // use some useless instructions instead
memcpy(code_pattern+0,
"\x90\x90\x90\x90\x90\x90", 6); // 6x nop
#endif
memcpy(code_pattern+6,
"\xff\x25\x00\x00\x00\x00", 6); // jmpq *0x0(%rip)
// insert into the pattern the address we want to jump to
ptrdiff_t target_address=(ptrdiff_t)target_function;
memcpy(code_pattern+6+6, &target_address, sizeof(target_address));
// consider the code pattern as a function
void (*fnct_ptr)(void)=NULL;
memcpy(&fnct_ptr, &code_pattern, sizeof(code_pattern));
// here we go
printf("about to do something I will probably regret...\n");
fnct_ptr();
printf("ah? it was not so painful after all!\n");
// let's forget everything about that
munmap(code_pattern, 6+6+8);
return 0;
}
使用Keystone's C# bindings 将程序集转换为字节。它非常容易使用,你给它一个字符串,它给你一个字节数组,代表你输入的汇编代码:
using Keystone;
using (Engine keystone = new Engine(Architecture.X86, Mode.X32) { ThrowOnError = true })
{
ulong address = 0;
keystone.ResolveSymbol += (string s, ref ulong w) =>
{
if (s == "_j1")
{
w = 0x1234abcd;
return true;
}
return false;
};
EncodedData enc = keystone.Assemble("xor eax, eax; jmp _j1", address);
enc.Buffer.ShouldBe(new byte[] { 0x00 });
enc.Address.ShouldBe(address);
enc.StatementCount.ShouldBe(3);
}
要将作弊引擎代码注入脚本转换为 C# 代码,您需要进行外部绕行。使用 VirtualAllocateEx()
在目标进程中获取 space,使用 WriteProcessMemory
将您使用 KeyStone 创建的 shell 代码写入内存,然后执行绕行到绕行执行流程到您的 shell你注入的代码。
您需要在绕行函数中手动解决相对跳转和所有相对地址。
或者,您可以使用 CreateRemoteThread()
在新线程中执行目标进程中的代码,而不是绕道而行,具体取决于您想要做什么。