이전 분석한 결론은 다음과 같다.
1, LogonUser API는 내부적으로 LogonUserExExW를 호출한다.
2. 계정정보를 담을 수있는 구조체가 있는데 그구조체에 계정 정보 등 보안 명세 등을 담는다.
3. LogonUserExExW 는 정보가 있는 구조체에서 로컬 계정인지 원격지 계정인지 확인한다.
여기까지 해서 0x75739835 Intruction이 마지막 이었으나 다시 올리면 어짜피 주소가 달라질테다.
75669835 57 PUSH EDI ; TestLogo.__native_startup_lock
75669836 83CF FF OR EDI,FFFFFFFF
------------------------------------------------------------------------------------
EDI = 0040337C || 0xFFFFFFFF = 0xFFFFFFFF;
HANDLE hEventHdl = INVALID_HANDLE_VALUE;
핸들 선언하고 초기화 하는작업인데, 이전에 JMP로 분기되어 넘어 왔기때문에.
if(!(struct.Int & DEFINE_VALUE_MODE_@))
{
struct.int = DEFINE_VALUE_MODE_@;
//아마도 제가 짠소스에서 로칼 PC에 있는 계정에 들어갈때 사용되는 값이 2 인가 봅니다.
//그럼 선언문은 대충 #define LOCALHOST_ACCOUNT_LOGON_MODE 2 이정도 되겐네요
}
...
else
{
...
}
{
HANDLE hEventHdl = INVALID_HANDLE_VALUE;
}
하나의 함수 내에서 지역구분자 뒤로 넘어와서 선언된 변수 인듯.
그런데 생각해보니 PUSH EDI 만 했으니... 전역 변수 그냥 초기화 문일지도 모르겠다는 생각 막드네..;;;
------------------------------------------------------------------------------------
75669839 393D 40706775 CMP DWORD PTR DS:[75677040],EDI
7566983F ^ 0F84 22F2FFFF JE 75668A67
------------------------------------------------------------------------------------
75677040 FF FF FF FF = 0xFFFFFFFF
그러니 아까 OR연산으로 초기화된 핸들이 진짜 인발리드 한 핸들인지 확인해보니깐
아마 다음에는 점프 해서 갈 75668A67 여기에서는 핸들을 오픈 한다든지 하는 작업 하겠지.
if( hEventHdl == INVALID_HANDLE_VAUE)
{
//.... 여기가기 이 Instruction 위치임.
//__asm{ JE 75668A67 }
}
------------------------------------------------------------------------------------
75668A67 BE 50716775 MOV ESI,75677150
75668A6C 56 PUSH ESI
75668A6D FF15 A0106675 CALL DWORD PTR DS:[756610A0] ntdll.RtlEnterCriticalSection
------------------------------------------------------------------------------------
RtlEnterCriticalSection 을 호출하기전 전역적으로 선언된 75677150 이변수에 초기화를 한다.
그치만 그럼 이함수의 원형을 보면
void EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
그러면 여기서 PUSH 한 ESI 의 값은 LPCRITICAL_SECTION 그이 되게 된다. 한마디로 크리티컬 섹션 처리를 하는 거니깐 전역 변수중 해당 구조체로 선언된 녀서의 포인터가 되게된다.
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
//
// The following three fields control entering and exiting the critical
// section for the resource
//
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
이구조체의 포인터다. 그냥 이건 이렇게 보면 된다는거만 보여줄라고 쓴거임 별내용없이 크리티컬 섹션 처리 해주는거 한마디로 특정작업을 할때 전역적인 데이터 레이스 방지를 위해서 처리 한거니 그닥 중요하진 않다.
(코드짤때는 멀티 쓰레드 환경에서 데이터 레이스 현상은 매우 중요할지 몰라도 분석할땐 이딴거 크게 중요 하지 않음 ㅋㅋㅋㅋ)
------------------------------------------------------------------------------------
75668A73 393D 40706775 CMP DWORD PTR DS:[75677040],EDI
75668A79 ^ 0F85 96BDFFFF JNZ 75664815
------------------------------------------------------------------------------------
이부분에서 아까 점프하기전 초기화한 전역 변수 핸들 hEventHdl 이 IVALID_HANDLE_VALUE인지 확인 하는군요 그리고 INVALID면 75664815 로 가게 되는데 아까 초기화 해서 유효하지 않은 값을 넣었으니 같다 그럼 CMP Instruction은 참이되고 그럼 ZeroFlag는 1로 셋팅. 이말인 즉슨
if(hEventHdl != INVALID_HANDLE_VALUE)
{
//75664815 여기
}
그래서 이부분에서는 그냥 넘어가게되고 아래 명령문 실행 될꺼임.
어짜피 이런코들은 거의 핸들 초기화하고 넘어온 핸들 현재 상태를 확인해서 핸들 오픈할려하는거 같은데, 왜 초기화하고 확인다시 하냐는건 API 자체가 멀티스레드 환경을 지원한다면 항상 전역 변수에 대한 값이 이전에 확인한 값이라는 보장을 하지 못하게된다. 그러면 항상 확인하고 더이상 그값에 대해 다른 쓰레드가 바꾸지 않게끔 해줘야 한다. 그래서 앞전에 크리티컬 섹션 처리를 해서 다른 쓰레드는 대기 할수 있도로 한거.
------------------------------------------------------------------------------------
75668A7F E8 19000000 CALL 75668A9D
------------------------------------------------------------------------------------
예상한대로 이부분에서 핸들을 열지 않을까 한다.
그러니 아래에 브레이크를 건다음에 안으로 스텝인투 해보자
------------------------------------------------------------------------------------
75668A84 85C0 TEST EAX,EAX ; 브레이끼
75668A86 0F84 7A730000 JE 7566FE06
75668A8C 56 PUSH ESI
75668A8D FF15 9C106675 CALL DWORD PTR DS:[7566109C] ntdll.RtlLeaveCriticalSection
75668A93 E9 B90D0000 JMP 75669851
75668A9D 8BFF MOV EDI,EDI
75668A9F 55 PUSH EBP
75668AA0 8BEC MOV EBP,ESP
//항상 함수의 std콜중 마소 지네들 패치 편하자고 5바이트 의미 없는코드 넣는다 별로 의미 없음.
75668AA2 83EC 10 SUB ESP,10 //스택 세그먼트 맞추기
/////////////////////////////////////////////////////////////////////////////////////////
75668AA5 56 PUSH ESI
75668AA6 57 PUSH EDI
75668AA7 68 70716775 PUSH 75677170
75668AAC E8 E6EEFFFF CALL 75667997
------------------------------------------------------------------------------------
ESI = 75677150 ->00 57 30 00 = 00305700("^\0^")
EDI = FFFFFFFF
75677170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
75677180 93 1F 66 75 74 25 67 75 94 73 66 75 45 34 67 75 ?fut%gu봲fuE4gu
75677190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
756771A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
756771B0 00 00 00 00 00 10 00 00 00 00 01 00 FF FF FE 7F ..........?
756771C0 0F 00 00 00 04 00 00 00 4A 02 00 00 00 00 01 00 ......J.....
지금 ESI 값은 아까 크리티컬 섹션 처리 할때 썻던 매게 변수인데 이거 전역 변수이다.
그리고 CALL 안으로 가보자
------------------------------------------------------------------------------------
75668AB1 8BF8 MOV EDI,EAX < 브레이끼
75667997 8BFF MOV EDI,EDI
75667999 55 PUSH EBP
7566799A 8BEC MOV EBP,ESP
7566799C 83EC 34 SUB ESP,34
//스텍 세그먼트 맞추는 작업
7566799F 53 PUSH EBX
756679A0 56 PUSH ESI
756679A1 57 PUSH EDI
756679A2 68 40796675 PUSH 75667940
[75667940 ; UNICODE "\SECURITY\LSA_AUTHENTICATION_INITIALIZED"]
756679A7 8D45 E4 LEA EAX,DWORD PTR SS:[EBP-1C]
756679AA 33FF XOR EDI,EDI
756679AC 50 PUSH EAX
756679AD 897D FC MOV DWORD PTR SS:[EBP-4],EDI
756679B0 FF15 88106675 CALL DWORD PTR DS:[75661088] ntdll.RtlInitUnicodeString
------------------------------------------------------------------------------------
Unicode String Initialyze Routine
-> ZwOpenEvent (열려는 대상은\SECURITY\LSA_AUTHENTICATION_INITIALIZED)
호출 하기위해서 매계변수들에 대한 처리 작업중인데 이부분은 우선 두가지 작업
- RtlInitUnicodeString을 호출하여 연결대상 네임버퍼를 초기화하고있다.
UNICODE_STRING ustrEventNamedPipe = {0,};
RtlInitUnicodeString(&ustrEventNamedPipe,
L"\\SECURITY\\LSA_AUTHENTICATION_INITIALIZED");
와 같은 소스코드로 바꿀수 있다.
------------------------------------------------------------------------------------
756679B6 8B1D 8C106675 MOV EBX,DWORD PTR DS:[7566108C] ; ntdll.ZwOpenEvent
------------------------------------------------------------------------------------
ZwOpenEvent의 함수 포인터를 지역변수로 옮긴다.
PFN_ZW_OPEN_EVENT pfn_ZwOpenEvent = (PFN_ZW_OPEN_EVENT)ZwOpenEvent;
------------------------------------------------------------------------------------
756679BC 8D45 E4 LEA EAX,DWORD PTR SS:[EBP-1C]
756679BF 8945 D4 MOV DWORD PTR SS:[EBP-2C],EAX
756679C2 8D45 CC LEA EAX,DWORD PTR SS:[EBP-34]
756679C5 50 PUSH EAX
756679C6 68 00001000 PUSH 100000
756679CB 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]
756679CE 50 PUSH EAX
756679CF C745 CC 1800000>MOV DWORD PTR SS:[EBP-34],18
756679D6 897D D0 MOV DWORD PTR SS:[EBP-30],EDI
756679D9 C745 D8 4000000>MOV DWORD PTR SS:[EBP-28],40
756679E0 897D DC MOV DWORD PTR SS:[EBP-24],EDI
756679E3 897D E0 MOV DWORD PTR SS:[EBP-20],EDI
------------------------------------------------------------------------------------
OBJECT_ATTRIBUTE 구조체 초기화
OBJECT_ATTRIBUTE objattr = {0,};
//구조체 초기화
InitializeObjectAttributes(objattr,&ustrEventNamedPipe,100000,...);
머 이런작업 세세한건 각자 해보시길...귀차네...
------------------------------------------------------------------------------------
756679E6 FFD3 CALL EBX
------------------------------------------------------------------------------------
pfn_ZwOpenEvent(&hEventHdl,0xFFFFFFFF/*Desired 값 귀찮음....확인하기*/,)
머 이렇게 대충해서 Call일이 난뒤에 로그인 이벤트가 완료 되는걸 기다리겠지.
이밑으로는 다음에 WinDbg로 커널 디버깅해서 또 블러깅 하겠음 머 암튼 이렇게해서 누구인지는 몰라도 해당 이벤트를 받아 처리하게 된다.
이 이벤트를 처리하는 녀석이 누구일까 싶어 후킹해서 알아볼려 한다. 다음에는 후킹을 통해서 오브젝트 네임 파이프가 \\SECURITY\\LSA_AUTHENTICATION_INITIALIZED 이녀석일때 어떤짓을 하는지 확인해보려한다. 물론 ZwOpenEvent를 후킹 해야 할것이다. 궁금한분들 한번 해보시길 ㅋㅋㅋ 윈도우 로그인 과정 파보면 먼가 재밌는것도 할수 있을듯 한데 말이지. ㅋㅋㅋ
머 어쨋든 여기까지 정리 한번 해보겠음.
1. 누가 받아 처리하는 이벤트 인지는 몰라도 APC레벨로 돌아가는 이벤트를 하나 깨움으로해서
로그인 과정이 이루어 지는듯하다.
2. 윈도우 로그인 과정은 LogonUser를 호출한 프로세스에서 Dispatch되지 않고 다른 시스템 프로세스에 로그인 이벤트를 알려 처리된다.
3. 로그인이벤트 과정중에 \\SECURITY\\LSA_AUTHENTICATION_INITIALIZED 이러한 파이프 네임이 무언지는 몰라도 보안 속성관련되어 이벤트를 이르킨다.
그리고 하나로 지금까지는 유저모드에서 분석 결과를 받았지만 앞으로 해야 할것은 아래와같다.
1. ZwOpenEvent가 호출되었으므로 커널로 진입하였다.
2. 진입할때 Event를 호출하여 이미 IRQL 은 APC레벨이니깐 분명 이벤트 Set되어 처리될것이다.
3. 그럼 2를 수행하기위해 특정 프로세스가 해당 이벤트를 처리 하기위해 다시한번 올라오게 될텐데. 이걸 확인하기 위해서는 ZwOpenEvent가 어느 루틴으로 흘러가는냐가 중요할것이다.
사실 Event핸들을 등록할때는 네임 파이프가 커널로 진입하면서 이벤트들이 활성화되는걸로 안다.
그럼 ZwOpenEvent에서는 CallBack이라든지 유저 프로세스또는 서비스와 노티할수 있는 Call이 생길것이다. 아마 Noti를 위해서 Noti Event변수를 깨우지 않을까 하는 짐작을 해본다.
어쨋든 이렇게 해서 대충 결론은 내려진듯하다.
------------------------------------------------------------------------------------
1. LogonUser API는 호출되면 해당 프로세스에서 처리되는것이 아니라 특정 이벤트에 의해 처리된다.
2. LogonUser API 이벤트 활성화 API다.
3. 그럼 커널로 진입할때 ZwOpenEvent를 통해 진입
다음 분석 블로깅할때는 ZwOpenEvent를 후킹해서 저녀석을 뜯어 봐야 할듯하다.
그럼 아직 끝난게 아니니 오늘은 바쁘기도 하고 아무튼 해서 여기까지 하고 다음 블로깅땐 더 자세하게 파해쳐 봐야 게다.
다들 즐프~
