yenas0님과 함께한 실습의 아카이빙입니다.
어떤 부모 프로세스가 자식 프로세스를 생성한다.
근데 그 자식 프로세스가 자기 자신일 때 이걸 self creation이라고 한다.
부모로서 실행될 때와, 자식으로서 실행될 때의 동작이 다르다면 이건 짚고 넘어갈 만하다.
하나의 PE파일이지만 실행경로가 두개인 것이다.
이런 것들은 보통 메인함수에 들어오는 파라미터에 따라서 operation type을 다르게 지정해주면 된다. 뭐 if, else if 조건문 쓰면 되지 않겠니...
부모 프로세스가 실행 되면 부모의 main함수가 실행된다.
메인에서 이제 자식프로세스를 생성하는데, 이때 suspend 모드로 생성한다.
보통 부모 프로세스가 fork() 또는 CreateProcess() 같은 시스템 호출을 이용해 자식 프로세스를 만들 때, 자식은 즉시 실행(READY 상태)가 된다.
하지만 SUSPEND 모드로 생성하면:
자식 프로세스의 메모리 공간이 할당되고, Import DLL들이 로딩됨.
메인 스레드(Main Thread)는 실행되지 않고 멈춘 상태(SUSPENDED)로 대기함.
이 때문에 Entry Point(EP) 코드가 실행되지 않음.
부모 프로세스가 원하는 시점에 자식 프로세스의 메인 스레드를 깨워서 실행할 수 있음.
멈춰 있는 자식 프로세스의 메인 스레드를 깨울 준비를 해야 한다.
메인 스레드의 context 값을 얻어서, eip를 원하는 주소로 옮긴다.
자식 프로세스의 코드 주소로 EIP가 맞춰졌으니 이제 실행을 재개한다.
이제 메인스레드를 실행하면, 우리가 놓은 주소의 코드가 실행되겠지.
코드로 말한다면
메인에서 CreateProcess API를 이용해서 자기 자신을 SUSPEND MODE로 실행
- DLL은 모두 임포트 됨
- 메인스레드만 멈춘 상태
CreateProcess API의 리턴 파라미터엔 PROCESS_INFORMATION 구조체가 있음.
- 자식 프로세스 관련 정보가 담겨 있다.
- 이 구조체의 hThread 멤버가 자식 프로세스의 메인 스레드 핸들이다.
GetThreadContext( ) API에 이 핸들을 입력하면 스레드 컨텍스트 구조를 얻는다.
- 스레드의 모든 정보가 이 구조체에 담겨있지.
- EIP 멤버를 이제 자식 프로세스의 함수 주소로 바꿔낸다.
-> 아니 근데 그럼 원래 EIP에는 무얼 담고 있었던거야? 원래도 자식 프로세스의 메인스레드 정보를 가진거잖아. 그럼 이 구조체에 담긴 eip도 어쨌든 "자식 프로세스의 어딘가"인거 아님?
자식 프로세스의 어딘가를 가리키는 EIP를 다시 자식 프로세스의 함수 주소로 바꿀 이유가 있나
지피티야 도와줘.
GPT : 원래 EIP는 자식 프로세스의 기본 Entry Point(EP) 를 가리키고 있음.
보통 Windows의 스레드 실행 루틴 (BaseThreadInitThunk) 같은 시스템 코드일 가능성이 높음.
우리가 원하는 실행 흐름(예: 특정 함수, 후킹한 코드, 쉘코드)로 바꾸려면 EIP를 수정해야 함.
SUSPEND 모드의 자식 프로세스는 디버거를 attach할 수 없다. 디버거 툴로 attach 메뉴 열어보면 가능한 목록에 자식 프로세스의 pid자체가 안 뜰 것이다.
실행 중인 프로세스에 예외가 발생하면, OS는 직접 지정한 디버거를 예외 발생 지점부터 attach 시켜준다.
그러기 위해선 우선 예외 발생시 디버거를 나의 디버거 툴로 지정해야 한다. 뭐 ollydbg든 x96dbg든 뭐든.
레지스트리 편집기 열어서 지정하세용
열어서 메인까지 실행시키셔
호출하고 나면, 자식 프로세스가 SUSPEND MODE로 실행한다.
자식 프로세스의 메인스레드 컨텍스트 구조체를 구하여 EIP 멤버를 우리가 원하는 자식 프로세스의 함수 주소로 바꾼다.
예: ChildProc( )의 주소가 401000이면 그것으로 변경한다.
전제조건
✔ 자식 프로세스에서 내가 원하는 함수의 VA(가상 주소)를 알아야 함.
✔ ASLR이 활성화된 경우, 주소가 매번 바뀔 수 있으므로 런타임에 GetProcAddress()로 찾아야 함.
✔ DLL에 있는 함수는 LoadLibrary() + GetProcAddress()를 사용해서 해결 가능.
이후엔 뭐 자식프로세스의 메인스레드를 재개한다.
그리고 자식 프로세스가 종료할 때까지 기다리는 것이지. 부모란게 그런게 아니겠어? 자식이 다 클 때까지 묵묵히 지켜보는거지. WaitForSingleObject API를 호출한다는 소리임.
여기가 더 어려움 ㄹㅇ
여기서부터 시작이야
아까 우리가 원하는 자식프로세스의 함수 주소가 401000이었지.
401000이 파일 상태에서 offset을 구해 (뭐 studPE 써도 되고, 직접 계산해도 되고)
JIT 디버깅의 핵심은 "예외 발생"이다. 그래서 우리는 여기다가 예외를 주입해야 한다.
이 함수의 명령어를 0xCC로 바꾼다.
0xCC = BreakPoint 발생 예외
그래서 이제 이걸 실행하면 EXCEPTION_BREAKPOINT 예외가 발생하는 것이다.

아까 지정한 디버거로 열어야 하니까 프로그램 디버그로 실행하면 이제 디버거가 실행되는 것이,
그럼 이제 원하는 자식프로세스의 함수 위치에서부터 디버깅할 수 있다.
그 전에!
0xCC로 편집했던 원본 코드를 디버거에서 편집해준다. 그래야 정상적인 동작이 될테니까.
이제 디버깅하세여