2012-02-13 4 views
4

이 게시물은 관련 게시물 question의 후속 조치로 여기에 Ran으로 게시됩니다.메소드를 콜백 함수로 Windows API 호출에 전달하는 방법 (후속 조치)?

accepted answer은 일반적인 평범한 함수를 사용합니다.

이 발췌 특히 내 관심을 잡을 :

인스턴스 방법은 인스턴스 참조를 포함하는 추가, 암시, 매개 변수, 즉 자기가 있습니다. 나는 종료합니다 (uneeded 자기 암시 기준을 없애 바꿔과 준수 적응 콜백 함수에 대한 포인터를 제공하기 위해) "매개 변수"어댑터의 종류를 사용하는 방법이 있어야한다는 확고한 신념을 가진

article콜백 클래스에 의해 Peter Morris으로 표시됩니다.

요약하자면, 그는 적응 트릭으로 썽킹 기술을 사용합니다. (면책 조항 : 나는 코드를 테스트 한 적이 없다.)

솔루션으로는별로 깨끗하지는 않지만 모든 장점이있는 객체 지향 디자인을 허용한다는 것을 알고 있습니다.

내 질문 :

TCallbackThunk이 콜백 함수 서명을 기반으로 알면, 피터 모리스가 갈 수있는 방법입니다처럼 그 일을하는 경우 위의 대해 참조 게시물의 해답이 될 것입니다 무슨?

.

+0

일부 API 콜백이 lParam의 해결 방법을 허용하지만 일부는 그렇지 않다는 것을 추가합니다 (예 : 그림의 WinNLS). 또한, 이것은 StdcallThunk 또는 뭔가라고해야합니다. – OnTheFly

+0

생성 된 코드가 실행 가능 페이지에 있지 않기 때문에 이는 작동하지 않습니다. 그 문제를 해결할 수 있습니다. 하지만 그때 당신은 x86에서만 작동하고 x64에서는 실패 할 수있는 코드를 갖게 될 것입니다. –

+0

@ David Heffernan : 감사합니다. 제 박스는 x86과 델파이 5, 7, 2007에서 작동하는 모든 코드입니다. XE는 그렇게 할 것입니다. 게시물을 수정해야합니다. – menjaraz

답변

3

EnumWindows (참조 된 질문의 기능)이 데이터 매개 변수를 제공하므로 실제로 모든 작업을 수행 할 필요가 없습니다. 답안에서 보여준 객체 참조와 같이 원하는 값을 넣을 수 있습니다. Morris의 기법은 범용 데이터 매개 변수를 제공하지 않는 콜백 함수에 더 적합합니다.

모리스의 코드를 사용하여 답을 적용하려면 먼저 콜백 메소드의 서명이 API의 콜백 함수의 서명과 일치하는지 확인해야합니다. EnumWindows을 호출하기 때문에 Bool을 반환하는 두 개의 인수를 갖는 함수가 필요합니다. 호출 규칙은 stdcall이어야합니다 (모리스의 코드에서 가정하기 때문에 다른 모든 호출 규칙을 사용하기가 어렵습니다).

function TAutoClickOKThread.cbEnumWindowsClickOK(
    Wnd: HWnd; Param: LParam): Bool; stdcall; 
begin 
    // ... 
end; 

다음에, 우리는 머신 코드로 TCallbackThunk 데이터 구조를 설정하고 점프 의도 콜백 방법을 참조하여 오프셋.

그러나 Morris가 설명한 방식을 사용하지 않습니다. 그의 코드는 데이터 구조를 스택에 둡니다. 즉, 실행 코드스택에 있습니다. 최신 프로세서와 운영 체제는 더 이상 허용하지 않습니다. OS가 프로그램을 중단시킵니다. VirtualProtect을 호출하여 현재 스택 페이지의 권한을 수정하고 실행을 허용하지만 전체 페이지를 실행 가능하게 만들 수 있으며 공격을 목적으로 프로그램을 열어두기를 원하지 않습니다. 대신 우리는 특히 스택과 별도로 썽크 레코드를위한 메모리 블록을 할당 할 것입니다.

procedure TAutoClickOKThread.Execute; 
var 
    Callback: PCallbackThunk; 
begin 
    Callback := VirtualAlloc(nil, SizeOf(Callback^), 
    Mem_Commit, Page_Execute_ReadWrite); 
    try 
    Callback.POPEDX := $5A; 
    Callback.MOVEAX := $B8; 
    Callback.SelfPtr := Self; 
    Callback.PUSHEAX := $50; 
    Callback.PUSHEDX := $52; 
    Callback.JMP := $E9; 
    Callback.JmpOffset := Integer(@TAutoClickOKThread.cbEnumWindowsClickOK) 
     - Integer(@Callback.JMP) - 5; 

    EnumWindows(Callback, 0); 
    finally 
    VirtualFree(Callback); 
    end; 
end; 

해당 레코드는 32 비트 x86 명령어입니다. 나는 해당 x86_64 명령이 무엇인지 전혀 모른다.

+0

+1 : 매우 유익한 정보! 대답을 받아들이 기 전에 Morris의 코드가 노화된다는 2 가지 사실을 확인하고 싶습니다. 1) 포인터 조정이 유효한지 (VMT/TObject가 버전 간 변경됨) 2) 하드 코드 된 ASM 명령어와 동일합니다. 나는 ASM과 BASM이 거의 없다는 것을 고백해야한다. 고맙습니다. – menjaraz

+0

코드 삽입을하면 보안이 손상 될 수 있습니다. – menjaraz

+0

포인터 조정은 JMP 명령어의 크기를 보완하기위한 것입니다. JMP 명령어의 오프셋은 * next * 명령어와 관련이 있습니다 (주소 계산이 수행되기 전에 EIP가 고급이기 때문에). VMT 크기 나 객체와 아무 관련이 없습니다. X86 명령어는 변경되지 않았습니다. –

관련 문제