【问题标题】:Find out if a received pointer is a string, ushort or array查明接收到的指针是字符串、ushort 还是数组
【发布时间】:2010-09-30 17:37:04
【问题描述】:

我在 C 中插入 memcpy() 函数,因为目标应用程序使用它来连接字符串,我想找出正在创建的字符串。代码是:

void * my_memcpy ( void * destination, const void * source, size_t num )
{
    void *ret = memcpy(destination, source, num);
    // printf ("[MEMCPY] = %s \n", ret);
    return ret;
}

函数被成功调用,但第一个参数可以是任何参数,如果结果是字符串或数组,我只想跟踪它。我不得不问它是数组还是字符串。我知道这不能直截了当:有没有办法找出 RET 指向什么?

我在 MACOSX 下工作并与 DYLD 进行交互。

非常感谢。

【问题讨论】:

  • 您可以访问地图文件吗?如果是这样,您可以找到放置字符串文字的位置并从中获取一些信息。全局数组也是如此。
  • 你不应该在你的函数中调用printf,因为printf非常复杂,它本身可能会再次调用memcpy。相反,我会选择简单的writewritev。或者您可以添加一些线程安全的激活计数器,如果 printf 部分尚未激活,则该计数器只执行。

标签: c++ pointers code-injection library-interposition


【解决方案1】:

由于void* 代表一个原始内存块,因此无法确定其中的实际数据。

但是,您可以在每个操作上进行“类似字符串”的内存转储,只需为结果输出提供某种“输出上限”。

这可以通过以下方式实现:

const size_t kUpperLimit = 32;

void output_memory_dump(void* memory) {
   std::cout.write(reinterpret_cast<char*>(memory), kUpperLimit);
}

对于非字符串类数据,输出很难解释,但否则你会得到你正在搜索的内容。

您可以尝试应用一些基于猜测的方法,例如遍历 reinterpret_cast&lt;void*&gt;(memory) 并对每个符号进行 is_alphanumeric &amp;&amp; is_space 检查,但这种方法似乎不太稳定(谁知道实际上可能存在什么void*...)。

无论如何,在某些情况下可能没问题。

【讨论】:

  • 如果内存区域小于 32 字节怎么办?还是小于5?你怎么知道使用什么上限?好吧,至少您可以为此使用num,但这个数字可能小于实际内存区域大小。
  • @Vlad 好的,我们将转储 5 个实际字节以及之后的所有内容。我几乎可以肯定这不会导致调试模式下的访问错误等等。 同样很明显,如果你只有void* 指针而没有其他任何东西,你就不可能有一个“足够好”的方法,但为什么不试试呢......
  • @HardCoder1986:哦,是的,由于访问受保护的内存,它可以轻松地对您的程序进行 SEGFAULT。
  • @Vlad 哦,我对此表示怀疑。 这样说,接收指向非空终止字符序列的指针的调试器也应该出现段错误。
  • 您可以尝试查看堆以查找已分配块的大小。尝试检查((long *)source)[-1] 并查看值是什么。
【解决方案2】:

您可以先对复制的内存应用一些启发式方法,然后根据此决定是否要打印它。

static int maybe_string(const void *data, size_t n) {
  const unsigned char *p;
  size_t i;

  p = data;
  for (i = 0; i < n; i++) {
    int c = p[i];
    if (c == '\n' || c == '\r' || c == '\t')
      continue;
    if (1 <= c && c < 32)
      return 0; /* unusual ASCII control character */
    if (c == '\0' && i > 5)
      return 1; /* null-terminated and more than a few characters long */
  }

  return 0; /* not null-terminated, so it isn't a string */
}

这种启发式方法并不完美。例如,以下模式失败:

const char *str = "hello, world";
size_t len = strlen(str);
char *buf = malloc(1024);
memcpy(buf, str, len);
buf[len] = '\0';

如果你也想抓住它,你必须改变上面的函数。

【讨论】:

  • 考虑到他正在设置标准的memcpy 函数,这会大大降低系统速度。更不用说内存可以包含任何二进制数据,包括 ASCII 范围内的所有字节,这并不意味着它是字符串。
  • 你是对的:当 每个 字节在 ASCII 范围内(而不是 \0)时,内存不包含字符串。这就是最后一行中的return 0 的用途。但除此之外,原始海报听起来更像是出于好奇而不是用于生产用途,因此放缓可能是可以接受的。我不认为打印一些额外的“字符串”会造成伤害,我只是想确保终端不会被意外的控制字符弄糊涂。因此检查异常的 ASCII 控制字符。
【解决方案3】:

ret 等于目标指针。但是无法确定它是数组还是字符串,除非您知道有关数组或字符串的更多信息(例如,字符串具有一定长度并且以空值结尾)。

【讨论】:

    【解决方案4】:

    不,您无法从 void 类型的指针中弄清楚这一点。另外,您不知道源或目标的大小,因此启发式方法不起作用。由于其他原因,它也不起作用,例如存储在void*指向的内存区域中的二进制数据实际上可以在末尾有零字节,但这并不意味着它是字符串。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-25
      • 1970-01-01
      • 1970-01-01
      • 2021-11-21
      • 1970-01-01
      • 1970-01-01
      • 2011-10-19
      • 2012-03-06
      相关资源
      最近更新 更多