前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >strlen的另一种实现,可以作为ShellCode

strlen的另一种实现,可以作为ShellCode

作者头像
IBinary
发布2020-02-12 23:26:27
4340
发布2020-02-12 23:26:27
举报
文章被收录于专栏:逆向技术逆向技术

在实际工作中会遇到很多strlen. 这里针对strlen函数做一下代码还原. 并且讲解其原理

高级代码如下:

#include <iostream>

int main()
{
    char szBuffer[] = "HelloWorld";
    scanf_s("%s", szBuffer);
    long StrSize = strlen(szBuffer);
    scanf_s("%d", &StrSize);
    return 0;
}

请不要关注scanf_s 为了编译器能够正确编译加上自己懒得去掉SDL检查.所以加了.这里的scanf_s 只是为了防止编译器直接全部优化掉.

对应汇编代码如下:

.text:00401050 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401050 main            proc near               ; CODE XREF: __scrt_common_main_seh+F5↓p
.text:00401050
.text:00401050 var_14          = dword ptr -14h
.text:00401050 var_10          = qword ptr -10h
.text:00401050 var_8           = word ptr -8
.text:00401050 var_6           = byte ptr -6
.text:00401050 var_4           = dword ptr -4
.text:00401050 argc            = dword ptr  8
.text:00401050 argv            = dword ptr  0Ch
.text:00401050 envp            = dword ptr  10h
.text:00401050
.text:00401050                 push    ebp
.text:00401051                 mov     ebp, esp
.text:00401053                 sub     esp, 14h
.text:00401056                 mov     eax, __security_cookie
.text:0040105B                 xor     eax, ebp
.text:0040105D                 mov     [ebp+var_4], eax
.text:00401060                 mov     ax, word ptr ds:aHelloworld+8 ; "ld"
.text:00401066                 movq    xmm0, qword ptr ds:aHelloworld ; "HelloWorld"
.text:0040106E                 mov     [ebp+var_8], ax
.text:00401072                 mov     al, byte ptr ds:aHelloworld+0Ah ; ""
.text:00401077                 mov     [ebp+var_6], al
.text:0040107A                 lea     eax, [ebp+var_10]
.text:0040107D                 push    eax
.text:0040107E                 push    offset aS       ; "%s"
.text:00401083                 movq    [ebp+var_10], xmm0
.text:00401088                 call    scanf_s

.text:00401090                 add     esp, 8
.text:00401093                 lea     edx, [eax+1]
.text:00401096


.text:0040108D                 lea     eax, [ebp+var_10]
.text:00401096 loc_401096:                             ; 
.text:00401096                 mov     cl, [eax]
.text:00401098                 inc     eax
.text:00401099                 test    cl, cl
.text:0040109B                 jnz     short loc_401096
.text:0040109D                 sub     eax, edx


.text:0040109F                 mov     [ebp+var_14], eax
.text:004010A2                 lea     eax, [ebp+var_14]
.text:004010A5                 push    eax
.text:004010A6                 push    offset aD       ; "%d"
.text:004010AB                 call    scanf_s
.text:004010B0                 mov     ecx, [ebp+var_4]
.text:004010B3                 add     esp, 8
.text:004010B6                 xor     ecx, ebp
.text:004010B8                 xor     eax, eax
.text:004010BA                 call    __security_check_cookie
.text:004010BF                 mov     esp, ebp
.text:004010C1                 pop     ebp
.text:004010C2                 retn
.text:004010C2 main            endp

提取出的strlen的汇编代码. 去掉流水线优化后如下

.text:00401093                 lea     edx, [eax+1]
.text:0040108D                 lea     eax, [ebp+var_10]
.text:00401096 loc_401096:                             ; 
.text:00401096                 mov     cl, [eax]
.text:00401098                 inc     eax
.text:00401099                 test    cl, cl
.text:0040109B                 jnz     short loc_401096
.text:0040109D                 sub     eax, edx

不要关注前边的地址. 因为去掉流水线优化了.不知道啥叫流水线优化.去查看 <C++反汇编与逆向技术解密>

汇编解析与原理:

1. lea eax,[ebp + var_10] 获取字符的首地址
2. lea     edx, [eax+1]   获取字符串首地址 + 1的值.
3. mov cl,[eax]           获取单个字符给cl
4. inc eax                地址递增
5. test cl,cl             判断单个字符是否为0也就是结尾.不是就上跳,上跳是循环.所以地址不断递增
6. sub eax,edx            循环过后.eax = 字符串的高地址. 此时高地址-低地址 就是字符串的长度.

因为strlen的特点就是遇到0结尾推出.所以汇编 sub eax,edx 
eax = 字符串0结尾的地址
edx = 首地址字符串+1的地址
两者相减就是字符串长度.

因为strlen的特点遇到0结尾.所以需要多减掉1. 所以这就是为什么 edx = 字符串首地址 + 1了.
其实也可以换成下方汇编

如下:
lea eax,字符串首地址
lea edx,字符串首地址

labale:
     mov cl,[eax]      取出单个字符
     inc eax           eax相加也就是地址做增量
     test cl,cl        判断是否结尾
     jnz lable         不是结尾上跳,是的话不跳转
     sub eax,edx       求出 字符串最后0结尾的地址 - 字符串首地址  
     sbu eax,1         strlen的特殊性.所以eax - 1 (也就是去掉0) 就是字符串的长度

上面edx是进行优化了. 所以虽然是获取了字符串 + 1的地址 但是最后相减就是字符串长度了.

ShellCode表现形式, 需要修复的位置为: 0x????

8D 05 ?? ?? ?? ?? lea eax,dword ptr ds:[77A11000]
8D 50 01          lea edx,dword ptr ds:[eax+1]
8A 08             mov cl,byte ptr ds:[eax]
40                inc eax
84 C9             test cl,cl
75 F9             jne -7    
2B C2             sub eax,edx
90                nop 




UCHAR StrlenShellCode{
0x8D, 0x05, 0x??, 0x??, 0x??, 0x??, 
0x8D, 0x50, 0x01, 
0x8A, 0x08, 
0x40, 
0x84, 0xC9, 
0x75, 0xF9,
0x2B, 0xC2, 
0x90
};

这是优化后的. 当前前边有说 使用传操作指令也可以实现.就看怎么实现了

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档