调用DLL未导出函数由参数列表引发的问题

Android安全 专栏收录该内容
6 篇文章 0 订阅

0x00 背景

为了调用一个dll中未导出的函数,首先用IDA分析它的调用约定和参数列表。分析出来的函数声明是这样的:

typedef int (__thiscall* FP_SUB)(void *t, int a2, int a3)

然后用一个for循环去调用这个函数,代码逻辑大致如下:

void call_stub(void* t, int a2, char a3)
{
    FP_SUB sub = 0x80043212;
    sub(t, 0, pwd[i]);
}

char pwd[] = "12345678";
int length = 8;

for (int i; i < length; i++) {
    call_stub(t, 0, pwd[i]);
}

我使用O2优化方式编译出来,第一次调用sub结果正常,第二次调用时发现i的值居然变成了9,length的值变成了莫名其妙的值。当我禁用优化编译后,调用一切正常。这让我觉得很奇怪,为什么O2优化反而把程序优化错了。

0x01 分析

首先看一下这个for循环的汇编指令:

loc_10001EC1:
movsx   dx, Dst[edi]
call    sub_10001C20
inc     edi
cmp     edi, esi
jb      short loc_10001EC1

不难看出,edi对应的循环参数iesi对应的是lengthi变成了9,猜想可能的原因是sub_10001C20调用返回后,edi的值被错误地赋值给了esi,那么现在进到call_stub()函数中看一下它的汇编指令:


push    ebp
mov     ebp, esp
push    0FFFFFFFFh
push    offset sub_1000E078
mov     eax, large fs:0
push    eax
sub     esp, 20h
mov     eax, ___security_cookie
xor     eax, ebp
mov     [ebp+var_10], eax
push    esi
push    edi
push    eax
...
push    ecx
add     eax, 22870h
push    102h
mov     ecx, edi
mov     dword_10018D34, eax
mov     [ebp+var_14], edx
call    eax
...
mov     ecx, [ebp+var_C]
mov     large fs:0, ecx
pop     ecx
pop     edi
pop     esi
mov     ecx, [ebp+var_10]
xor     ecx, ebp
call    @__security_check_cookie@4 ; __security_check_cookie(x)
mov     esp, ebp
pop     ebp
retn

如上所示,它用栈去保护了ediesi寄存器的值,在恢复这两寄存器的值的时候出错了,可能的原因就是在执行call eax这条指令之后堆栈没有平衡好。

然后看了一下需要调用函数的汇编指令,最后平衡堆栈的指令是:

retn 0Ch

这说明用栈传递了3个参数(32bit),那么我的函数指针声明错了,使得被调用函数平衡后的栈顶指针向下移了一个位置,导致ecx的值被无形中销毁了。那么edi的值出栈保存在了ecx中,esi的值出栈保存在了edi中,所以edi的值正好是8,回到原来的循环加1变成了9。
最后正确的函数指针声明应该是:

typedef int (__thiscall* FP_SUB)(void *t, int a2, int a3, int a4)

而在禁用优化的模式下,编译器压根就没有使用ediesi去优化循环,也没有在栈上去保存寄存器,碰巧避开了这个问题。

  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值