RtlXXXMemory宏和mem*
搜索WRK源代码,找不到memxxx相关函数的源代码。它们在如下prebuilt lib库中。
IDA工具查找ntoswrk.lib里memxxx函数的实现如下:
- ; 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会按byte去写,这样没有充分利用64bit硬件的单个指令高效性。
工具生成pseudo code:
首先判断是否超过8B, 超过就会优先按8B为单位去写。这里利用了 Val * 0x0101010101010101巧妙一次填充8个Val.
eg:
为了尽可能减少以8字节为单位不断从内存(或CacheLine)中抓数据,内核实作优先采用更大的单位。
32B:
4KB:
prefetchnta指令可以实现跨页边界存取,非常强大。内核可能存在超过4KB数据量copy, 此指令相比常规的register大小为单位可以大幅提高性能。