윈도우즈는 vista 시리즈부터 major kernel version이 6으로 올라갔다.
커널 6에선 보안을 더 신경쓰면서 달라진 기능들이 있는데, ALSR과 Session 0 isolation이다. 그리고 CreateRemoteThread( ) 불가능이다.
Address Space Layout Randomization은 프로세스가 실행되어 메모리에 탑재될 때마다 로딩되는 메모리 주소의 시작을 매번 다르게 랜덤화하는 기술이다.
항상 고정된 메모리 주소를 가지게 된다면 그점을 노린 보안 취약점이 발생할 수 있기 때문이다.
그런데 리버싱하는 입장에선 이 ASLR 때문에 PE를 항상 같은 환경에서 분석하기 어렵게 한다 주소 체계가 좀 낯설겠지 아마도
그래서 ASLR 기능을 끌 수 있는 법을 알려주겠다.
ASLR이 적용된다는 것은 주소의 재배치, 재할당, relocation이 적용된다는 것이다. 그 점에서 기존의 PE와 다른 정보들이 생긴다.
첫번째론, 기존에 없던 .reloc이란 섹션이 생긴다.
주소 재배치를 할 때 이 섹션을 보고 참고한다.

내가 aslr을 끄고 싶다면, 이 섹션을 지워도 괜찮은 것이다.
단, DLL파일은 항상 재배치를 고려해야 해서 지우면 안된다.
더 중요한 것은 NT헤더 중에서 IMAGE_FILE_HEADER에 가보면 Characteristics 멤버가 있다. PE에서 이 이미지가 가진 고유한 성질들의 값을 한데 모은 값이다.
ASLR이 적용되면 IMAGE_FILE_RELOCS_STRIPPED라는 속성이 해제되어서 아예 플래그가 빠져있다.
원래는 이렇게 있어야 한다.

IMAGE_OPTIONAL_HEADER에서 DLL Characteristics에 들어가보면, ALSR이 적용된 파일은 DYNAMIC BASE로 끝나는 성질이 추가되어 있다. 성질값은 40이다.

역동적 베이스, 즉 시작 주소를 다이나믹하게 매번 바꿔준다는 것이지.
그럼 어떻게 끄냐
Hex 값을 편집할 수 있는 에디터 아무거나로 저 DLL Characteristics에 있는 성질값 40을 빼주면 된다.
아까 보면 DLL Characteristics의 값은 다 합치면 8140이었지. 여기 가보면 리틀 엔디언으로 4081 되어있을거야. 그걸 40빼면 8100이니까 00 81로 바꿔주면 돼.
세션은 로그온 사용자 환경이다.
사실 이렇게 말하면 로그온이 뭔지도 말해야 한다. 접근 허용 정도로만 생각하자.
운영체제는 동시에 여러 사용자의 로그온을 허가한다. 각 로그온은 독립된 사용자 환경을 제공받는다.
로컬 사용자 세션, 원격 사용자 세션 등등이 있지
작업관리자에서 이런거 볼 수 있어.
만약에 지금 이 OS에 두명의 사용자가 로그온되어 있다고 하자. 뭐 사용자 전환 기능이나 원격 데스크톱 연결 기능을 쓴거지.
그냥 당연하게 생각하면 사용자가 둘이면 세션 개수도 둘.

근데 커널 6에선 다르다.

세션ID가 3개다.
세션id 0은 시스템 세션이다. 시스템 프로세스와 서비스 프로세스는 이 시스템 세션에서 실행된다.
커널 6부터는 사용자 세션과 시스템 세션을 구별한다.
첫번째 로그온 사용자의 세션을 1부터 배정한다.
원래 커널 6 이전엔 첫번째 로그온 사용자 세션을 그냥 0으로 똑같이 줬다.
그래서 XP에서 잘 돌아가는 기술들도 커널 6에선 안되기도 한다.
이런 정책을 세션 0 아이솔레이션이라고 한다.
근데 좀 허술하다. 세션0 프로세스들이 완전 분리된 것은 아니라서 세션 1의 프로세스에서 세션 0의 프로세스에 접근해서 프로세스들을 종료할 수 있다.
디버깅API들도 그대로 동작한다고 한다.
뭐 ReadProcessMemory( )
WriteProcessMemory( )
VirtualAllocEx( )
원래 우리가 알던 DLL 인젝션은 CreateRemoteThread( )를 이용했다.
- 프로세스 핸들 구하고 (OpenProcess)
- 타깃 프로세스 메모리에 인젝션할 DLL 문자열이 들어갈 공간 마련해주고 (VirtualAllocEx)
- 이렇게 할당받은 메모리 공간에 dll 경로 적어주고 (WriteProcessMemory)
- 타깃프로세스의 LoadLibrary( ) 주소를 구하는데 타깃이나 인젝터나 똑같은 주소에 로드되니까 지껄로 구했지.
- 원격 스레드 실행 (RemoteThread)
하지만 커널6 버전에선 세션0의 프로세스에 dll 인젝션을 하는 것이 안된다.
세션 1에 있는 메모장 이런건 또 되는듯?
kernel32!CreateRemoteThread( )를 뜯어보자.
이것의 파라미터는 아래와 같았다.
- 타깃 프로세스 핸들: 내가 공격할 프로세스의 핸들, 뭐 PID
- LoadLibrary( ) API 주소 : kernel32!LoadLibrary( )의 API주소를 받아야해ary( )의 API주소를 받아야해
- 타깃 프로세스에서 할당받은 메모리 버퍼 주소
커널 6에선 이런 차이가 생긴다. API를 파고 파고 내려가보면 ntdll!ZwCreateThreadEx( ) API가 호출된다.
근데 파라미터의 개수가 늘어났다.
그리고 API의 호출관계가 늘어나면서 문제가 생겼다.

즉, ntdll!ZwCreateThreadEx( )부터 더 파고 가면 SYSENTER이라는 명령어가 나오고 커널 모드로 진입하게 된다. 그럼 기존의 유저 모드 디버깅으론 더 이상 진행할 수가 없는 것이다.
ntdll!ZwCreateThreadEx( )는 마소에서 막 공개한 API도 아니라서 구글링해서 이 구조를 좀 봐야한다.
DWORD ZwCreateThreadEx
(
PHANDLE ThreadHandle,
ACCESS_MASK ACCESS_MASK
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE IpStartAddress,
LPVOID IpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dwl,
DWORD dw2,
PUNKNOWN pUnknown
);
커널 6에서 인젝션이 실패할 땐 저 7번째 CreateSuspended가 False(0)이더라 이거야.
근데 직접 ZwCreateThreadEx( )를 호출할 땐 저 CreateSuspended가 True(1)더라.
CreateRemoteThread( ) api말고 ZwCreateThreadEx( )를 직접 호출하면 세션ID에 상관없이 잘 된다.
즉, 디버깅을 ZwCreateThreadEx( )까지 진행하고 스택에 7번째로 쌓인 CreateSuspended 값을 1에서 0으로 바꿔줘라.
이케 보면 지금 1이잖아.

걍 0으로 바꾸란거임.
이거 말고도 ZwCreateRemote( ) API가 홏ㄹ될 때 조건문에 의해 ZwResumeThread( )를 호출하지 않고 아래로 건너뛴다.
ZwResumeThread()를 호출하면 되는거지.
점프 조건은 SF!=OF이다.
그럼 S Flag를 더블클릭해서 변경하면 되겠네.
근데 이건 좀 어려워보이더라