한 번의 수수께끼 풀기: VB에서 인터페이스 설명이 없는 DLL 함수를 호출합니다

본고는qingye2008이 보낸 이 글의 토론에서 나온 것으로 진휘, 마운검, qingye의 인내심과 공유에 감사드립니다.
1. 인용문
그나저나 qingye 학생은 복호화에 사용되는 Dll(이 동적 라이브러리는 여기서 다운로드)을 받았습니다. Dll Export Viewer를 통해 dll에 두 개의 내보내기 함수가 있는데 각각 Dll 입니다.EncIn 및 DllEncOut, qingye 학생은 VB에서 이 두 함수를 사용하고 싶어 합니다.인터페이스 설명을 할 수 없기 때문에, 어셈블리 코드를 보고 매개 변수의 수량과 유형을 추측할 수밖에 없다.학우들의 모색을 통해 대체적인 과정은 다음과 같다.
 
(1) 먼저 IDA와 같은 정적 어셈블리 도구로 함수의 매개 변수가 몇 개, 되돌아오는 값이 있는지 확인한다.(두 개 확인, 반환 값 없음)
(2) OD와 같은 동적 어셈블리 디버깅 도구로 레지스터에 저장된 파라미터가 구체적으로 무엇인지 보고 함수의 예정 기능에 따라 파라미터가 어떤 유형으로 성명되고 어떤 내용을 전달해야 하는지 추측한다.
a) 예를 들어 이 함수의 기능은 암호화인데 기능에 따라 전송할 매개 변수는 최소한 명문도 있고 길이도 있을 수 있다고 추측한다.또한 함수는 암호화된 밀문을 되돌려 주는 방법이 있어야 한다. 함수가 값을 되돌려 주지 않은 이상 주소를 전달하는 매개 변수가 밀문일 수도 있다.따라서 초보적으로 두 개의 매개 변수가 있어야 한다. 하나는 전명문 문자열이고, 하나는 밀문 문자열을 수신하는 데 쓰일 것이다.
b) OD로 따라 들어가서 레지스터의 내용을 보니 문자열'123'을 발견했고 레지스터에'03313233'이라고 적혀 있었다.다시 문자열 '123123' 을 보내면 레지스터에 '063132333' 이라고 쓰여 있습니다.이 매개 변수는 문자열이고 문자열의 인코딩은 ANSI(&H31은 숫자 1의 ANSI 인코딩이기 때문)이며 문자열 버퍼 앞의 첫 바이트는 이 ANSI 문자열의 바이트 길이이다.
c) Delphi로 이루어진 동적 라이브러리임을 감안하여 Delphi와 관련된 문서를 찾아보고 Delphi에 Short String이라는 문자열이 b)에 기술된 특징에 부합되는 것을 확인하여 이러한 추측을 입증하였다.
d) 두 개의 매개 변수는 밀문이 앞에 있습니까 아니면 명문이 앞에 있습니까?OD로 관찰한 결과에 따라 첫 번째 파라미터를 기억하고 창고에 들어가면 밀문이 앞에 있고 명문이 뒤에 있다는 것을 확인할 수 있다.
(3) 상기 추측에 근거하여 VB에 구성된 성명.이것은 바로 이 박문과 이 박문에서 언급한 지식을 써야 한다.
 
2. 패스 구조 지침
먼저 진휘가 쓴 코드를 보면 가장 좋고 가장 간편한 글씨라고 생각해요.
Option Explicit

Private Declare Function Dll_EncIn Lib "d:/EncryptionA.dll" (ByVal lpstrOutput As Long, _
    ByVal lpstrInput As Long) As Long
Private Declare Function Dll_EncOut Lib "d:/EncryptionA.dll" (ByVal lpstrOutput As Long, _
    ByVal lpstrInput As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
    Source As Any, ByVal Length As Long)

Private Type SHORT_STRING
    Length As Byte
    Buffer(254) As Byte
End Type

Public Sub Test_Encode()
    Dim p As SHORT_STRING
    Dim pp As SHORT_STRING
    Dim strTmp As String
    
    p.Length = 6
    CopyMemory p.Buffer(0), ByVal "123123", 6

    Call Dll_EncIn(VarPtr(pp), VarPtr(p))
    
    strTmp = StrConv(pp.Buffer, vbUnicode)
    strTmp = Left(strTmp, pp.Length)
    Debug.Print strTmp
    
    Call Dll_EncOut(VarPtr(pp), VarPtr(pp))

    strTmp = StrConv(pp.Buffer, vbUnicode)
    strTmp = Left(strTmp, pp.Length)
    Debug.Print strTmp
End Sub

설명은 다음과 같습니다.
(1)
이 두 파라미터는 우리가 스트링 유형을 입고 들어갈 수 없을 것이다. 비록 VB가 자동으로 전송된 문자열 변수를 기대하는 ANSI 인코딩으로 바꾸지만, 분명히 이 DLL이 기대하는 것은 VB의 BSTR 문자열이 아니다. 인코딩이 맞지만 구조가 맞지 않으면 안 된다.
(2)
스트링을 직접 전할 수 없다면 우리는 스스로 전할 것을 만들어야 한다.비교적 원시적인 방법은 스스로 Byte 수조를 구성하는 것이다(뒤에 예를 들 것이다).또 하나는 구조구조로 위에 진휘가 쓴 코드와 같다.
구조적인 장점은 VB에서 코드를 비교적 명확하게 써서 직접 사용할 수 있다는 것이다.연산자로 상응하는 바이트를 얻다.
(3) 모두 주의하고,
구조 안의 Byte 그룹을 문자열로 정의할 수 없습니다. 아래와 같이
Private Type STRING_TYPE
    Length As Byte
    strBuffer As String
End Type

이렇게 정의하면 VB의 자동 UA/AU 변환을 일으키지 않지만 VB에서 문자열 변수와 문자열 버퍼는 분리되어 저장되기 때문에 여기strBuffer는 4바이트의 바늘 변수일 뿐이다.DLL은 바이트 길이에 문자열 버퍼를 추가하는 연속적인 메모리를 기대하기 때문에 번호가 맞지 않거나 맞지 않는다(이 게시물의 토론 참조).단, 이strBuffer를
긴 문자열(그리고 길이가 4자 바이트 배수)을 정하면 데이터는 바로 옆에 저장된다. 아래의 구조의 정의를 보면 정확한 결과를 얻을 수 있다.
'            ——  Tiger_Zhao
Private Type SHORT_STRING
    Length As Byte
    strBuffer As String * 252
End Type

Public Sub Test_EncodeCH()
    Dim pIn As SHORT_STRING
    Dim pOut As SHORT_STRING
    Dim strTmp As String
    
    pIn.Length = 6
    pIn.strBuffer = StrConv("123123", vbFromUnicode)

    Call Dll_EncIn(VarPtr(pOut), VarPtr(pIn))
    
    strTmp = StrConv(pOut.strBuffer, vbUnicode)
    strTmp = Left(strTmp, pOut.Length)
    Debug.Print strTmp
    
    pIn = pOut
    Call Dll_EncOut(VarPtr(pOut), VarPtr(pIn))

    strTmp = StrConv(pOut.strBuffer, vbUnicode)
    strTmp = Left(strTmp, pOut.Length)
    Debug.Print strTmp
End Sub

(4) 진휘가 쓴 이 몇 개의 Copy Memory를 보면 매우 편리하다. 직접 VB에서 한 바이트씩 (뒤에 나는 이런 코드를 쓸 것이다) 스스로 쓰는 것보다 훨씬 간편하지만 잘못 쓰지 않도록 조심해야 한다.그러나 CopyMemory를 사용하지 않아도 되므로 긴 문자열로 정의하는 것이 더 편리합니다(위의 Tiger Zhao 코드 참조).
 
3. 바이트 배열의 방법
이것은 내가 쓴 것이고,
Option Explicit

Private Declare Sub Dll_EncIn Lib "D:/EncryptionA.dll" (ByVal lpstrOutput As Long, _
    ByVal lpstrInput As Long)
Private Declare Sub Dll_EncOut Lib "D:/EncryptionA.dll" (ByVal lpstrOutput As Long, _
    ByVal lpstrInput As Long)

'  1: 1            '
'  2:DLL         :  +ANSI  '
Public Sub Test_Encode()
    Dim strInput As String, strOutput As String
    Dim bytInput() As Byte, bytOutput() As Byte
    Dim i As Long    
    
'               '
    '  ANSI   '
    strInput = StrConv("123", vbFromUnicode)
    bytInput = strInput
    '      , CopyMemory    '
    ReDim Preserve bytInput(UBound(bytInput) + 1)
    For i = UBound(bytInput) To 1 Step -1
        bytInput(i) = bytInput(i - 1)
    Next i
    bytInput(0) = LenB(strInput)
    
    ReDim Preserve bytOutput(254)

'  Dll'
'    Call Dll_EncIn(bytOutput, bytInput) '           '
    Call Dll_EncIn(VarPtr(bytOutput(0)), VarPtr(bytInput(0)))
    Erase bytInput
    
'        VB'
    '       '
    For i = 1 To UBound(bytOutput)
        bytOutput(i - 1) = bytOutput(i)
    Next i
    ReDim Preserve bytOutput(UBound(bytOutput) - 1)
    '  Unicode   '
    strOutput = bytOutput
    Erase bytOutput
    strOutput = StrConv(strOutput, vbUnicode)
    ' VbNullChar  '
    strOutput = Left(strOutput & vbNullChar, InStr(1, strOutput, vbNullChar) - 1)
    
    Debug.Print strOutput
End Sub

설명은 다음과 같습니다.
(1) 내가 CopyMemory를 사용하지 않고 직접 만든 Byte 수조를 보니 진휘의 코드보다 말이 많지 않나요?
(2) 그룹 주소를 전송하려면 그룹 변수의 주소를 직접 전송하지 말고
전송수 그룹의 원소의 주소입니다.VB의 그룹 변수와 C의 그룹 변수가 다를 때 주의하십시오.C의 그룹 변수는 바늘로 첫 번째 원소를 가리킬 수 있다.VB의 배열 변수는 배열 설명자를 가리킵니다.나는 어제 이 두 개를 헷갈려서 VB에게 자꾸 끊으라고 했다.

좋은 웹페이지 즐겨찾기