我如何 运行 字符串变量中的 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+... 更改为它的最终地址。

现在我正在努力实现的是:

不知道可不可以,我也有这个想法:

请记住,此 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() 在新线程中执行目标进程中的代码,而不是绕道而行,具体取决于您想要做什么。