【问题标题】:How can i run ASM opcode in a string variable Or convert it to bytes?如何在字符串变量中运行 ASM 操作码或将其转换为字节?
【发布时间】:2020-06-13 13:10:04
【问题描述】:

我在 字符串属性中有这段 asm 代码:​​

mov [ecx+ebx*4+10],ff
jmp client.dll+3A8D96

在进入我想要实现的目标之前,首先我会将 client.dll+... 更改为它的最终地址。

现在我目前想要实现的是:

  • 将那段 asm 代码转换成它的等效字节,这样我就可以在使用 winapi 更改这些字节后将这些字节写回内存..

  • 或者以某种方式将该字符串的内容写入分配的内存中,如果不将其转换为字节,这似乎是不可能的.. idk

我不知道是否可行,但我也有这个想法

  • 在 c++ 中创建一个 dll,该 dll 将具有可以从我的 c# 应用程序导入的函数
  • 通过将 asm 操作码和分配的内存地址作为参数传递(不是 c++ 专家),使用任何可能的方法来实现我的目标,但我知道在 c++ 中可以使用内联 asm。

请记住,这篇文章中给出的 ASM 代码只是为了演示我正在尝试做的事情,它实际上会发生很大变化,我希望你明白这一点。

【问题讨论】:

  • 想要在 C++ 中运行内联 ASM 通常是个坏主意。
  • @JesperJuhl 我只是想把它放在那里,但感谢您提供的信息。
  • 当我进行汇编编程时,我将我的代码放在一个*.ASM 文件中并通过我的汇编器和链接器运行它。
  • @Eljay 我还想过我只是忘了在帖子中说,一个完美的方法是将我的字符串的内容复制到 file.asm 并执行它,但这仍然无济于事将 asm 代码写入新分配的内存,但是我认为可以获得字节? :思考:
  • 你没有在你的帖子中显示操作码,你已经显示了 asm 源代码。但是随后您谈到在运行时从进程中提取 asm。正常进程在内存中不包含任何 asm 字符串,它们包含机器代码。你说你想把“asm 转换成它的等效字节”。这就是assembler 所做的(文本->binary),但是你将其描述为disassembler。所以甚至不清楚你想要什么:只是执行一些你从另一个进程中复制出来的机器代码?好的,将它复制到一个可执行页面(VirtualAllocmmap)。你必须重新定位任何相对的跳跃......

标签: c# c++ assembly opcode


【解决方案1】:

如果您使用的是 c#,我建议您使用包装器来写入进程内存并导入必要的 dll 等,并获取您的 asm 操作码的原始字节并按照您自己的意愿操作它们

如果您使用的是 c++,您可以直接使用这些函数,而无需获取包装器等;大多数函数都在 C++ 库中

如果您正在寻找类似作弊引擎 codecave(带有跳转和回跳的 aob 注入)之类的东西,请考虑您花一些时间来学习有关内存操作/分配内存/asm 一般如何工作的知识,之后您就可以搞砸了轻松记忆

对于使用 c++ 的内部 dll,您需要使用原始字节,如果我没记错的话,他们从 Visual Studio 2010 中删除了 codecave 等的 asm 功能,并将其仅限于企业版本或其他东西

如果您考虑按原样使用此操作码 jmp client.dll+3A8D96 如果您知道 asm,那将是一个非常糟糕的主意,如果它接近跳跃并没什么大不了的,但是这些偏移量可能会不时改变

"\xFE\xC8\x5B\x23\xC1"

这只是我如何在代码中使用字节的示例,youtube 上有一些很好的教程,您应该查看它们

【讨论】:

  • 嗯,这并不能回答我的问题.. 你所说的都是我自古以来就知道要做的基本事情。但谢谢你的尝试,我猜..
  • 将那段asm代码转换成它的等效字节,这样我就可以在使用winapi更改它们后将这些字节写回到内存中......为此你可以使用1)在线网站并手动执行2)如果您想以编程方式执行此操作,有 C++ 编码的反汇编程序,使用带有字符串解析的源代码并根据您的协议进行修改 3)您可以在任何程序或您要操作的程序上使用作弊引擎来获取原始字节
  • 表示我在示例中给出的操作数代码只是为了演示,它将是动态的,所以手动操作为null。我没有说任何关于调试器或作弊引擎的事情,所以让它过去吧:)。是的,我要问的是如何以编程方式进行操作,您所说的是真的吗您能提供有关这些反汇编程序的示例以及如何使用吗?
  • 这是其中之一 github.com/skywind3000/asmpure 也可以查看此线程 stackoverflow.com/questions/17001094/… 我有一个更好的,但不记得细节了,因为我自己很长一段时间都在使用原始字节跨度>
  • 干杯伙伴!非常感谢您的努力,非常有用的参考!
【解决方案2】:

这远不是你的全部问题(C#...),但它可以给你一些线索。

这个例子是用 C 语言在 Linux-64 上用 gcc 完成的;你可能需要适应它 你的平台。

第一步是用一些内联汇编(gnu-asm 语法)以查看它与 objdump 的外观(它从未被调用 实际上)。
在这里找到了跳转到常量地址的技巧: https://stackoverflow.com/a/53876008/11527076

之后我们必须创建一个可执行内存段并填充 它具有受前一个函数启发的字节(感谢 objdump)。
如果唯一可能改变的是地址,那么我们只需覆盖 这个地址在前面的代码中。
在此示例中,另一个函数的地址用于此目的。

那么这个可执行内存段可以看成是一个函数 (嗯……希望如此)我们通过函数指针来使用它。
而且它似乎有效!

我能看到的唯一问题是指令movb $0xff,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   $0xff,0xa(%ecx,%ebx,4)\n"
    "\tjmpq   *0x0(%rip)\n"
    ".quad 0xAA00BB11CC22DD33\n");
  // the jump relative to rip is inspired from
  // https://stackoverflow.com/a/53876008/11527076
}

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   $0xff, 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;
}

【讨论】:

    【解决方案3】:

    使用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() 获取目标进程中的空间,使用WriteProcessMemory 将您使用KeyStone 创建的shell 代码写入内存,然后绕道执行流程以绕过您注入的shellcode。

    您需要在 detour 函数中手动解析相对跳转和所有相对地址。

    或者,您可以使用CreateRemoteThread() 在新线程中执行目标进程中的代码,而不是绕道而行,具体取决于您要执行的操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-23
      • 2013-06-21
      • 1970-01-01
      • 2015-07-21
      • 1970-01-01
      • 2015-05-07
      相关资源
      最近更新 更多