티스토리 뷰

Etc

[SQLite] SQLite 바이트코드 엔진

Tribal 2018. 11. 20. 14:27

개요


  SQLite는 바이트코드를 사용해서 동작하는데, 이 바이트코드를 공부하는건 일반적인 프로그램 개발에 전혀 필요하지 않다. 단순히 SQLite가 어떻게 내부에서 쿼리를 처리하는지 궁금할 수 있기에 이를 공부하고자 한다. SQLite에서도 이런걸 예상하였는지 SQLite 바이트코드 엔진이 어떻게 동작하는지를 문서로 작성하였고, 이를 참고하였다. 맨 마지막 참고에 링크를 남겨두었다.


  우선 SQLite의 바이트코드 엔진 구조는 다른 바이트코드 엔진을 사용하는 언어와 별로 다를게 없다. 아래의 사진이 SQLite의 전체 구조를 나타낸 그림이다.


  바이트코드로 변환하는 코어 부분이 다른 바이트코드 언어와 동일하게 인터페이스로부터 받아 이를 토큰화 및 파싱을 통해 바이트코드로 생성한 다음, 해당 바이트 코드를 실행시킬 수 있는 가상 머신에서 이를 처리하는 순서로 이루어지는 것을 확인할 수 있다.


  현재 궁금한 내용은 위 그림에서 Core의 SQL Command Processor에서 생성된 바이트코드 언어가 가상 머신에서 어떻게 실행하는지 이므로 대부분 생략하고 어떻게 실행하는지를 위주로 볼 것이다.



API 및 용어

  • 용어
    • vdbe : Virtual Data Base Engine, 데이터베이스를 처리하기 위한 바이트코드를 실행하는 엔진
    • prepared statement : 데이터 베이스를 처리하기 위한 바이트코드로 작성(변환)된 일종의 바이트코드 프로그램
  • API(sqlite3.c 또는 vdbe.c 참고)
    • sqlite3_prepare : SQL 쿼리문을 SQL 바이트코드로 변환하는 API 함수, sqlite3_stmt 객체( = Vdbe 객체)를 반환
    • sqlite3_step : SQL 쿼리문을 가상 머신에서 실행시켜 처리하는 API 함수
      • sqlite3VdbeExec : sqlite3_step 함수 내부에서 실제로 바이트코드를 읽어들여 처리하는 함수 (엄청난 라인의 함수....)
    • sqlite3_finalize : sqlite3_prepare 함수로 생성된 sqlite3_stmt 객체를 정리(free)


들어가기 전


  바이트코드의 Opcode 값이 SQLite의 모든 버전이 동일하지 않다. 패치를 통해 새롭게 생성된 Opcode도 존재하고, 기존의 Opcode로 정의한 값이 다른 값으로 변경된 부분도 매우 많다. 따라서 특정 버전의 SQLite Opcode의 값이 어떤 의미인지 알고 싶다면 해당 버전의 SQLite 소스 코드를 구해 직접 컴파일을 해서 컴파일 도중 생성된 opcodes.h를 보면 된다.

  SQLite의 소스 코드를 구하는 방법과 빌드하는 방법(for Linux, SQLite 버전이 중요)은 다음과 같다.

  1. Release(https://www.sqlite.org/src/timeline?t=release)에서 버전 및 Release된 년도를 확인
  2. 버전 정보를 7자리로 표준화, 예를 들어 3.8.7.4인 경우는 3080704, 잘 이해가 안 되면 아래의 관련 참고의 TomWitt2 라는 분이 남긴 내용 확인
    ※ 관련 참고 : https://stackoverflow.com/questions/615213/where-can-one-find-source-archives-of-old-sqlite-versions
  3. 'http://www.sqlite.org/YEAR/sqlite-src-7DIGITS.zip' 에서 YEAR 부분을 Release된 년도, 7DIGITS을 표준화된 7자리 숫자로 변경
  4. 3번의 url에 접속해서 해당 버전의 소스 코드 다운로드
    ※ 1번에서 특정 버전을 check-in에 걸린 링크로 접속해서 Downloads - ZIP archive를 클릭해서 받아도 됨, 이 경우 2번과 3번을 생략해도 됨
  5. unzip 커맨드로 압축 해제
  6. 압축해제된 디렉토리로 이동
  7. ./configure 파일 실행
  8. make 커맨드 실행
  9. 8번의 빌드 성공 실패 여부와 관계없이 현재 디렉토리에 opcodes.h가 생성될 텐데, 이를 참고


바이트코드 명령어 형식


  API에 대해서 간략하게 설명하는 과정에서 sqlite3_prepare 함수를 호출하여 SQL 쿼리문을 바이트코드로 변환해서 prepared statement 객체인 sqlite3_stmt 객체를 반환한다고 하였다. 따라서 sqlite3_stmt 객체의 내부에서 바이트코드를 찾을 수 있다.


  우선 sqlite3_stmt 구조체는 다음과 같다. sqlite3_stmt는 실제로 구현된 구조체가 존재하지 않고, Vdbe 구조체를 명시적으로 자료형변환하여 사용하기 때문에 내부 멤버는 Vdbe 구조체를 따른다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/*
** An instance of the virtual machine.  This structure contains the complete
** state of the virtual machine.
**
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
**
** The Vdbe.inVtabMethod variable is set to non-zero for the duration of
** any virtual table method invocations made by the vdbe program. It is
** set to 2 for xDestroy method calls and 1 for all other methods. This
** variable is used for two purposes: to allow xDestroy methods to execute
** "DROP TABLE" statements and to prevent some nasty side effects of
** malloc failure when SQLite is invoked recursively by a virtual table 
** method function.
*/
struct Vdbe {
  sqlite3 *db;            /* The database connection that owns this statement */
  Op *aOp;                /* Space to hold the virtual machine's program */
  Mem *aMem;              /* The memory locations */
  Mem **apArg;            /* Arguments to currently executing user function */
  Mem *aColName;          /* Column names to return */
  Mem *pResultSet;        /* Pointer to an array of results */
  Parse *pParse;          /* Parsing context used to create this Vdbe */
  int nMem;               /* Number of memory locations currently allocated */
  int nOp;                /* Number of instructions in the program */
  int nCursor;            /* Number of slots in apCsr[] */
  u32 magic;              /* Magic number for sanity checking */
  char *zErrMsg;          /* Error message written here */
  Vdbe *pPrev,*pNext;     /* Linked list of VDBEs with the same Vdbe.db */
  VdbeCursor **apCsr;     /* One element of this array for each open cursor */
  Mem *aVar;              /* Values for the OP_Variable opcode. */
  char **azVar;           /* Name of variables */
  ynVar nVar;             /* Number of entries in aVar[] */
  ynVar nzVar;            /* Number of entries in azVar[] */
  u32 cacheCtr;           /* VdbeCursor row cache generation counter */
  int pc;                 /* The program counter */
  int rc;                 /* Value to return */
  u16 nResColumn;         /* Number of columns in one row of the result set */
  u8 errorAction;         /* Recovery action to do in case of an error */
  u8 minWriteFileFormat;  /* Minimum file format for writable database files */
  bft explain:2;          /* True if EXPLAIN present on SQL command */
  bft inVtabMethod:2;     /* See comments above */
  bft changeCntOn:1;      /* True to update the change-counter */
  bft expired:1;          /* True if the VM needs to be recompiled */
  bft runOnlyOnce:1;      /* Automatically expire on reset */
  bft usesStmtJournal:1;  /* True if uses a statement journal */
  bft readOnly:1;         /* True for statements that do not write */
  bft bIsReader:1;        /* True for statements that read */
  bft isPrepareV2:1;      /* True if prepared with prepare_v2() */
  bft doingRerun:1;       /* True if rerunning after an auto-reprepare */
  int nChange;            /* Number of db changes made since last reset */
  yDbMask btreeMask;      /* Bitmask of db->aDb[] entries referenced */
  yDbMask lockMask;       /* Subset of btreeMask that requires a lock */
  int iStatement;         /* Statement number (or 0 if has not opened stmt) */
  u32 aCounter[5];        /* Counters used by sqlite3_stmt_status() */
#ifndef SQLITE_OMIT_TRACE
  i64 startTime;          /* Time when query started - used for profiling */
#endif
  i64 iCurrentTime;       /* Value of julianday('now') for this statement */
  i64 nFkConstraint;      /* Number of imm. FK constraints this VM */
  i64 nStmtDefCons;       /* Number of def. constraints when stmt started */
  i64 nStmtDefImmCons;    /* Number of def. imm constraints when stmt started */
  char *zSql;             /* Text of the SQL statement that generated this */
  void *pFree;            /* Free this when deleting the vdbe */
  VdbeFrame *pFrame;      /* Parent frame */
  VdbeFrame *pDelFrame;   /* List of frame objects to free on VM reset */
  int nFrame;             /* Number of frames in pFrame list */
  u32 expmask;            /* Binding to these vars invalidates VM */
  SubProgram *pProgram;   /* Linked list of all sub-programs used by VM */
  int nOnceFlag;          /* Size of array aOnceFlag[] */
  u8 *aOnceFlag;          /* Flags for OP_Once */
  AuxData *pAuxData;      /* Linked list of auxdata allocations */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
  i64 *anExec;            /* Number of times each op has been executed */
  int nScan;              /* Entries in aScan[] */
  ScanStatus *aScan;      /* Scan definitions for sqlite3_stmt_scanstatus() */
#endif
};
cs


  여러 멤버들에서 일부만 뽑아서 알아보면 다음과 같다. 

  • aOp : 바이트코드 프로그램, 일반적인 바이너리의 text 섹션처럼 해당 주소에 바이트코드들이 저장되어 있음
  • nOp : 바이트코드 프로그램의 명령어 수
  • aMem : 바이트코드 프로그램이 사용하는 메모리 주소
  • nMem : 메모리의 전체 크기
  • apArg : 바이트코드 함수 호출 시, 함수 인자
  • aColName : Column 명의 배열
  • pc : 바이트코드 프로그램의 program counter
  • rc : 함수 반환 값
  • zSql : 현재 실제 SQL 쿼리문

  aOp가 prepared statement 역할을 수행하는 프로그램인데 Op라는 객체 배열의 형태 이루어져 있고, pc를 객체 배열의 Offset으로 Op 객체를 참조해서 사용한다. 이 의미는 Op 객체가 하나의 명령어(Instruction) 역할을 한다는 것이 되고, 현재 찾고자 하던 바이트코드의 Instruction format으로 이어진다.

※ Op *ins = &aOp[pc];


  Op 구조체는 typedef로 정의된 자료형인데, 원형은 VdbeOp 구조체이다. 이 구조체의 멤버는 빌드 시, 전처리문에 따라 추가될 수 있는데 이 멤버들은 중요하진 않지만 구조체의 크기( = 바이트코드 Instruction 크기)에 영향을 준다는 정도만 알고 있으면 될 것 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* vdbe.h */
/*
** A single instruction of the virtual machine has an opcode
** and as many as three operands.  The instruction is recorded
** as an instance of the following structure:
*/
struct VdbeOp {
  u8 opcode;          /* What operation to perform */
  signed char p4type; /* One of the P4_xxx constants for p4 */
  u8 opflags;         /* Mask of the OPFLG_* flags in opcodes.h */
  u8 p5;              /* Fifth parameter is an unsigned character */
  int p1;             /* First operand */
  int p2;             /* Second parameter (often the jump destination) */
  int p3;             /* The third parameter */
  union {             /* fourth parameter */
    int i;                 /* Integer value if p4type==P4_INT32 */
    void *p;               /* Generic pointer */
    char *z;               /* Pointer to data for string (char array) types */
    i64 *pI64;             /* Used when p4type is P4_INT64 */
    double *pReal;         /* Used when p4type is P4_REAL */
    FuncDef *pFunc;        /* Used when p4type is P4_FUNCDEF */
    CollSeq *pColl;        /* Used when p4type is P4_COLLSEQ */
    Mem *pMem;             /* Used when p4type is P4_MEM */
    VTable *pVtab;         /* Used when p4type is P4_VTAB */
    KeyInfo *pKeyInfo;     /* Used when p4type is P4_KEYINFO */
    int *ai;               /* Used when p4type is P4_INTARRAY */
    SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */
    int (*xAdvance)(BtCursor *int *);
  } p4;
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
  char *zComment;          /* Comment to improve readability */
#endif
#ifdef VDBE_PROFILE
  u32 cnt;                 /* Number of times this instruction was executed */
  u64 cycles;              /* Total time spent executing this instruction */
#endif
#ifdef SQLITE_VDBE_COVERAGE
  int iSrcLine;            /* Source-code line that generated this opcode */
#endif
};
typedef struct VdbeOp VdbeOp;
 
 
/* vdbeInt.h */
/*
** SQL is translated into a sequence of instructions to be
** executed by a virtual machine.  Each instruction is an instance
** of the following structure.
*/
typedef struct VdbeOp Op;
cs


  VdbeOp 구조체의 중요 멤버들은 바이트코드 Instruction으로 다음과 같은 기능을 수행한다.

  • opcode : Instruction이 수행할 동작을 명시
  • opflags : opcode가 수행할 연산을 좀 더 세부적으로 명시
  • p1 : operand, b-tree에서 Instruction이 처리할 위치를 가리키기 위한 cursor 번호
  • p2 : operand, jump 시 목적지로 사용
  • p3 : operand
  • p4type : 멤버 p4의 type을 명시
  • p4 : operand, p4type에 맞춰 다양한 기능 제공
  • p5 : operand, opflags의 기능 보조


Opcodes(define 값은 버전마다 다르므로 개별 버전마다 확인 필요)

  • Abortable : Debug build, Abort가 발생할 수 있는지 확인해서 Abort로 인한 데이터베이스 손상을 방지
    ※ Write 작업이 아니거나 active 상태 저널이 있는 경우, Abort로부터 안전
  • Add : p1 레지스터 값과 p2 레지스터 값을 더해서 p3 레지스터에 저장, 입력 값에 NULL이 1개라도 있다면 결과는 NULL
  • AddImm : 상수 p2 값과 p1 레지스터 값에 더함.
    ※ 결과는 반드시 integer이기 때문에 p1 레지스터 값을 integer로 설정하기 위해서 0을 더하면 됨
  • Affinity : 
  • AggFinal
  • AggInverse
  • AggStep
  • AggStep1
  • AggValue
  • And
  • AutoCommit
  • BitAnd
  • BitNot
  • BitOr
  • Blob
  • Cast
  • Checkpoint
  • Clear
  • Close
  • CollSeq
  • Column : p1 커서가 가리키는 레코드에서 p2 열의 값을 추출해서 p3 레지스터에 저장, 레코드의 값이 (p2 + 1)보다 적게 있다면 NULL을 추출
  • ColumnsUsed
  • Compare
  • Concat
  • Copy
  • Count
  • CreateBtree
  • CursorHint
  • DecrJumpZero
  • DeferredSeek
  • Delete : p1 커서가 현재 가리키고 있는 레코드 제거
  • Destroy
  • Divide
  • Dropindex
  • DropTable
  • DropTrigger
  • ElseNotEq
  • EndCoroutine
  • Eq
  • Expire
  • FkCounter
  • FkIfZero
  • Found
  • Function : 사용자 함수 호출
  • Function0 : 사용자 함수 호출
  • Ge : p3 레지스터 값이 p1 레지스터 값보다 크거나 같으면 점프 수행
  • Gosub
  • Goto : p2가 가리키는 위치로 무조건 점프
  • Gt : p1 레지스터의 값보다 p3 레지스터의 값이 큰 경우, 점프
  • Halt : 즉지 바이트코드 프로그램을 종료하고, 열려있는 모든 커서 등을 닫음
  • GaltIfNull
  • IdxDelete
  • IdxGE
  • IdxGT
  • IdxInsert
  • IdxLE
  • IdxLT
  • IdxRowid
  • If
  • IfNoHope
  • IfNot
  • IfNotZero
  • IfNullRow
  • IfPos
  • IfSmaller
  • IncrVacuum
  • Init : p2가 0이 아닌 경우, 바이트코드 프로그램의 p2( = pc)로 점프. Once Instruction 실행을 위해 p1의 값을 증가
  • InitCorutine
  • Insert : p3 레지스터에 저장된 key인 MEM_Int 값과 p2 레지스터에 저장된 data인 MEM_Blob 값을 커서 p1 커서가 가리키는 테이블에 새로운 항목으로 삽입.
  • InsertInt : Insert와 동잃하게 수행되나 p3 레지스터에 저장된 key를 사용하는게 아닌, Integer 값 p3을 key로 사용
  • Int64 : 64비트 Integer 포인터인 p4가 가리키는 Integer 값을 p2 레지스터에 세팅
  • IntCopy : p1 레지스터에 저장된 Integer 값을 p2 레지스터에 복사
  • Integer : 32비트 Integer 값 p1을 p2 레지스터에 세팅
  • IntegrityCk : 현재 열려있는 데이터베이스를 분석. p1 레지스터에 에러 메세지 또는 NULL을 저장
  • IsNull : p1 레지스터의 값이 NULL이라면 p2로 점프
  • IsTrue
  • JournalMode
  • Jump
  • Last
  • Le
  • LoadAnalysis
  • Lt
  • MakeRecord
  • MaxPgcnt
  • MemMax
  • Move
  • Multiply
  • MustBeInt
  • Ne
  • NewRowid
  • Next : 커서 또는 포인터가 테이블이나 인덱스의 다음 key/data 쌍을 가리키게 함. 다음 key/data 쌍이 없는 경우 다음 명령어로 넘어가고, 다음 key/data 쌍이 있어서 명령어를 성공적으로 수행했다면 p2로 즉시 점프
  • NoConflict
  • Noop : 아무것도 하지 않음
  • Not : p1 레지스터 값을 bool 타입의 값으로 해석하고, 이 값의 보수를 p2 레지스터에 저장. p1 레지스터의 값이 NULL인 경우는 p2 레지스터에 NULL을 저장
  • NotExists
  • NotFound
  • NotNull
  • Null : p3 > p2인 경우, p2와 p3 사이의 모든 레지스터에 NULL을 쓰고, p3 < p2인 경우, p2만 NULL로 세팅
  • NullRow
  • Offset
  • OffsetLimit
  • Once : 바이트코드 프로그램 실행 도중, 처음 실행되는 경우 다음 명령어로 넘어가고, 이후에는 Init Instruction p1과 현재 p1을 비교해 같으면 p2로 점프
  • OpenAutoindex
  • OpenDup
  • OpenEphemeral
  • OpenPseudo
  • OpenRead : database 파일에서 root 페이지가 p2인 데이터베이스 테이블에 대한 읽기 전용 커서를 Open, 데이터베이스 파일은 p3에 의해 결정(0은 기본 데이터베이스, 1은 임시 테이블 데이터베이스, 1보다 큰 경우는 연결된 데이터베이스 사용 의미)
  • OpenWrite : database 파일에서 root 페이지가 p2인 데이터베이스 테이블 또는 인덱스에 대한 읽기/쓰기 커서를 Open
  • Or : p1 레지스터 값과 p2 레지스터 값을 or 연산하여 p3 레지스터에 저장
  • Pagecount : 데이터베이스 p1의 현재 페이지 수를 메모리 셸 p2에 기록
  • Param
  • ParseSchema
  • Permutation
  • Prev
  • Program
  • ReadCookie
  • Real
  • RealAffinity
  • Remainder
  • ReopenIdx
  • ResetCount
  • ResetSorter
  • ResultRow
  • Return
  • Rewind : p1을 대상으로 Rowid나 Column, Next Instruction을 사용하면 데이터베이스 테이블이나 인덱스의 첫 entry를 참조할 것임. 만약 해당 테이블이나 인덱스가 비어 있다면 p2로 즉시 점프하고, 비어 있지 않다면 다음 명령어로 넘어감. 
  • RowData
  • Rowid : p2 레지스터에 p1이 가리키고 있는 테이블 entry의 키인 integer 값을 저장
  • RowSetAdd
  • RowSetRead
  • RowSetTest
  • Savepoint
  • SCopy
  • SeekEnd
  • SeekGE
  • SeekGT
  • SeekHit
  • SeekLE
  • SeekLT
  • SeekRowid
  • Sequence
  • SequenceTest
  • SetCookie
  • ShiftLeft
  • Shift Right
  • SoftNull
  • Sort
  • SorterCompare
  • SorterData
  • SorterInsert
  • SorterNext
  • SorterOpen
  • SorterSort
  • SqlExec
  • String
  • String8
  • Subtract
  • TableLock : 특정 테이블을 Lock. 공유 캐시 기능이 활성화된 경우에만 사용 가능
  • Trace
  • Transaction : 트랜잭션이 활성화되지 않은 경우, p1 데이터베이스에서 트랜잭션을 시작. p2가 0이면 읽기 트랜잭션을 시작하고, p2가 0이 아니거나 읽기 트랜잭션이 이미 활성화된 경우 쓰기 트랜잭션을 시작
  • Vacuum
  • Variable
  • VBegin
  • VColumn
  • VCreate
  • VDestroy
  • VFilter
  • VNext
  • VOpen
  • VRename
  • VUpdate
  • Yield


바이트코드 해석 예시(https://www.sqlite.org/opcode.html의 Viewing The Bytecode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ sqlite3 ex1.db
sqlite> explain delete from tbl1 where two<20;
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     12    0                    00  Start at 12  
1     Null           0     1     0                    00  r[1]=NULL    
2     OpenWrite      0     2     0     3              00  root=2 iDb=0; tbl1
3     Rewind         0     10    0                    00               
4       Column         0     1     2                    00  r[2]=tbl1.two
5       Ge             3     9     2     (BINARY)       51  if r[2]>=r[3] goto 9
6       Rowid          0     4     0                    00  r[4]=rowid   
7       Once           0     8     0                    00               
8       Delete         0     1     0     tbl1           02               
9     Next           0     4     0                    01               
10    Noop           0     0     0                    00               
11    Halt           0     0     0                    00               
12    Transaction    0     1     1     0              01  usesStmtJournal=0
13    TableLock      0     2     1     tbl1           00  iDb=0 root=2 write=1
14    Integer        20    3     0                    00  r[3]=20      
15    Goto           0     1     0                    00
cs


  mysql에서 explain 키워드를 추가하면, 쿼리를 수행하는 과정의 바이트코드를 볼 수 있다. 위의 예시로 바이트코드를 간단하게 해석해 볼 것이다.

  1. 0번 주소 : 12번 주소에서 바이트코드 시작
  2. 12번 주소 : p2가 1이므로, 쓰기 트랜잭션 활성화
  3. 13번 주소 : 0번 데이터베이스(p1 = 0)에서 p4에 있는 테이블에 write lock(p3 = 1) 설정, 이 때 테이블의 root 페이지는 2번(p2 = 2)이다.
  4. 14번 주소 : 3번 레지스터(p2 = 3)에 Integer 값 20 세팅
  5. 15번 주소 : 1번 주소로 점프
  6. 1번 주소 : 1번 레지스터(p2 = 1)에 NULL 값 세팅, p3이 p2보다 작기 때문에 1번 레지스터에만 NULL이 세팅됨
  7. 2번 주소 : 0번 데이터베이스(p3 = 3이므로, 이미 연결된 데이터베이스)를 쓰기 권한으로 열고, 2번 root 페이지(p2 = 2, tbl1)에 대해 읽기/쓰기 커서 생성
  8. 3번 주소 : 커서가 2번 root 페이지의 시작을 가리킴
  9. 4번 주소 : 2번 레지스터(p3 = 2)에 현재 커서(p1 = 0, tbl1)의 1번 열(p2 = 1)의 값을 저장
  10. 5번 주소 : 2번 레지스터(p3 =2, tbl1.two)가 3번 레지스터(p1 = 3, 정수 값 20)보다 크거나 같다면, 9번 주소로 점프
  11. 6번 주소 : 4번 레지스터(p2 = 4)에 현재 커서(p1, tbl1)가 가리키는 행의 key 저장
  12. 7번 주소 : 어떤 결과든 8번 주소로 점프해서 의미를 잘 모르겠다....
  13. 8번 주소 : 현재 커서(p1, tbl1)가 가리키는 값 제거
  14. 9번 주소 : 현재 커서(p1, tbl1)의 다음 행이 있다면, p1이 다음 행을 가리키고 4번 주소로 점프
  15. 10번 주소 : 특별한 의미가 없다.
  16. 11번 주소 : 바이트코드 프로그램 종료

  바이트코드 해석을 그대로 나열했기 때문에 꽤나 복잡한데, 중요한 부분은 4번 주소부터 9번 주소 까지 이다. 실제로 테이블에서 컬럼 내용을 가져와 이 값을 20보다 작은지 확인해 제거하는 과정이 4번 주소부터 9번 주소 사이에서 진행되고 있다.


  현재 해석한 내용의 경우, opcode 설명을 전부 이해하지 못 한 점과 일부 operand 값을 제대로 확인하지 않은 점도 있어 틀린 부분이 있을 수 있긴 하지만, 도중 실제로 연산되는 부분은 파악할 수 있었다.



참고


'Etc' 카테고리의 다른 글

[Exploit] NULL Dereference exploit case 예제 정리  (0) 2018.09.18
CWE case별 차이 정리  (0) 2018.08.21
Bindiff 설치 및 약간의 삽질  (0) 2017.07.23
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31