為了呼叫乙個dll中未匯出的函式,首先用ida分析它的呼叫約定和引數列表。分析出來的函式宣告是這樣的:
typedef
int (__thiscall* fp_sub)(void *t, int a2, int a3)
然後用乙個for迴圈去呼叫這個函式,**邏輯大致如下:
void call_stub(void* t, int a2, char a3)
char pwd = "12345678";
intlength = 8;
for (int i; i < length; i++)
我使用o2
優化方式編譯出來,第一次呼叫sub
結果正常,第二次呼叫時發現i
的值居然變成了9,length
的值變成了莫名其妙的值。當我禁用優化編譯後,呼叫一切正常。這讓我覺得很奇怪,為什麼o2
優化反而把程式優化錯了。
首先看一下這個for
迴圈的彙編指令:
loc_10001ec1:
movsx dx, dst[edi]
call sub_10001c20
inc edi
cmp edi, esi
jb short loc_10001ec1
不難看出,edi
對應的迴圈引數i
,esi
對應的是length
。i
變成了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
如上所示,它用棧去保護了edi
和esi
暫存器的值,在恢復這兩暫存器的值的時候出錯了,可能的原因就是在執行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)
而在禁用優化的模式下,編譯器壓根就沒有使用edi
和esi
去優化迴圈,也沒有在棧上去儲存暫存器,碰巧避開了這個問題。 變長引數列表函式
可變引數列表 標頭檔案提供了遍歷未知數目和型別的函式引數表的功能。該標頭檔案的實現因不同的機器而不同,但提供的介面是一致的。假定函式 f 帶有可變數目的實際引數,lastarg 是它的最後乙個命名的形式引數 引數列表必須至少包括乙個命名引數 那麼,在函式 f 內宣告乙個型別為 va list 的變數...
變長引數列表函式
可變引數列表 標頭檔案提供了遍歷未知數目和型別的函式引數表的功能。該標頭檔案的實現因不同的機器而不同,但提供的介面是一致的。假定函式 f 帶有可變數目的實際引數,lastarg 是它的最後乙個命名的形式引數 引數列表必須至少包括乙個命名引數 那麼,在函式 f 內宣告乙個型別為 va list 的變數...
php函式引數列表
1.直接傳值 function fun arg1 2.傳引用 function fun arg1 1,arg2 2 一定程度上可以模擬函式過載。同時還要注意任何預設引數一定要放在非預設引數的右邊,也就是說fun arg1 1,arg2 這個函式原型是錯誤的。4.引數為陣列 function fun ...