Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
Prineo_RE — 아케이드 PriPara 게임을 구동하는 엔진의 리버스 엔지니어링 | Kitploit
도구/GitHubGitHub/exifdev/prineo_re
Embedded Systems SecurityReverse EngineeringCryptographyBinary AnalysisLearning & EducationFirmware Analysis
GitHubexifdev/prineo_re

Prineo_RE

아케이드 PriPara 게임을 구동하는 엔진의 리버스 엔지니어링

저장소 보기
54개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

소개

PriPara (내부명 Prineo)는 SynSophia에서 개발한 아케이드 리듬 게임입니다.

목표

이 프로젝트의 목표는 아케이드 시리즈의 모델 포맷을 리버스 엔지니어링하여 독점 캐릭터를 Nintendo Switch의 최신 버전 게임으로 이식하는 것이었습니다.

스크린샷

RE 과정 중 찍은 몇 가지 흥미로운 스크린샷입니다.


Early Noesis addon import Noesis 애드온 초기 버전 — 첫 부분 모델 가져오기 (2025-09-14)


C# model reader 역직렬화 구현을 테스트하는 데 사용된 C# 모델 리더 (2025-12-06)


Hooking hashing function VM에서 복호화된 덤프를 실행하고, MinHook과 Xenos를 사용한 DLL 인젝션을 통해 해싱 함수를 후킹하고, ReturnAddress 내장 함수를 사용하여 해시 함수 호출을 추적하는 모습 (2025-12-08)


Imported scene Noesis 애드온을 통해 게임에서 가져온 씬 (2025-12-27)


Imported model Noesis 애드온을 통해 암호화된 리소스에서 가져온 스킨 모델


Ported model Ported model2

PriPara 아케이드에서 NintendoSwitch 버전으로 이식된 드레스, Noesis 플러그인을 사용하여 내보낸 모델


커스텀 암호화 구현

이 게임은 표준 암호화 프리미티브의 수정된 버전을 구현합니다.


1. FNV-1

  • 연산 순서 반전
  • 초기 오프셋 베이시스 누락

Implementation: https://github.com/ExIfDev/Prineo_RE/blob/main/Noesis/fmt_prineo.py#L1130-L1140


2. Mersenne Twister

  • 덧셈 대신 뺄셈 사용
  • 커스텀 승수 0x13F8769B
  • 31비트로 마스킹됨
  • 커스텀 상수를 사용한 변형된 트위스트 연산
  • 수정된 마스크 상수

Implementation: https://github.com/ExIfDev/Prineo_RE/blob/main/Noesis/fmt_prineo.py#L1074-L1128

파일 포맷 연구

  • Game: PriPara (Prineo)
  • 손방향: 오른손
  • Endianness: LE
  • 삼각형 와인딩: 시계방향
  • Type: sparse chunk
  • MaxPolyC: 65535
  • MaxBoneC: 255

다음 문서는 SSZL 압축 해제 후의 파일을 참조합니다.

.BIN (일반 컨테이너)

root@kitploit:~
const byte[] ident = {0x00, 0x13, 0x10, 0x09};
struct Header
{
    uint32 VERSION;//1
    uint32 FILE_SIZE;
    byte[4] Ident;
    int32 unk1;
    int32 unk2;
    int32 unk3;
    uint32 DATA_OFFSET; //absolute offset
    uint32 DATA_SIZE;
};


.MDJ

root@kitploit:~
//the MDJ file can contain a variety of buffers related to geometry and scenes, it does
//not have a header, its assumed to start with one of the following buffers
//and always terminate with the "end/0/" marker


struct Pose //pose applied to a skeleton (=restPose in a model container) 
            //usually this pose is the same as in the "base" buffer.
            //if AnimParams are present then what follows is the animation curves.
            //AnimParams can be null when there are no keyframes.
{
    char[4] ident = "pose";
    uint16 strLen;
    char[strLen] POSE_NAME;
    byte[2] unk;
    uint16 BONE_COUNT;
    byte[8] unkdata;

    struct Bone[BONE_COUNT]
    {
        uint16 serializer_id;//518
        uint16 strLen;
        string[strLen] BONE_NAME;
        //Base transform
        float sclX, sclY, sclZ;
        float rotX, rotY, rotZ;
        float tslX, tslY, tslZ;

        //for all channels (9) "Sx", "Sy", "Sz", "Rx", "Ry", "Rz", "Tx", "Ty", "Tz"

        struct AnimationParams //6 bytes
        {
            uint16 unk2; //possibly an uint32 since unk3 is always 0
            uint16 unk3;
            uint16 CHANNEL_MASK;
        };
        //if anim params are not null then....
        uint32 KEYFRAME_COUNT;
        struct Keyframe[KEYFRAME_COUNT]
        {
            float VALUE;//radians
            float TIME;//centiseconds
        }:
    };
};

struct TextureDefs //define texture name and id bindings
{
    string[4] ident ="imag";
    uint16 TEX_COUNT;
    struct TexDef[TEX_COUNT]
    {
        uint16 serializer_id;//1029
        uint16 unk;
        uint16 TEX_ID;
        uint16 strLen;
        string[strLen] texName;
    };
};

struct MaterialChunk //defines parameters of materials and shaders
{
    string[4] ident ="mate";
    uint16 MAT_COUNT;

    struct Material[MAT_COUNT]
    {
        uint16 serializer_id;//1024
        uint16 strLen;
        string[strLen] MatName;
        uint32 TYPE;//..maybe hash? looked up in a registry

        //Texture slots

        //ambient
        uint16 aCount;
        for (int i = 0; i < aCount; i++)
        {
            uint16 serializer_id;
            if (serializer_id == 1029)
            {
                uint32 unk;
            }
            else
            {
                uint32 unk2;
            }
        }

        //diffuse
        uint16 dCount;
        for (int i = 0; i < dCount; i++)
        {
            uint16 serializer_id;
            if (serializer_id == 1029)
            {
                uint32 unk;
            }
            else
            {
                uint32 unk2;
            }
        }

        //specular
        uint16 sCount;
        for (int i = 0; i < sCount; i++)
        {
            uint16 serializer_id;
            if (serializer_id == 1029)
            {
                uint32 unk;
            }
            else
            {
                uint32 unk2;
            }
        }

        //possibly parameter slots

        uint16 unk1Count;
        for (int i = 0; i < unk1Count; i++)
        {
            uint16 serializer_id;
            float unk12;
        }

        uint16 unk2Count;
        for (int i = 0; i < unk2Count; i++)
        {
            uint16 serializer_id;
            float unk22;
        }

        byte HAS_TEXTURE;

        if (HAS_TEXTURE == 1)
        {
            uint16 TEX_COUNT;
            struct TexureMap[TEX_COUNT]
            {
                uint16 serializer_id;
                uint32 TEX_ID;
            }  
        }

        uint16 strLen;
        string[strLen] unkNullString;    
    };

    //two possible chunks could follow:

    struct MPSS_Subchunk //clear usage unknown
    {
        string header = "MPSS";
        uint16 count;
        struct MPSS_Entry[count]
        {
            uint16 unk32;
            byte[16] payload;
        }
    }

    struct MPDS_Subchunk //string to value shader parameter chunk
    {
        string header = "MPDS";
        uint16 count;

        struct MPDS_Entry[count]
        {
            uint16 strLen;
            string[strLen] Name;
            byte[32] value;
        }
    }
};

struct LightChunk //defines parameters of lights in a scene
{
    char[4] ident = "ligh";
    int16 LIGHT_COUNT;

    struct light[LIGHT_COUNT]
    {
        uint16 serializer_id;
        uint16 strLen;
        char[strLen] light_name;
        byte[49] unk40; //transform matrix along with color vals
    }

}

struct ModeBuffer//marks the start of a submesh list and its buffers                                 
{
    char[4] ident = "mode";
    uint16 serializer_id;//513
    uint16 strLen;
    char[strLen] unkName; //possibly the root node
    byte[4] unk; 
    uint16 unkC;  
};

struct MeshMeta//a submesh starts with some metadata about its bounding size,name, and its boneMap
{
    uint16 serializer_id;//770 0x0302
    uint16 strLen;
    char[strLen] MESH_NAME; 
    byte[4] unk;  
    uint16 boneMapCount;
    struct HashedBoneMap[boneMapCount]
    {
        uint32 boneHash;//SSFNV1a hashed bone name 
    }
    float unk3;
    byte unk2;
    byte[24] AABB;
}
struct MeshBegin //indicates the beginning of a submesh
{
    string[4] ident ="verb";
};
struct FaceBuffer 
{
    string[4] ident = "surf";
    int16 FACE_COUNT;
    struct Face [FACE_COUNT]//stride: 6 //type: triangle list // winding:clockwise
    {
        uint16 A;
        uint16 B;
        uint16 C;
    };
};

struct VertexBuffer 
{
    string[4] ident = "coor";
    uint16 VERTEX_COUNT;
    struct Vertex [VERTEX_COUNT] //stride 12
    {
        float x;
        float y;
        float z;
    };
};

struct NormalBuffer
{
    string[4] ident ="norm";
    uint16 NORM_COUNT; //should be the same as VERTEX_COUNT
    struct Normal[NORM_COUNT]//stride 12
    {
        float nx;
        float ny;
        float nz;
    };
};

struct BinormalBuffer//binormal buffer (not present in any file)
{
    string[4] ident ="bino";
    uint16 BINO_COUNT; //should be the same as VERTEX_COUNT
    struct BiNormal[NORM_COUNT]//stride 12
    {
        float bx;
        float by;
        float bz;
    };
};

struct UV0Buffer
{
    string[4] ident ="tex0";
    uint16 UV0_COUNT;    //should be the same as VERTEX_COUNT
    struct UV[UV0_COUNT] //stride 8
    {
        float u;
        float v;
    };
};

struct UV1Buffer
{
    string[4] ident ="tex1";
    uint16 UV1_COUNT;    //should be the same as VERTEX_COUNT
    struct UV[UV1_COUNT] //stride 8
    {
        float u;
        float v;
    };
};

struct VertexColors
{
    string[4] ident = "colo";
    uint16 VERTEX_COUNT;   //should be the same as VERTEX_COUNT
    struct UV[UV1_COUNT] //stride 4
    {
        byte r,g,b,a;
    };
}


struct TangentBuffer //signless tangent vector

{
    char[4] ident ="tan ";
    uint16 TANG_COUNT;      //should be the same as VERTEX_COUNT
    struct Tang[TANG_COUNT]//stride 12
    {
        float tx;
        float ty;
        float tz;
    };
};

struct WeightBuffer //Weight values for each bone assigned to a vertex
{
    char[4] ident ="weig";
    uint16 WEIGHT_COUNT;
    struct VertexWeight[WEIGHT_COUNT] //stride 16 
    {
        float w1;
        float w2;
        float w3;
        float w4;
    };
};

struct VBIBuffer //Vertex Bone Index, in order, bone indices that influence a vertex
                 //this table is local to the current vertex buffer
{
    char[4] ident ="bone";
    uint16 VERTEX_COUNT;
    struct VBI[VERTEX_COUNT]
    {
        byte boneIDX1;
        byte boneIDX2;
        byte boneIDX3;
        byte boneIDX4;
    };
};


//
struct Skeleton //Bone Node Tree, defines the bone relationships
{
    char[4] ident ="skel";
    struct BoneName[BONE_COUNT] //Bone count read from POSE chunk
    {
        uint16 serializer_id; //257=root 258=child
        uint16 strLen;
        char[strLen] BONE_NAME;
        uint32 unk2;
        uint16 CHILD_COUNT;
    };
};

struct RestPose //defines the rest pose of the skeleton
{
    char[4] ident ="base";
    uint16 strLen;
    char[strLen] POSE_NAME;
    int16 BONE_COUNT;
    struct BoneTrs[BONE_COUNT]
    {
        uint16 serializer_id;//1537
        uint16 strLen;
        char[strLen] BONE_NAME;
        float sx, sy, sz; //scale
        float rx, ry, rz; //rotation
        float tx, ty, tz; //translation
    };
};



struct MeshEnd //indicates the end of a mesh
{
    string[4] ident ="vere";
    uint16 strLen;
    char[strLen] NAME;
    byte[2] padding;

};

struct EndFlag //indicates the end of the file
{
    string[4] ident ="end "
}

_tex.BIN (텍스처 컨테이너)

root@kitploit:~
//information is related to the decompressed file
struct Header
{
    uint32 VERSION;
    uint32 FILE_SIZE;
    uint32 tex_ident;
    uint32 unk;
    uint32 TEXTURE_COUNT;
};

struct TextureDecl[TEXTURE_COUNT]
{
    uint32 HASH;//hashed texture name minus the extension
    uint32 OFFSET;//absolute offset
    uint32 SIZE;
};
//follows texture buffers at OFFSET with SIZE
//TGA or DDS

감사의 말

  • @REDxEYE: 머티리얼 버퍼 리버스 도움
도구 다운로드