x64 Native Tools Command Prompt for VS 2022 실행

git clone https://github.com/Microsoft/Detours.git
cd detours
nmake

VS 2022로 Detours\vc\Detours.sln 오픈 후, x64, x86 각각 빌드


VS에서 새 DLL 프로젝트를 만들고, 해당 디렉토리에 아래의 파일을 복사

Detours\lib.X??\*.lib
Detours\include\*.h

속성 -> 링커 -> 입력 -> 추가 종속성에 아래의 파일을 추가

detours.lib
syelog.lib

아래와 같이 후킹 코드를 작성하고 빌드한다.

#include "pch.h"
#include <stdio.h>
#include "detours.h"

HANDLE(WINAPI* OrgCreateFileW)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE) = CreateFileW;

HANDLE WINAPI MyCreateFileW(
    _In_ LPCWSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
)
{
    wprintf(L"%s\n", lpFileName);    
    return OrgCreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
}

BOOL Start()
{
    AllocConsole();
    FILE* fp;
    freopen_s(&fp, "CONOUT$", "w", stdout);
 
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)OrgCreateFileW, MyCreateFileW);
    DetourTransactionCommit();
    return TRUE;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        Start();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

아래의 DLL Injector로 테스트가 가능하다.

 

GitHub - TarekExister/UWP-Dll-Injector-32bit-64bit: universal windows platform (uwp) apps Dll injector [32bit-64bit]

universal windows platform (uwp) apps Dll injector [32bit-64bit] - TarekExister/UWP-Dll-Injector-32bit-64bit

github.com

 

'Windows > Dev' 카테고리의 다른 글

[Visual Studio 2022] curl 빌드  (0) 2023.01.30
[VC++] string deallocate  (0) 2022.05.23
get EIP (gcc / vc)  (0) 2019.04.11
C용 초경량 XML 파서 : Mini-XML 소개 및 사용법  (0) 2017.06.29
x86 __usercall 함수 후킹하기  (0) 2017.05.11
pragma solidity ^0.8.20;

contract ContractCall {

	address public owner;
	uint256 public num;
	address public lib;
	
	constructor() {
		owner = msg.sender;
	}
	function setLibAddress(address _Lib) public {
		lib = _Lib;
	}
	function nowOwner() public view returns (address) {
		return owner;
	}
	function callDelegate(uint256 _num) public payable returns (bool)
	{		
		(bool success, ) = lib.delegatecall(abi.encodeWithSignature("changeOwner(uint256)", _num));
		return success;
	}
}
pragma solidity ^0.8.20;

contract ChangeOwner {

    address public owner;
    uint256 public num;

    function changeOwner(uint256 _num) public {
        owner = msg.sender;
        num = _num;
    }
}

각각 deploy 후 setLibAddress에 ChangeOwner 컨트랙트의 주소를 넣고 callDelegate를 호출하면 num 및 owner가 변경된다.

주의할 점은 변수의 순서가 일치하여야 한다.

만약 아래와 같이 코딩했다면, ContractCall의 owner에는 num, num에는 owner가 저장된다.

contract ContractCall {
	address public owner;
	uint256 public num;
	address public lib;
    
contract ChangeOwner {
	uint256 public num;
	address public owner;

remix로 확인 결과

x64 Native Tools Command Prompt for VS 2022 오픈

editbin /dynamicbase:NO FSViewer1.exe

 

0. Windows 11 에서 정상적인 실행이 되지 않으므로, hyper-V 등을 통해서 windows 10에서 실행하는 것을 권장한다.

1. 다운로드 : Python3, Visual studio 2022, DynamoRIO(https://github.com/DynamoRIO/dynamorio/releases)

2. 적절한 경로에 DynamoRIO 압축 해제

3. config.h 에서 input 파일 최대 사이즈 수정  (1*1024*1024 를 아래와 같이 수정)

/* Maximum size of input file, in bytes (keep under 100MB): */

#define MAX_FILE            (100 * 1024 * 1024)

4. x64 Native Tools Command Prompt for VS 2022 열기

git clone https://github.com/googleprojectzero/winafl
cd winafl
git submodule update --init --recursive
mkdir build64
cd build64
cmake -G"Visual Studio 17 2022" -A x64 .. -DDynamoRIO_DIR=I:\DynamoRIO\cmake -DTINYINST=1 -DUSE_DRSYMS=1 -DINTELPT=1 -DUSE_COLOR=1
cmake --build . --config Release

5. harness 작성

#include <iostream>
#include <windows.h>

typedef int(__stdcall* _OHMYGOD)(const char* data); 
_OHMYGOD func;

extern "C" __declspec(dllexport) __declspec(noinline) int fuzzme(const char* path)
{   
    int result = func(path);
    return result;
}

int main(int argc, char *argv[])
{    
    HMODULE hMod = GetModuleHandle(0);
   
    hMod = LoadLibrary(L"I:\\victim\\x64\\Release\\victim.dll");
    if (NULL == hMod)
    {
        printf("dll load error\n");
        return 0;
    }
   
    func = (_OHMYGOD)GetProcAddress(hMod, "ohmygod");
    fuzzme(argv[1]);    
}

6. victim 작성 (타겟 DLL)

#include "pch.h"
#include <iostream>

extern "C" __declspec(dllexport) int ohmygod(const char* path)
{
    std::cout << path << std::endl;
    char data[40] = { 0, };
    char buf[30] = { 0, };
    
    HANDLE hFile = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hFile)
    {
        DWORD dwRead;
       
        ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
        std::cout << "read : " << dwRead << std::endl;
        if (dwRead)
        {
            memcpy(buf, data, dwRead+40);
            std::cout << buf;
        }
        CloseHandle(hFile);
    }
    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

원활한 crash 발생을 위해 각각 Release 로 빌드한다.

7. 입출력 디렉토리 설정

afl은 사용자가 지정한 입력 디렉토리에 있는 파일들을 퍼징 데이터로 사용한다. 

crash를 유발하지 않는 정상 입력 데이터이어야 첫 실행 시 에러가 발생하지 않는다.

아래 데이터를 1.txt로 저장한다.

abcd

8. 공격

afl-fuzz.exe -D I:\\dynamorio\\bin64  -i "d:\\fuzz_input" -o "d:\\fuzz_output" -t 1000 -- -coverage_module victim.dll -target_module harness.exe -target_method fuzzme -fuzz_iterations 10 -nargs 1 -- I:\\harness\\x64\\Release\\harness.exe @@

실행은 반드시 afl-fuzz.exe 디렉토리에서 하여야 한다. 그렇지 않다면 아래와 같은 에러가 발생한다.

주의 : -coverage_module 및 -target_module 에는 파일명만 기입한다.

아래와 같은 화면이 나오면 실행 성공이다.

crash가 발생할 경우, i:\fuzz_output\crashes 경로에 상세 정보가 저장된다.

만약 CPU가 인텔인 경우, -D 옵션 대신 -P옵션을 사용하도록 하자. 속도가 압도적으로 빠르다. (내 PC에서는 5배 이상 차이가 났다.)

afl-fuzz.exe -P -i "d:\\fuzz_input" -o "d:\\fuzz_output" -t 1000 -- -coverage_module victim.dll -target_module harness.exe -target_method fuzzme -fuzz_iterations 10 -nargs 1 -- I:\\harness\\x64\\Release\\harness.exe @@

 

sudo passwd root
su
apt-get update
apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker

아래의 에러가 발생한다면..

root@DESKTOP-3NGJPPV:/home/server/kiloton-main# sudo systemctl start docker
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

아래처럼 파일을 수정하고, wsl --shutdown으로 종료 후 wsl을 재시작한다.

sudo vim /etc/wsl.conf
[boot]
systemd=true

 

bogus control flow (bcf)

핵심 : 불투명 술어로 코드 제어

예시

int x = 1;
if (x + x == 3)
{
	// 불투명 술어 거짓. 실행되면 안되므로 더미 코드가 들어감
}
if (x - 1 == 0)
{
	// 불투명 술어 참. 실행되어야 하는 코드
}
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main()
{
        srand(time(NULL));  
        int i = rand() % 3;      

        if (i == 0)
        {
               printf("0\n");
        }
        else if (i == 1)
        {
               printf("1\n");
        }
        else if (i == 2)
        {
               printf("2\n");
        }
        else if (i == 3)
        {
               printf("3\n");
        }

        return i;
}

IDA Result (Original)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // w0
  int v5; // [xsp+8h] [xbp-8h]

  v3 = time(0LL);
  srand(v3);
  v5 = rand() % 3;
  if ( v5 )
  {
    switch ( v5 )
    {
      case 1:
        printf("1\n");
        break;
      case 2:
        printf("2\n");
        break;
      case 3:
        printf("3\n");
        break;
    }
  }
  else
  {
    printf("0\n");
  }
  return v5;
}

IDA Result (Obfuscation)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // w0
  int v4; // w19

  v3 = time(0LL);
  srand(v3);
  v4 = rand() % 3;
  switch ( v4 )
  {
    case 2:
      puts("2");
      break;
    case 1:
      if ( ((x + x * x) & 1) != 0 && y > 9 )
        goto LABEL_16;
      while ( 1 )
      {
        puts("1");
        if ( ((x + x * x) & 1) == 0 || y < 10 )
          break;
LABEL_16:
        puts("1");
      }
      break;
    case 0:
      if ( ((x + x * x) & 1) != 0 && y > 9 )
        goto LABEL_13;
      while ( 1 )
      {
        puts("0");
        if ( ((x + x * x) & 1) == 0 || y < 10 )
          break;
LABEL_13:
        puts("0");
      }
      break;
  }
  return v4;
}

 

  케이스1 (ret) 케이스2 (br) 케이스3 (b) 케이스4 (b) 케이스5 (br) 케이스6 (ret)
IDA 7.7 분석 안됨 분석 안됨 분석 안됨 정상 분석 됨 정상 분석 됨 분석 꼬임
Binary Ninja 3.3 분석 안됨 정상 분석 됨 정상 분석 됨 정상 분석 됨 정상 분석 됨 분석 꼬임

 

케이스 1 (ret)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");
    __asm__ volatile(
        "adr x30,0x0 \n\t"
        "add x30,x30,0xc \n\t"
        "ret \n\t"
        );
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

 
방해 asm 이후 함수가 끝난 것으로 표시됨

분석이 asm 이후 되지 않음
분석이 asm 이후 되지 않음

Binary Ninja 3.3.3996 분석 결과

방해 asm 이후 함수가 끝난 것으로 표시됨
분석이 asm 이후 되지 않음

 

케이스 2 (br)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");
    __asm__ volatile(
        "adr x30,0x0 \n\t"
        "add x30,x30,0xc \n\t"
        "br x30 \n\t"
        );
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

분석이 asm 이후 되지 않음

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

케이스 3 (b)

#include <ctime>
int main(int argc, char const *argv[])
{
    printf("hello world!\n");
   
    // 항상 false가 되어야 실행되지 않는다.
    // 최적화 되지 않기 위해 불투명 술어로 코딩한다.
    if (rand() < 0) {
        __asm__(
            "b 0x4\n"
            ".long 12345678\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

함수 전체가 분석 안됨

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

 

케이스 4 (b)

#include <ctime>
int main(int argc, char const *argv[])
{
    printf("hello world!\n");
   
    // 항상 false가 되어야 실행되지 않는다.
    // 최적화 되지 않기 위해 불투명 술어로 코딩한다.
    if (rand() < 0) {
        __asm__(
            "b 0x4\n"
            "add sp,sp,#0x100\n"
            "add sp,sp,#0x100\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

정상적으로 분석 됨

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

케이스 5 (br)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");

    if (rand() < 0) {
        __asm__(
            "mov x8,#0x1\n"
            "adr x9, #0x10\n"
            "mul x8, x9, x8\n"
            ".long 0x12345678\n"
            "br x8\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

정상적으로 분석 됨

Binary Ninja 3.3.3996 분석 결과

정상적으로 분석 됨

케이스 6 (ret)

int main(int argc, char const *argv[])
{
    printf("hello world!\n");

    if (rand() < 0) {
        __asm__(
            "adr x8,#0xc\n"
            "mov x30,x8\n"
            "ret\n"
        );
    }
    printf("test 1\n");
    printf("test 2\n");

    return 0;
}

IDA 7.7 분석 결과

분석 꼬임

Binary Ninja 3.3.3996 분석 결과

분석 꼬임

 

 

제어 흐름 난독화란? if, switch와 같은 제어/분기 구문에 난독화를 하는 것

아래는 예제이다.

package obfuscate.test;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import java.util.Random;
import bam.boo.zoo;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Random을 넣은 이유는, 상수를 넣으면 최적화 되어 버린다.
        Random rand = new Random();
        int num = rand.nextInt(100000);
        System.out.println("hohoh!oho " + num);
        zoo.func1(num);

        System.out.println("end!");
    } 
}

난독화 테스트를 위해 제어 흐름을 if 구문을 사용하여 코딩하였다.

package bam.boo;

public class zoo {
    public static void func1(int num)
    {
        if (num > 1)
        {
            System.out.println("if");
        }
        else
        {
            System.out.println("else");
        }
    }
}

zoo class를 decompile하면 아래와 같다.

package a.a;

import java.io.PrintStream;

public class a {
   public static void a(int var0) {
      PrintStream var1;
      String var2;
      if (var0 > 1) {
         var1 = System.out;
         var2 = "if";
      } else {
         var1 = System.out;
         var2 = "else";
      }

      var1.println(var2);
   }
}

아래는 smali 코드이다.

smali 코드로 변환하면서 goto 명령어가 생성되었다.

goto와 같은 분기 명령어에 pass를 적용한 후 dummy 코드를 포함하여 ifne/goto 명령어를 적절히 추가하면 분기가 복잡하게 보이도록 만들 수 있다.

@Override
public void visitProgramClass(ProgramClass programClass)
{
    if ((programClass.getAccessFlags() & AccessConstants.INTERFACE) != 0)
    {
        return;
    }
    // 수를 참조할때 static 변수가 아니면 최적화되어 버리므로 번거롭지만 생성하여야 한다.
    ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor(programClass);

    ClassEditor classEditor = new ClassEditor(programClass);
    int nameIndex = constantPoolEditor.addUtf8Constant("valueX");
    int descriptorIndex = constantPoolEditor.addUtf8Constant("I");
    
    ProgramField opaqueField = new ProgramField(
        AccessConstants.PRIVATE | AccessConstants.STATIC, nameIndex, descriptorIndex, null);
    classEditor.addField(opaqueField);

    new InitializerEditor(programClass).addStaticInitializerInstructions(/*mergeIntoExistingInitializer=*/true,
            // static 변수에 저장
            ____ -> {
                ____.ldc(rand.nextInt(10000)) // 양수 생성
                    .putstatic(programClass, opaqueField);
            });
    programClass.accept(new AllMethodVisitor(new AllAttributeVisitor(this)));
}

@Override
public void visitBranchInstruction(Clazz             clazz,
                                   Method            method,
                                   CodeAttribute     codeAttribute,
                                   int               offset,
                                   BranchInstruction branch)
{
    // goto 명령어에만 적용한다.
    if (branch.opcode != Instruction.OP_GOTO) {
        return;
    }
    InstructionSequenceBuilder ____ = new InstructionSequenceBuilder((ProgramClass)clazz);
    FrameFinder finder = new FrameFinder(this.partialEvaluator, offset);
    codeAttribute.instructionsAccept(clazz, method, finder);
    if (!finder.targets.isEmpty())
    {
            // x + 1 != 0
            // x 값으로 양수를 생성했으므로 위의 수식은 무조건 성립한다.
            ____.getstatic(clazz.getName(), "valueX", "I") // 위에서 생성한 x값
                .iconst_1() // 상수 1
                .iadd() // 덧셈
                .ifne(branch.branchOffset) // 0이 아니면 분기
                // 여기서부터 절대 실행되면 안됨(dummy)
                .getstatic("java/lang/System", "out", "Ljava/io/PrintStream;")
                .ldc("never seen")
                .invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V")
                .goto_(finder.targets.get(rand.nextInt(finder.targets.size())) - offset);
                // 끝
                
                // 명령어 교체
                codeAttributeEditor.replaceInstruction(offset, ____.instructions());
    }
}

위 pass를 적용하면 goto 명령어가 getstatic ~ goto로 대체된다.

apk 빌드 후 smali 코드를 확인해 보면 아래와 같다.

위에서는 ifne가 goto 명령어를 대체하므로, 무조건 참이 되는 수식을 작성하여 원래 가고자 했던 곳으로 점프시킨다.

그 밑에는 실행되면 안되는 fake코드를 넣고, 마지막에는 goto 명령어를 넣어서 임의의 위치를 가리키게 한다.

 

java 코드로 변환하면 아래와 같이 난독화 된 것을 볼 수 있다.

x64 Native Tools Command Prompt for VS 2022 프롬프트 실행

git clone https://github.com/curl/curl.git
cd curl
buildconf.bat
Set RTLIBCFG=static
cd winbuild
nmake /f MakeFile.vc mode=static vc=17

빌드는 아래 경로에 생성 됨

curl\builds\libcurl-vc17-x64-release-static-ipv6-sspi-schannel

curl.zip
1.25MB

프로젝트 설정

#include <curl.h>
int main()
{
    CURL* curl = curl_easy_init();
    CURLcode res;
}

 

'Windows > Dev' 카테고리의 다른 글

detours 빌드 및 적용  (0) 2024.04.11
[VC++] string deallocate  (0) 2022.05.23
get EIP (gcc / vc)  (0) 2019.04.11
C용 초경량 XML 파서 : Mini-XML 소개 및 사용법  (0) 2017.06.29
x86 __usercall 함수 후킹하기  (0) 2017.05.11

1. ipsw.me에서 기종 + 버전 선택 후 다운로드

2.

맥 : https://github.com/blacktop/ipsw 다운로드 

우분투(wsl에서 실행 불가) : sudo snap install ipsw 

우분투 : apfs-fuse  다운로드

apfs-fuse
0.44MB

./ipsw extract -d iPhone13,1_15.6.1_19G82_Restore.ipsw

3. https://github.com/keith/dyld-shared-cache-extractor 다운로드 후 make

mac용 다운로드

extractor
0.03MB

4. dyld_shared_cache_arm64e 추출

./extractor dyld_shared_cache_arm64e ~/bin

'iOS' 카테고리의 다른 글

[iOS] C++, Objective C, Swift 상호 함수 호출  (0) 2022.10.18

+ Recent posts