엠바카데로의 터보 어셈블러가 버그가 있어서, MS사의 매크로 어셈블러로 작성한 것임.
TASM으로는 64bit Structured Exception Handling 코드도 작성할 수 없음.
엠바 애들 물건을 이런 식으로 개판으로 만들면서 돈 받고 팔아 먹는거 보면 신기함 ㅋ
다음은 MS 매크로 어셈블러로 작성한 소스코드임.
.686P
.XMM
.model flat
EXTRN _setlocale:PROC
EXTRN _PKEY_Device_FriendlyName:BYTE
EXTRN __imp__PropVariantClear@4:PROC
EXTRN __imp__CoInitialize@4:PROC
EXTRN _printf:PROC
EXTRN _atoi:PROC
EXTRN __imp__CoCreateInstance@20:PROC
STATICDATA SEGMENT
__GUID_f8679f50_850a_41cf_9c72_430f290290c8 DD 0f8679f50H
DW 0850aH
DW 041cfH
DB 09cH
DB 072H
DB 043H
DB 0fH
DB 029H
DB 02H
DB 090H
DB 0c8H
__GUID_a95664d2_9614_4f35_a746_de8db63617e6 DD 0a95664d2H
DW 09614H
DW 04f35H
DB 0a7H
DB 046H
DB 0deH
DB 08dH
DB 0b6H
DB 036H
DB 017H
DB 0e6H
__GUID_bcde0395_e52f_467c_8e3d_c4579291692e DD 0bcde0395H
DW 0e52fH
DW 0467cH
DB 08eH
DB 03dH
DB 0c4H
DB 057H
DB 092H
DB 091H
DB 069H
DB 02eH
__GUID_870af99c_171d_4f9e_af0d_e63df40c2bc9 DD 0870af99cH
DW 0171dH
DW 04f9eH
DB 0afH
DB 0dH
DB 0e6H
DB 03dH
DB 0f4H
DB 0cH
DB 02bH
DB 0c9H
sLocale DB 'kor', 00H
sDevN DB 'Device Number[%d]: %ws', 0aH, 00H
sCurDev DB 0aH, 'Current Default Device: %ws', 0aH, 0aH, 00H
STATICDATA ENDS
PUBLIC _main
_TEXT SEGMENT
_devConfig@@ = -4
myChangeInternalDevice PROC
push ecx
push esi
lea eax, DWORD PTR _devConfig@@[esp+8]
push eax
push OFFSET __GUID_f8679f50_850a_41cf_9c72_430f290290c8
push 23
push 0
push OFFSET __GUID_870af99c_171d_4f9e_af0d_e63df40c2bc9
mov esi, ecx
call DWORD PTR __imp__CoCreateInstance@20
test eax, eax
js SHORT @@noAction
mov eax, DWORD PTR _devConfig@@[esp+8]
push 1
mov ecx, DWORD PTR [eax]
push esi
push eax
call DWORD PTR [ecx+52]
mov ecx, DWORD PTR _devConfig@@[esp+8]
push ecx
mov edx, DWORD PTR [ecx]
mov esi, eax
call DWORD PTR [edx+8]
@@noAction:
pop esi
pop ecx
ret
myChangeInternalDevice ENDP
_Device@@ = -28
_Store@@ = -24
_friendlyName@@ = -20
myPrintDefaultDevice PROC
sub esp, 28
mov eax, DWORD PTR [ecx]
lea edx, DWORD PTR _Device@@[esp+28]
push edx
push 1
push 0
push ecx
mov DWORD PTR _Device@@[esp+44], 0
call DWORD PTR [eax+16]
mov eax, DWORD PTR _Device@@[esp+28]
lea edx, DWORD PTR _Store@@[esp+28]
push edx
mov DWORD PTR _Store@@[esp+32], 0
mov ecx, DWORD PTR [eax]
push 0
push eax
call DWORD PTR [ecx+16]
mov eax, DWORD PTR _Device@@[esp+28]
push eax
mov ecx, DWORD PTR [eax]
call DWORD PTR [ecx+8]
mov eax, DWORD PTR _Store@@[esp+28]
lea edx, DWORD PTR _friendlyName@@[esp+28]
xorps xmm0, xmm0
push edx
movq QWORD PTR _friendlyName@@[esp+32], xmm0
movq QWORD PTR _friendlyName@@[esp+40], xmm0
mov ecx, DWORD PTR [eax]
push OFFSET _PKEY_Device_FriendlyName
push eax
call DWORD PTR [ecx+20]
mov eax, DWORD PTR _Store@@[esp+28]
push eax
mov ecx, DWORD PTR [eax]
call DWORD PTR [ecx+8]
push DWORD PTR _friendlyName@@[esp+36]
push OFFSET sCurDev
call _printf
add esp, 8
lea eax, DWORD PTR _friendlyName@@[esp+28]
push eax
call DWORD PTR __imp__PropVariantClear@4
add esp, 28
ret
myPrintDefaultDevice ENDP
_Device$$ = -44
_Enum$$ = -40
_Store$$ = -36
_loopCount$$ = -32
_Devices$$ = -28
_Param$$ = -24
_devID$$ = -20
_friendlyName$$ = -16
_main PROC
push ebp
mov ebp, esp
and esp, 0fffffff0H
sub esp, 56
push esi
push edi
push OFFSET sLocale
push 0
call _setlocale
add esp, 8
cmp DWORD PTR [ebp+8], 2
mov DWORD PTR _Param$$[esp+64], -1
jne SHORT @@skipParam
mov eax, DWORD PTR [ebp+12]
push DWORD PTR [eax+4]
call _atoi
add esp, 4
mov DWORD PTR _Param$$[esp+64], eax
@@skipParam:
push 0
call DWORD PTR __imp__CoInitialize@4
mov esi, eax
mov DWORD PTR _Enum$$[esp+64], 0
test esi, esi
js @@fail
lea eax, DWORD PTR _Enum$$[esp+64]
push eax
push OFFSET __GUID_a95664d2_9614_4f35_a746_de8db63617e6
push 23
push 0
push OFFSET __GUID_bcde0395_e52f_467c_8e3d_c4579291692e
call DWORD PTR __imp__CoCreateInstance@20
mov esi, eax
test esi, esi
js @@fail
mov eax, DWORD PTR _Enum$$[esp+64]
lea edx, DWORD PTR _Devices$$[esp+64]
mov ecx, DWORD PTR [eax]
push edx
push 1
push 0
push eax
call DWORD PTR [ecx+12]
mov esi, eax
test esi, esi
js @@fail
mov eax, DWORD PTR _Devices$$[esp+64]
lea edx, DWORD PTR _loopCount$$[esp+64]
mov DWORD PTR _loopCount$$[esp+64], 0
mov ecx, DWORD PTR [eax]
push edx
push eax
call DWORD PTR [ecx+12]
xor edi, edi
cmp DWORD PTR _loopCount$$[esp+64], edi
jbe @@fail
@@loop:
mov eax, DWORD PTR _Devices$$[esp+64]
lea edx, DWORD PTR _Device$$[esp+64]
push edx
mov DWORD PTR _Device$$[esp+68], 0
mov ecx, DWORD PTR [eax]
push edi
push eax
call DWORD PTR [ecx+16]
mov esi, eax
test esi, esi
js @@skip
mov eax, DWORD PTR _Device$$[esp+64]
lea edx, DWORD PTR _devID$$[esp+64]
mov DWORD PTR _devID$$[esp+64], 0
mov ecx, DWORD PTR [eax]
push edx
push eax
call DWORD PTR [ecx+20]
mov esi, eax
test esi, esi
js @@skip
mov eax, DWORD PTR _Device$$[esp+64]
lea edx, DWORD PTR _Store$$[esp+64]
push edx
mov DWORD PTR _Store$$[esp+68], 0
mov ecx, DWORD PTR [eax]
push 0
push eax
call DWORD PTR [ecx+16]
mov esi, eax
test esi, esi
js SHORT @@skip
mov eax, DWORD PTR _Device$$[esp+64]
push eax
mov ecx, DWORD PTR [eax]
call DWORD PTR [ecx+8]
mov eax, DWORD PTR _Store$$[esp+64]
lea edx, DWORD PTR _friendlyName$$[esp+64]
push edx
xorps xmm0, xmm0
movdqa XMMWORD PTR _friendlyName$$[esp+68], xmm0
mov ecx, DWORD PTR [eax]
push OFFSET _PKEY_Device_FriendlyName
push eax
call DWORD PTR [ecx+20]
mov esi, eax
test esi, esi
js SHORT @@skip
mov eax, DWORD PTR _Store$$[esp+64]
push eax
mov ecx, DWORD PTR [eax]
call DWORD PTR [ecx+8]
push DWORD PTR _friendlyName$$[esp+72]
push edi
push OFFSET sDevN
call _printf
add esp, 12
lea eax, DWORD PTR _friendlyName$$[esp+64]
push eax
call DWORD PTR __imp__PropVariantClear@4
cmp edi, DWORD PTR _Param$$[esp+64]
jne SHORT @@skip
mov ecx, DWORD PTR _devID$$[esp+64]
call myChangeInternalDevice
@@skip:
inc edi
cmp edi, DWORD PTR _loopCount$$[esp+64]
jb @@loop
@@fail:
mov eax, DWORD PTR _Devices$$[esp+64]
push eax
mov ecx, DWORD PTR [eax]
call DWORD PTR [ecx+8]
mov ecx, DWORD PTR _Enum$$[esp+64]
call myPrintDefaultDevice
mov eax, DWORD PTR _Enum$$[esp+64]
push eax
mov ecx, DWORD PTR [eax]
call DWORD PTR [ecx+8]
pop edi
mov eax, esi
pop esi
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
Visual Studio 개발환경 Command Prompt 창 열어 놓고, 아래와 같이 build.bat을 실행하면 컴파일/링크가 될 것임.
파라미터 없이 다음과 같이 test.exe 를 실행하면, 현재 PC에 있는 Audio Endpoint Device 들이 출력될 것임.
내컴엔 메인보드 내장형의 리얼텍과 사운드 블라스트가 같이 설치되어 있고, 기본 사운드 장치로 사블의 SPDIF가
선택되어 있는 상태임.
이 상태에서 "test 2"로 Device Number가 2인 장치를 선택하도록 파라미터를 넘겨주면 다음과 같이 기본 사운드 장치가 바뀌게 됨.
COM을 프로그래밍 해본 경험이 있다면, 작성된 소스 코드를 이해하는데 별 어려움이 없을 것임. 파일도 같이 첨부함.
사족으로 낼모레면 환갑인 사람도 이렇게 닭짓을 하는데, 젊은 사람이 그렇게 노력 없이 거저 먹으려고 하는 건 ㅋ
하두고 님이 쓰신 글 :
: 로그인을 안했더니 이름 없이 답변이 올라가네요.
: 버그인가? 아니면 마음씨 좋은 관리자님의 배려?
:
: 힌트를 주셔서 감사하구요.
:
: 너무 거저 먹으려 해서 죄송 한데요.
: 혹시 테스트했던 자료가 남아 있으면 주실 수 없는지요?
:
: 님이 쓰신 글 :
: : 헐....
: : 그 어려운걸 저보고 하라니요....
: : 나 이제 죽었다....
: :
: : 그래도 캄캄했는데 힌트라도 주시니 감사합니다.
: :
: : 지나다가 님이 쓰신 글 :
: : : COM 오브젝트를 인젝션해서 분석해 보면 답이 나옴 ㅋ.
: : :
: : : 기본 사운드 장치 선택은 rundll32.exe 라는 호스트 프로세스에 의해서 로드 되는 mmsys.cpl 이라는
: : : Control Panel Applet에 의해서 선택되게 되어있고,
: : :
: : : class GUID가 870af99c_171d_4f9e_af0d_e63df40c2bc9 인 COM 객체를 통해서,
: : : GUID가 f8679f50_850a_41cf_9c72_430f290290c8 인 Interface 를 취하고 있음.
: : :
: : : MS사에서 이와 관련한 COM Interface에 대한 헤더파일을 제공하고 있지 않기 때문에 Interface 이름에 대해선 알수 없지만
: : : 내부적으로 사용하는 이름이 SetDefaultEndpointDevice? 정도가 아닐까 하는 추론이 가능함.
: : :
: : : GUID가 f8679f50_850a_41cf_9c72_430f290290c8 인 COM Interface의 Virtual Method Table을 분석해 보면
: : : 도합 12개의 Virtual Method를 정의해서 사용하고 있는 것을 알수 있는데,
: : :
: : : 이중 0 기준으로 10번째 인덱스로 정의 되어 있는 인터페이스 버추얼 메소드가 사운드 기본장치를 선택하는데 사용되고 있음
: : :
: : : IMMDeviceEnumerator 로 얻은 스트롱 네임의 Endpoint Device ID 중에 하나를 파라미터로 이 인터페이스에 Dummy 값 하나와
: : : 같이 넘겨주면 제어판 경유 없이, 다이렉트로 프로그래밍 방법으로 강제적으로 원하는 기본 사운드 장치를 선택할 수 있음.
: : :
: : : 테스트 해보니까 Win7, Win8 다 먹힘 ㅋ
: : :
: : :
: : :
: : :
: : :
: : :
: : :
: : :
: : : 하두고 님이 쓰신 글 :
: : : : 그냥 제어판의 사운드창을 백그라운드에 띄워놓고
: : : : 창에 나열된 사운드 장치 정보를 읽어서
: : : : 선택한 후 [기본장치]버튼을 눌러주도록 해서 처리했습니다.
: : : : 뭐 간단하게 해결되네요.
: : : :
: : : : 하지만 문제는 사운드 장치 이름만 읽어오고
: : : : 장치 이름 아래 있는 설명을 읽어올 수 없어 사운드 장치가 모두 "스피커"라고 표시되서
: : : : 도데체 어느 스피커가 어떤 사운드 장치의 스피커인지 알 수가 없네요.
: : : :
: : : : 리버스엔지니어링 님이 쓰신 글 :
: : : : : 윈도우에서 디폴트 사운드 장치를 선택하는 방법은 제어판을 통한 방법 밖에 없습니다
: : : : : MS에서 그런 API를 제공했다간 사운드카드 만드는 회사들이 경쟁적으로 자기들 카드를 디폴트 사운드장치로
: : : : : 설정하려는 사태가 벌어지겠죠. 여러 사운드장치 중 하나로 웨이브 데이타가 출력되게 할수는 있어도 시스템 디폴트
: : : : : 사운드장치를 선택해주는 API는 없습니다.
: : : : :
: : : : : 해결 방법은 비스타 이후 COM으로 바뀐 윈도우 오디오 아키텍쳐를 리버스엔지니어링으로 해킹하는 방법 밖에는 없습니다.
: : : : : COM은 일반적인 API처럼 노출되는게 아니라 해킹하기도 쉽지 않습니다. 그럼에도 불구하고 정 필요하고 패이 조건이 맞으면
: : : : : 리버스엔지니어링으로 해킹해 드릴 수는 있습니다
: : : : :
: : : : :
: : : : :
: : : : : 하두고 님이 쓰신 글 :
: : : : : : 앞에서 했던 질문인데 시원한 답변이 없어 여기 저기 돌아다니며 찾은 소스코드입니다.
: : : : : : 아래 소스코드는 피시에 장착된 사운드장치를 나열하는 소스코드인데요.
: : : : : :
: : : : : : 아래 소스에서 나열된 사운드장치를 선택하여 더블클릭하면
: : : : : : 해당 사운드장치가 윈도우 기본 사운드장치로 바뀌게 하고 싶은데요.
: : : : : :
: : : : : : 기본 사운드 장치를 바꾸는 방법을 모르겠습니다.
: : : : : :
: : : : : : procedure TForm1.EnumAudioDevices;
: : : : : : var
: : : : : : dsCreateDevEnum : ICreateDevEnum;
: : : : : : EnumDevice : IEnumMoniker;
: : : : : : DeviceMoniker : IMoniker;
: : : : : : Data : Integer;
: : : : : : DevicePropBag : IPropertyBag;
: : : : : : DeviceName : OLEVariant;
: : : : : : begin
: : : : : : If CoCreateInstance(CLSID_SystemDeviceEnum,nil,CLSCTX_INPROC_SERVER,IID_ICreateDevEnum,dsCreateDevEnum) = S_OK then
: : : : : : Begin
: : : : : : If dsCreateDevEnum.CreateClassEnumerator(CLSID_AudioRendererCategory,EnumDevice,0) = S_OK then
: : : : : : Begin
: : : : : : EnumDevice.Reset;
: : : : : : While EnumDevice.Next(1,DeviceMoniker,@Data) = S_OK do
: : : : : : Begin
: : : : : : If DeviceMoniker.BindToStorage(nil,nil,IID_IPropertyBag,DevicePropBag) = NOERROR then
: : : : : : Begin
: : : : : : If DevicePropBag.Read('FriendlyName',DeviceName,nil) = NOERROR then
: : : : : : IF Copy(DeviceName, 1, Length('DirectSound')) = 'DirectSound' Then
: : : : : : ListBox1.Items.Add(Copy(DeviceName, Length('DirectSound: ') + 1, Length(DeviceName) - Length('DirectSound: ')));
: : : : : : DevicePropBag := nil;
: : : : : : End;
: : : : : : DeviceMoniker := nil;
: : : : : : End;
: : : : : : EnumDevice := nil;
: : : : : : End;
: : : : : : dsCreateDevEnum := nil;
: : : : : : End;
: : : : : : End;