找不到memcpy源代码?
RtlXXXMemory宏和mem*
搜索WRK源代码,找不到memxxx相关函数的源代码。它们在如下prebuilt lib库中。
IDA工具查找ntoswrk.lib里memxxx函数的实现如下:
先从memchr开始
; amd64 assemble
.text:00000000004A51D0 ; void *__cdecl memchr(const void *Buf, int Val, size_t MaxCount)
.text:00000000004A51D0 public memchr
.text:00000000004A51D0 memchr proc near
.text:00000000004A51D0 test r8, r8
.text:00000000004A51D3 jz short loc_4A51E1
.text:00000000004A51D5
.text:00000000004A51D5 loc_4A51D5: ; CODE XREF: memchr+F↓j
.text:00000000004A51D5 cmp [rcx], dl
.text:00000000004A51D7 jz short loc_4A51E1
.text:00000000004A51D9 inc rcx
.text:00000000004A51DC dec r8
.text:00000000004A51DF jnz short loc_4A51D5
.text:00000000004A51E1
.text:00000000004A51E1 loc_4A51E1: ; CODE XREF: memchr+3↑j
.text:00000000004A51E1 ; memchr+7↑j
.text:00000000004A51E1 xor eax, eax
.text:00000000004A51E3 test r8, r8
.text:00000000004A51E6 cmovnz rax, rcx
.text:00000000004A51EA retn
.text:00000000004A51EA memchr endp
a. 为何出现r8/rcx/dl寄存器操作?
如上为amd64架构汇编,amd64 calling conversion默认函数前四个参数以rcx, rdx, r8 and r9传入。
Buf | rcx |
Val | rdx |
MaxCount | r8 |
b. 汇编flow: test r8, r8判断r8, 如果是0:
跳到最后,eax清成0,再次判断r8是否是0,不为0,把rcx(Buf指针)赋值给rax, rax作为返回值返回。
不是0:
比较*Buf和Val的低八位(以Byte单位),如相同,即找到字符位置,跳到最后返回,如不相同,Buf++, MacCount--, 重复开始的比较。
工具生成 pseudo code
void *__cdecl memchr(const void *Buf, int Val, size_t MaxCount)
{
void *result; // rax
for ( ; MaxCount; --MaxCount )
{
if ( *(_BYTE *)Buf == (_BYTE)Val )
break;
Buf = (char *)Buf + 1;
}
result = 0i64; // 此处是工具解析的问题,可以先忽略
if ( MaxCount )
result = (void *)Buf;
return result;
}
memchr没有什么特别的。
memset
常规意义上,memset会按byte去写,这样没有充分利用64bit硬件的单个指令高效性。
工具生成pseudo code:
首先判断是否超过8B, 超过就会优先按8B为单位去写。这里利用了 Val * 0x0101010101010101巧妙一次填充8个Val.
eg:
memcpy
为了尽可能减少以8字节为单位不断从内存(或CacheLine)中抓数据,内核实作优先采用更大的单位。
32B:
4KB:
prefetchnta指令可以实现跨页边界存取,非常强大。内核可能存在超过4KB数据量copy, 此指令相比常规的register大小为单位可以大幅提高性能。