'gravity-free/assembly'에 해당되는 글 3건

  1. 2012.06.25 ARM asm 1
  2. 2012.03.23 asm pt2
  3. 2011.01.04 assembly

ARM asm

gravity-free/assembly 2012. 6. 25. 17:35

ARM Assembly를 이해하기 위해서는 또 간단한 명령어들을 쫌 알아주셔야 이해하기도 편하고 하겠지요? 뭐 실은 아주 자주 사용하는 것들만 여기에 잘 써주시고 나머지 복잡한 명령어들은 ARM 책들이나 인터넷에 흩뿌려진 자료들을 참조하는 것이 훨씬~ 도움이 되리라고 생각하고 있습니다. 그래서 아주 기본적인 Assembly 명령어와 특징들 그리고 syntax를 살펴 보도록 하는 지루하고 무익한 시간을 갖겠습니다. 
 
- 남들이 보면 이런 왕 망할 초간단 Assembly 설명은 도대체 무엇이냐 하고 비난할 만 합니다만, 저는 이런 거는 C 알고리즘 책에 C syntax를 설명해서 지면만 엄청 차지 하는 것과 같다고 봅니다. 그래서 이 section에서는 정말 기본 적인 것들만 다뤄 주시고 그때 그때 필요할 때 다시 언급하는 수작을 부리도록 하겠습니다.  지못미 -
 
일단 알아두셔야 하는 것 중에 하나는 ARM assembly는 아주 기본 뼈대 명령어 뒤에 조건이나 덧붙임 명령어를 더 붙여서 활용한다는 concept을 이해하셔야 합니다. 이건 중요하니까 꼭 기억해 두세요. 
 
일단은 어떤 종류의 명령어들이 있는지 아시면 조금 더 편할 테니까, 그 종류들을 나열해 볼게요. 잘 봐두세요. 제일 많고 까다로운 부분은 Data Processing과 곱셈 부분인데 이부분은 중요한 몇 개만 봐두고 넘어갈 거니까 아쉬워는 마시고요 ㅋ. 
 
그리고 ARM Assembly는
 
 
 명령어 Rd, 뭐 어쩌구  
 
의 형식을 많이 갖는데 맨 앞의 Rd가 destination이 되는 형식이에요. “뭐 어쩌구를 어떻게 해서 Rd에 넣는다”가 그 기본 idea이고요. 단, 밑에서 보겠지만 STR만이 대상이 거꾸로 되어 있습니다. 근데 그 형식 자체가 그야말로 거꾸로 될 수 밖에 없는 구조지만 말이에요. 
 
Assembly의 형식을 유식한 표현으로는 OP 코드 + Operand 라고 표현하는데요, 명령어가 OP 코드 - 즉, instruction - 라고 불리고요, 뒤에 오는 내용들이 Operand라고 불리는데, Operand에는 Register를 어떻게 다룰 것이냐 뭐 이런 내용들인 게죠. 
 
앞에서도 언급했지만, Assembly를 설명하면서 * (레지스터이름) 등의 pointer 형식의 설명을 쓸 때가 더러 있는데, 이건 레지스터이름의 값은 주소이고 이 주소가 가리키는 곳의 값을 의미합니다. 이게 편할 때도 있어 가끔 사용하니까 주의해 주세요. 레츠고.
 
1. Branch 명령어.
    B, BL, BX, BLX
2. Data Processing
    MOV, MVN, CMP, CMN, TST, TEQ, ADD, SUB ...
3. Load/ Store
    LDR, STR...
4. 의사 명령어
    LDR, ADR
5. SWAP 명령
    SWP
6. PSR 명령
    MRS, MSR
7. SWI 명령
    SWI
8. DCD directives
9. EXPORT, IMPORT
10. 덧붙임 명령어들 ^ ! S
 
자자, 1번 부터 차례로 잘근 잘근 씹어주시죠.
 
1. Branch 명령어.
   Branch 명령어는 pc에 주소를 넣고서 넣어진 주소로 프로그램 실행 번지를 바꾸는 역할을 하며, 보통 함수가 호출 되었을 때 대상 함수로 jump하기 위해서 삽입 됩니다.
 
   B <주소>로 사용되며, 그 변종으로는 L이나 X를 덧붙여서 만들어 냅니다.
 
   B _printf             ; _printf로 jump 하라
   BL  _printf           ; _printf로 jump를 히되 R14에 돌아올 주소를 넣고 가라.
                                돌아와야 되니까 R14 (LR)에 돌아올 주소를 꼭~ 넣어라~
   BX _printf            ; 현재 mode가 ARM이라면 Thumb으로 mode바꾸고 가고
                               Thumb라면 ARM으로 mode 잘 바꿔서 가라.
   BLX _printf           ; 위 두개의 짬뽕이다.
 
   자 여기서도 보듯이 L과 X만으로도 변종을 만들어서 사용하는게 ARM Assembly의  철학입니다. 이제부터 계속 이런 식이니까 잘 보세요.
 
2. Data Processing
    대상 레지스터 Rd에 뭔가를 장난쳐서 집어 넣는 것이 대부분 입니다.
 
   명령어, 대상레지스터, 장난레지스터, 레지스터 또는 값. 형식이고
             대상레지스터:= 장난레지스터와 레지스터 또는 값을 명령어로 장난 침.
 
   MOV Rd, 레지스터 또는 값.       Rd:=레지스터 또는 값
                MOV R1, R2            R1:=R2
   MVN Rd, 레지스터 또는 값.       Rd:=레지스터 또는 값의 음의 값
                MVN R1, #0            R1:=0xFFFFFFFF
   ADD Rd, Rn, 레지스터 또는 값.  Rd:= Rn+레지스터 또는 값.
                ADD R0, R1, R2       R0:= R1 + R2
   SUB Rd, Rn, 레지스터 또는 값.  Rd:= Rn - 레지스터 또는 값.
                SUB R0, R1, #5      R0:= R1 - 5
   RSB Rd, 레지스터 또는 값, Rn   Rd = 레지스터 또는 값 - Rn (역으로 빼기)
                RSB R0, R2, R3       R0 = R3 - R2
   AND Rd, Rn, 레지스터 또는 값   Rd:=Rn&레지스터 또는 값 (bit & 연산)
                AND R0, R2, R3       R0:= R2 & R3
   BIC   Rd, Rn, 레지스터 또는 값  Rd:=Rn&~(레지스터 또는 값)
                BIC R0, R2, R3        R0:= R2 & ~R3 (R3의 1이 들어 있는 filed만 0으로 set)
   CMP  Rn, 레지스터 또는 값       Rn - 레지스터 또는 값을 conditon field에 update↔
                CMP R0, R2            R0>R2 면 다음 조건에서 큰 경우, 아니면 작은 경우라고 해석
   
3. Load/ Store
    대상 레지스터에 Memory 주소가 가르키는 곳의 값을 가져오는 일을 합니다.  여기에서 Assembly중 특이한 사항은 pointer와 비스므리한 syntax가 있다는 것인데 바로 [Rn] = *Rn 이라는 뜻입니다.  결국 Rn이 가르키는 곳의 값이라는 뜻입니다.  또 재미있는 것이 하나 있는데 "!" 연산자 입니다. 뒤에 !를 붙이면 !가 붙은 곳의 값을 update하라는 뜻입니다. 좀 말이 헷갈리는데 아래를 보면 좀 나아 질 꺼에요. !는 Write back 연산자라고 부른답니다.
    
    LDR r1, [r2]
    는 r1에 r2에 들어 있는 값을 주소로 보고 그 주소에 있는 값을 가져와라는 뜻이에요.  예를 들어 r2에 0x100이 들어 있었고, 0x100번지에 3이 들어 있었다면  r1값은 3이 되는거죠.  그리고 LDR r1, [r2]! ,0x100 이라고 쓰면 r1값은 3이 되고요 r2는 r2= r2+0x100이 되는 거구요.  간단한데 여기에 좀더 복잡한 indexing 방식이 있어 소개해야 하겠습니다.  명령어 대상레지스터, 주소. 형식으로서  LDR인 경우 대상레지스터에 Load를 하고  Store인 경우에는 대상레지스터를 저장하는 형식입니다.
 
    LDR Rd, [Rn, offset]              Rd:= *(Rn + offset)
            LDR Rd, [Rn + 0x100]    Rd:= * (Rn + 0x100), Rn은 그대로.
    LDR Rd, [Rn, offset]!             Rn:=Rn+Offset, Rd=*(Rn)
            LDR Rd, [Rn + 0x100]!   Rn:= Rn+0x100으로update, Rd = *(Rn)
                                            으흐흐 웃기죠 이런걸 pre-index방식이라고 부르기도 합니다.
                                            뭐 말만 멋있게 만들었지 별거 아닙니다요.
    LDR Rd, [Rn], offset              Rd:= *(Rn), Rn:= Rn+0x100으로update
                                            이건 또 특이하죠. Rn이 operation후 update됩니다.
                                            요런걸 post index방식이라고 유식한척들 합니다.
                                            Post index방식은 !를 안붙여도 Rn이 update됩니다.
     STR의 경우에도 위의 LDR자리에서 STR로만 바꾸면 똑같고요.  딱 하나만 예를 든다면
      STR Rd, [Rn, offset] 이라면        *(Rn + offset) = Rd 입니다 똑같죠? 괜히 했나...?
 
     LDRB (byte), LDRH (Halfword), LDRSB (signed byte), LDRSH (signed halfword)
     STRB (byte), STRH (Halfword), STRSB (signed byte), STRSH (signed halfword)
 
     과 같이 뒤쪽에 크기를 붙여서 여러가지 size를 Load 또는 Store할 수 있습니다.
 
     뭐 잘 외우려면  괄호가 붙어 있는거 먼저 하면 되어요. 근데 별로 외우기 싶지 않네요.  안타깝다.  LDR Rd, 0x1000 같은 숫자가 직접 오는 경우가 있는데요. 요건 Rd에 0x1000번지에 들어 있는  값을 Load해라라는 의미에요. 요것도 좀 헷갈리죠.  주의 사항으로는 R15가 Rn (base register)가 될 때는 ! (Write back) 연산자를 사용할 수  없습니다. 이걸 사용하면 R15가 마음대로 update되어버려 알수 없는 일을 저지를 지 모르지요.  이외 Multiple Store/ Load명령어는 Stack관련한 Chapter에서 자세히 다루고 있어요.
 
4. 의사 명령어
    의사 명령어라 해서 굳이 특별한 건 아니고, 가짜 명령어라고 해야죠. 편의를 위해서 Assembly에는 있지만, 실제로 처리는 다른 방법으로 처리하는 명령어를 의미하는데, 특별한 주소에 값을 loading할 때 이런 명령어가 사용됩니다. 그 명령어에는 LDR, ADR있고요. 그 사용 예를 보시는 게 제일 이해하기 빠른 길이 아닌가 싶습니다.
 
  
    LDR Rd, Label
 
    이런 식으로 Assembly를 작성해 주시면, Compiler가 최종 Mnemonic을 만들 때 Label을 [PC, #offset] 형태로 다시 만들어 주고요. 결국에는 LDR Rd, =[PC + offset]이 되며, 결론적으로 Rd = * (PC + offset) 형태로 만들어서 data를 loading하게 해줍니다. Assembly로는 간단하게 한 줄로 쓰지만, 실제 compile되면 위와 같이 몇 가지 Assembly가 합쳐져서 같은 일을 하게 되지요. 이런 의사 명령어를 사용할 경우 메모리 접근 시간과 Compiler에서 같은 효과 처리를 위한 명령어를 더 추가하기 때문에, Code 크기도 늘어나고, 시간도 많이 들기 때문에 꼭 필요할 때만 쓰는 운용의 묘를 가져야 해요.
  
    어떻게 하느냐 하면,
 
    LDR r0, TARGET 이라고 쓰면
 
    내부적으로는
                                    LDR r0 ,[PC,#offset]
                               .
                               .
                           offset 만큼 떨어져 있다.
                    TARGET :   
                                  DCD 0xFFFFFFFF
 
    이라고 해석 되고요. 결국엔, LDR r0, =0xFFFFFFFF 을 실행하게 되는 셈이지요.
 
여기에 값을  Register에 Loading하는 방법을 하나더 쌍콤하게 더하자면  ADR  Rd, Label도 먹어요. ADR의 경우에는 Label을 직접 써주면, Label의 Adderss를  Rd에 알아서 저장해준다는 거지요.
 
    32 bit 상수값을 Register에 직접 Loading하는 방법에 LDR이 쓰이기도 합니다.  LDR Rd, = 0xFFFFFFFF  뭐 이런 식으로 말입니다.
 
    이런 가짜 명령어가 왜 필요하느냐! 하면, 첫 번째로, Branch 명령어의 경우 32 bit중 Branch 명령어 자체가 차지하는 9bit를 제외한 23bit를 branch하는 주소로 사용하는데, 이 23bit를 ARM 명령어로 생각해서 4 byte당 한 주소씩으로 따져 2비트 Left shift을 사용해 봤자 분기 영역은 최대로 상수 표현 범위(pc ± 32MB) 라서 필요에 따라 그 이상의 분기가 필요할 때 32 bit를 fully 사용 가능한 의사 명령어를 이용한답니다. Thumb에서의 Branch의 경우에는 더욱 최악이라, pc ± 4MB 밖에 되지 않기 때문에 이런 의사 명령어가 꼭 필요할 때도 있어요. 왜 4MB이냐 하면, Thumb에서 BL 명령어 자체가 16 bit중에 5bit를 잡아 먹고요. 11bit 가지고 jump할 수 있는데요. 이것 갖고는 택도 없으니까, BL 명령어 자체가 두 단계 명령어로 구성되어 있어요. 원래 BL 명령어의 4K 하고 바로 다음에 OR 명령이 오는데, 이 명령어를 제외하고, 10bit를 offset으로 또 사용 가능해서 BL자체의 offset과 바로 다음에 오는 OR명령을 이용해서 전체 21bi를 사용가능하고요. 2byte씩 묶어서 jump할 수 있으니까, 4MB씩 쓸 수 있는 거죠. 꼭 주소가 아니더라도, 32bit중에는 Assembly 자체가 차지하는 공간이 꼭 있어서, 32 bit 상수를 그대로 register에 loading할 수 있는 방법이 없으니까, 이런 꽁수를 써서 하게 되는 거지요. 하지만, 이런 의사 명령어를 남발한다 하여도, Register에 한번에 loading할 수 있는 크기라면, 곧바로 MOV나, MVN으로 Compile해 준답니다. 예를 들어, MOV의 경우에는 ldr r0, =0xff  요렇게 Assembly를 만들어 넣었더라도, mov ro,#0xff 요렇게 만들어 주죠. 똑똑한 녀석.
 
 
5. SWAP 명령
    SWP 명령어는 일반 memory 영역의 값과 register값을 바꿔치는데 사용합니다. 뭐 별거 있겠습니까 그냥 바꾸고 싶을 때 바꿔치는 거죠.  이 명령을 이용할 때는 두가지 일을 함에도 불구하고, Interrupt가 걸리지 않는 다는  특성이 있어서 Semaphore등을 구현할 때 사용한다고는 합디다.
 
    SWP R0, R1, [R2]   :   R0 = *(R2), *(R2) = R1 이 되는거죠.
 
    원래
    *(R2) = 0x10, R1= 0x20 이었다면,
    *(R2) = 0x20, R0 = 0x10, 이 되는거죠.
 
     SWP R0, R1, [R2]  
                 0x20 0x10
            0x10       0x20
 
    뭐, 엄밀히 말하면 R0이 tmp처럼 들어갔지만, 값을 빼앗아 오긴 하네요. 음.  글쎄요.. 한번에 하는 명령이니까 좋긴한데 헷갈리니까 저는 잘 쓰지 않습니다. ㅋㅋ 하지만 R15 (PC)에 사용하면 안되니까, 그건 주의하세요.■
 
6. PSR 명령
    PSR명령어란 다름이 아니라, CPSR과 SPSR 두개와 일반 Register 사이를에 값을 서로 복사할 수 있는  명령어들을 말해요, MRS, MSR 두개로 모든걸 처리합니다.
 
    예를 들어,
    MRS r5, CPSR    : r5:=CPSR
    MSR CPSR, r5    : CPSR:=r5
    라는 뜻이에요.
 
    좀 헷갈리죠.  S는 PSR을 의미하고, R은 일반 Register를 의미해요.  그리고 방향은 M X ← Y 이런 형태이고요.  그러니까 MRS는 Register ←PSR, MSR은 PSR ← Register라고 아시면 되겠습니다.  그런데, PSR은 여러가지 구성으로 되어 있지요. 특히나 flag와 condition 부분이 있는데,  이런 녀석들을 따로 쓰기 위해서 특별한 mask가 준비되어 있습니다.  그 녀석들은 CPSR_c (control), CPSR_f (flag), CPSR_cf (control, flag)로 준비되어 있는데,  MSR을 이용해서 PSR을 update할 때 사용됩니다.  예를 들어,  MSR CPSR_c, r5라고 쓰면 CPSR의 control field에 r5값을 넣습니다.  똑같이 MSR CPSR_f, r5이면 flag field에 r5값을 넣는 거구요, Exception발생한 후 뒷처리를 할 때나, 아니면 굳이 mode를 바꿔야 할 때 이 명령어를 사용해서  장난치게 됩니다.  자세한 내용은 Inline Assembly 편에서 다시 공부해 보아요.
 
7. SWI 명령
    Software Interrupt 명령은 Software가 Exception을 일부러 발생시킬 수 있는 유일한  명령입니다. 보통 user mode에서 다른 mode로 전환할 때 사용하는데,  가장 큰 용례로서는 kernel은 SVC mode, 일반 application은 user mode일 때 일반 application이 kenel에게 뭔가 부탁할 때 이런 명령어가 쓰입니다.      
     SWI 0x11      : Software Interrupt Handler에서 0x11번째 case를 호출.
 
8. DCD directives
   : 이 Directive를 만나면 Data를 위한 메모리를 할당해 준답니다.   여러개를 늘어 놓으면 여러개의 Array처럼 쓸 수도 있어요. 
    DCB 1Byte 짜리 Data 
    DCW 2Byte 짜리 Data
    DCD 4Byte 짜리 Data
    DCQ 8Byte 자리 Data
    Assembly 최초 예제 코드에 그예가 나와 있지요? 여기에 Label까지 지정하게 되면 주소를 Symbol로 관리해 줘요.  재미있는 사용 예로는, DCB와 '='는 같은거구요, DCD는 '&'와 같습니다요. 뭐 다시 말하면, DCD는 특정한 Memory 영역에 내가 원하는 값을 넣어 놓도록 memory를 확보해 놓아라 라는 뜻입니다.  의사 명령어에서도 나왔었는데,
    0x1000             ldr r0, =0xFFFFFFFF →  ldr r0 ,[PC,#offset]
                                    .
                                    .
   00x1000+offset  DCD 0xFFFFFFFF
   이런식으로 만들어 주면 00x1000+offset 주소에는 0xFFFFFFFF이 들어 있도록 compiler (linker)가  조작해 준답니다.  다른 식으로 사용하는 예는 
   MY_DATA
   DCD 0xFFFF0000
   이런식으로 Label을 주고서, 그곳에 0xFFFF0000을 해 놓으면, MY_DATA를 Access하면 그곳에는  0xFFFF0000가 들어 있는 꼴이지요.  또는 Symbol 자체도 넣을 수가 있어요.
   INT_RESET_PHY
   DCD INT_RESET_LOGICS
   라고 써주면, INT_RESET_LOGICS의 주소를 INT_RESET_PHY로 재정의 해서 사용하겠다.  라는 의미죠. - INT_RESET_LOGICS가 함수라면 함수의 시작주소 -  비스므리 한거로는 SPACE 라는 명렁어로서, SPACE 8 하면 8byte만큼 0으로 채워진  메모리 영역을 예약해 줍니다요. 어디다 쓰냐고요? static으로 선언된 로컬 변수처럼 써도 되겠네요? 왜냐하면 자신만의 주소를 갖는 변수가 하나 생겼는데, 남들은 Access못하고,  자기만 Access 가능하니까, Static 처럼 쓸 수 있는거에요.
 
9. EXPORT, IMPORT directive
     이건 아주 재미있는 표현인데요, Assembly에서 사용된 Symbol을 외부에서 사용가능하도록 하는  Directive인데요, Export는 외부에서 가져다 쓸 수 있도록 해주고요, Import는 어디선가 Export된 걸  가져다 쓴다는 의미 입니다.  Export 는 Global로 사용하겠다는 뜻, Import는 extern 이라는 뜻입니다. C하고 비슷하긴 하지만,  전역으로 쓰겠다는 것도 알려줘야 하는게 좀 다르지요.  EXPORT는 GLOBAL하고 같은 말이기도 해요. 그리고, 이렇게 선언된 Symbol들은 뒤에 WEAK를  붙여서 Weak Symbol로 만들어 줄 수도 있답니다. Weak Symbol은 잘 아시다 시피,  똑같은 이름의 Symbol이 더 있을 수 있다는 의미이고요 (똑같은 이름이 또 있어도, Duplicate Error안나요) Linker도 Weak Symbol이 아닌 Strong Symbol을 link해 준답니다.
 
10. FIELD와 MAP (같은 표현으로는 ^와 # 있어요)  directive
     FILED MAP # 모두 같은 말인데요, 여하간 C에서 Struct 구조체 선언할 때랑 비슷한 용법으로 사용해요.  MAP은 시작 주소를 알려주고요, FIELD를 이용해서 메모리를 확보해요.
 
          MAP     0x1000, r5      ; 이곳의 주소는 r5+0x1000
member FIELD    4                ; member라는 이름의 32bit 메모리 확보, 0x1004 + r5
count   FIELD    4                 ; coutn 라는 이름의 32bit 메모리 확보, 0x1008 + r5
 
사용법은
      ADR r5, Data       : r5에 Data의 주소를 Load
      LDR r0, member    ; r0에는 Data시작주소 + 0x1004
요렇게 쓰지요. 한가지 재미있는건 MAP  0x1000, r5 에서 r5를 base register라고 하는데이걸 안 줄 수도 있고요, 그러면 절대 주소로 0x1000에 할당되겠죠!
 
12. 덧붙임 명령어들 ! ^ S
! : 요건 index 에서 봤죠? base register를 update해라! 라는 뜻이에요.
S : 계산 후 CPSR에 SPSR을 넣어 복구하는 명령어.
^ : PC가 포함된 경우는 연산 후 CPSR에 SPSR을 넣어 복구
      or SYSTEM/USER 모드가 아닌 경우이고 PC가 없는 경우는 SYSTEM/USER 모드 레지스터에
     값을 LDR/STR
 
뭐 위의 모든 명령어가 다 들어가 있는건 아니지만, 뭐 대충 많이 쓰는 것들을 살펴 보았구요. 더 자세한 것들은 ARM Assembly 책을 찾아 보심이 어떠할까 생각합니다. 대체로 짧은 Assembly는 해석할 수 있을 수준은 된거 같은데 다음의 Assembly를 한번 연습해 봅시다.  어떤 의미인지 해석해 보세요. 좋은 연습이 되었으면 하는 작은 바램이 있네요.
 
; generated by Thumb C Compiler, ADS1.2 [Build 805]
; commandline [-O2 -S -IC:\apps\ADS12\INCLUDE]
        CODE16
        AREA ||.text||, CODE, READONLY
calc PROC
        PUSH     {r3-r7,lr}
        LDR      r6,|L1.32|
        MOV      r5,r0
        MOV      r4,#0
        MOV      r7,#2
|L1.10|
        LSL       r5,r5,r7
        ADD      r4,r4,r5
        MOV      r0,r4
        LDR      r1,[r6,#4]  ; data
        BL       manual
        CMP      r0,#0
        BNE      |L1.10|
        MOV      r0,r4
        POP      {r3-r7,pc}
        DCW      0000
|L1.32| DATA
        DCD      data
        ENDP
 
        AREA ||.data||, DATA, ALIGN=2
||.data$0||
data
        DCB       0x0000000a
        DCB      0x00000014
        DCB      0x0000001e
        DCB      0x0000002
 
 
 
제가 한번 해석해 보겠습니다. 뭐 제가 해석한다고 크게 달라지지는 않겠지만서도요. 한번 해보는 것과 안해보는 건 하늘과 땅차이니까 한번만 해보세요.
 
; generated by Thumb C Compiler, ADS1.2 [Build 805]
; commandline [-O2 -S -IC:\apps\ADS12\INCLUDE]
        CODE16                                                 1. 일단 Thumb mode compile되었군요.
        AREA ||.text||, CODE, READONLY           2. Section 이름은 ||.text||이고 code네요.
calc PROC                                                      3. 함수 이름이 calc라는 걸로 구현되어 있나 봐여
        PUSH     {r0-r7,lr}                                 4. 어디선가 불리면서 r0~r6을 stack에 넣고, lr도 넣네요.
        LDR      r6,|L1.32|                                  5. r6에 |L1.32|의 주소를 넣고서..
        MOV      r5,r0                                         6. 뭔지 모르겠지만 r0는 argument로 받았고
                                                                          이녀석을 r5에 복사하네요?
        MOV      r4,#0                                        7. r4에는 0을 넣고요.
        MOV      r7,#2                                        8. r6에는 2를 넣습니다.
|L1.10|
        LSL      r5,r5, r7                                          9. r5를 r7만큼 shift해서 r5에 넣고
        ADD      r4,r4,r5                                     10. r4에 다시 r4와 r5를 더한 것을 넣어요. 
        MOV      r0,r4                                         11. r4을 r0에 넣고서 
        LDR      r1,[r6,#4]  ; data                       12. r6가 가르키는 곳에서 4만큼 더한 곳의 값을 r1에..
        BL       manual                                          13. manual이라는 함수로 r0와 r1의 값을 가지고 jump
                                                                            LR에는 CMP r0, #0를 넣고서..
        CMP      r0,#0                                         14. manul의 return값이 0인지 확인해서
        BNE      |L1.10|                                       15. 0이 아니면 |L1.10|으로 jump
        MOV      r0,r4                                          16. 이제 돌아가기 위해서 r4를 r0에 넣고
        POP      {r3-r7,pc}                                  17. 이 함수를 부른 곳으로 돌아간다 .
        DCW      0000
|L1.32| DATA
        DCD      data
        ENDP
 
        AREA ||.data||, DATA, ALIGN=2
||.data$0||
data
        DCB      0x0000000a
        DCB      0x00000014
        DCB      0x0000001e
        DCB      0x0000002
 
대충 감이 오세요?뭐 별건 아니지만 그림으로 그리면 다음과 같은 Story입니다. 

 

일단은 뭔가를 loop를 돌고 있는데, manual 이라는 함수의 결과 값이 0이면 계속 loop를 도는 형태로서, 그 loop안에서는 r5가 계속 2의 좌승 형태로 늘어나면서 r4에 저장되는 형태를 취하고 있고요. manual의 argument는 계속 저장되는 r4와 특정 memory 영역에서 가져온 값인 r1, 2개를
갖습니다. 또한 이 함수는 return값으로 계속 저장된 값 r4를 최종적으로 돌려주게 되어 있는 형태 입니다. 대충 감 오시는 지요?
 
이 녀석을 대충의 c code로 다시 reverse engineering해 보면 다음과 같이 상상해 볼 수 있을 것입니다.
 
byte data[] = { 10, 20, 30, 40};
 
int calc (int a)
{
    int sum=0;                          //아마도 r4
 
    while (1)
    {
       a = a^4;                             // 여기에서 재미있는 사실은 ^4할 때마다 <<2를 해주면 같은 효과
                                                   // 아마도 r5
       sum = sum + a ;                // r4 = r4 + r5
       
       if (manual (sum,data[1]))  // r4와 r6이 가르키는 이상한 data를 manual로 넘겨줌.
        break;                              // 조건이 return이 0이 아니면 끝.
    }
    return sum;                         // r4를 return함.
}
 
어때요? 생각한 것과 비슷한 느낌입니까~? 이렇게 몇번만 Assembly를 쳐다보면서 상상을 하다보면 Assembly 느는건 한순간이에요 음화화.
 
예전에 얘기했던 Software의 정체를 다시 한번 comment하자면, mnemonic과 Assembly는 1:1 matching이고요, LDR을 예를 들면요, LDR에 대해서 mnemonic을 보면 아래와 같아요.
 
LDR R0,[R1,#0x21]     21 00 91 E5
LDR R0,[R5]                00 00 95 E5
LDR R2, [R3]               00 20 93 E5
LDR R1, [R6]               00 10 96 E5
 
으흐흐, 그러니까, Memory에서 32 bit짜리 21 00 91 E5 (00100001 00000000 10010001 11100101) 신호가 전기적으로 CPU에 input이 되면 LDR R0, [R1, #0x21]을 실행하게 되는거죠. 으흐흐. 가만히 보면 LDR은 E5로 끝나죠? 그리고 9x이죠 ㅋㅋ. 9도 역시나 LDR 명령어의 조각이고요, 거기보면 x가 Source Register이고요, ㅎ. 두 번째 byte가 Destination Register이고요, 첫 번째 byte가 offest이네요.

ADC - Add with carry -> Rd := Rn + Op2 + Carry

ADD - Add -> Rd := Rn + Op2

AND - AND -> Rd := Rn AND Op2

B - Branch -> R15 := address

BIC - Bit clear -> Rd := Rn AND NOT Op2

BL - Branch with link -> R14 := R15, R15 := address

BX - Branch and exchange -> R15 := Rn, T bit := Rn[0]

CDP - Coprocess data processing -> (Coprocessor-specific)

CMN - Compare Negative -> CPSR flag := Rn + Op2

CPM - Compare -> CPSR flag := Rn - Op2

EOR - Exclusive OR -> Rd := (Rn AND NOT Op2) OR (Op2 AND NOT Rn)

LDC - Load coprocessor from memory -> Coprocessor load

LDM - Load multiple registers -> Stack mainpulation (Pop)

LDR - Load register from memory -> Rd := (address)

MCR - Move CPU register to coprocessor register -> cRn := rRn{<op>cRm}

MLA - Multiply accumulate -> Rd := (Rm x Rs) + Rn

MOV - Move register or constant -> Rd := Op2

MRC - Move from coprocesser or register to CPU register -> Rd := cRn{<op>cRm}

MRS - Move PSR status/flags to register -> Rn := PSR

MSR - Move register to PSR status/flags -> PSR := Rm

MUL - Multiply -> Rd := Rm x Rs

MVN - Move negative register -> Rd := 0 x FFFFFFFF EOR Op2

ORR - OR -> Rm := Rn OR Op2

RSB - Reverse subtract -> Rd := Op2 - Rn

RSC - Recerse subtract -> Rd := Op2 - Rn -1 + Carry

SBC - Subtract with Carry -> Rd := Rn - Op2 -1 + Carry

STC - Store coprocesser register to memory -> address := CRn

STM - Store Multiple -> Stack manipulation (Push)

STR - store register to memory -> <address> := Rd

SUB - Subtract -> Rd := Rn -Op2

SWI - software interrupt -> OS call

SWP - Swap register with memory -> Rd := [Rn] , [Rn] := Rm

TEQ - Test bitwise equality -> CPSR flag := Rn EOR Op2

TST - Test bits -> CPSR flags := Rn AND Op2

[출처] ARM 어셈 명령어셋|작성자 종자돈


출처 : 


'gravity-free > assembly' 카테고리의 다른 글

asm pt2  (0) 2012.03.23
assembly  (0) 2011.01.04
:

asm pt2

gravity-free/assembly 2012. 3. 23. 14:12
데이터 타입 :

 타입 설 명
 BYTE 8bit 부호 없는 정수
 SBYTE 8bit 부호 있는 정수
 WORD 16bit 부호 없는 정수
 SWORD 16bit 부호 있는 정수
 DWORD 33bit 부호 없는 정수
 SDWORD 32bit 부호 있는 정수
 FWORD 48bit 정수
 QWORD 64bit 정수
 TBYTE 80비트 정수


피연산자(operand) 타입 :

 피연산자 설 명 
 r8 8bit 범용 레지스터
 r16 16bit 범용 레지스터
 r32 32bit 범용 레지스터
 Reg 임의의 범용 레지스터
 Sreg 16bit 세그먼트 레지스터
 imm 8, 16, 32bit 즉시값
 imm8 8bit 즉시값
 imm16 16bit 즉시값
 imm32 32bit 즉시값
 r/m8 8bt 범용 레지스터, 메모리
 r/m16 16bit 범용 레지스터, 메모리
 r/m32 32bit 범용 레지스터, 메모리
 mem 8, 16, 32bit 메모리


어셈블리 명령어 :

INC(Increase)
피연산자에 1을 더한다.
연산 결과에 따라 ZF(Zero Flag)나 OF(Overflow Flag)가 세트될 수 있다.
ex. INC reg, INC mem

DEC(Decrease)
피연산자에 1을 뺀다.
연산 결과에 따라 ZF나 OF가 세트될 수 있다.
ex. DEC reg, DEC mem

ADD(Add)
Destination에 Source의 값을 더해서 Destination에 저장한다.
연산 결과에 따라 ZF, OF, CF(Carry Flag)가 세트될 수 있다.
ex. ADD eax(Destination), 100(Source) : eax레지스터에 100을 더해서 eax레지스터에 저장

SUB(Subtract)
Destination에 Source의 값을 빼서 Destination에 저장한다.
연산 결과에 따라 ZF, OF, CF가 세트될 수 있다.
ex. Sub eax(Destination), 100(Source) : eax레지스터에 100을 빼서 eax레지스터에 저장

MUL(Unsigned Integer Multiply)
부호 없는 al, ax, eax의 값을 피연산자와 곱한다. 피연산자가 8비트이면 al과 곱해서 ax에 저장되고 16비트면 ax와 곱하고 dx(상위16비트):ax(하위16비트)에 저장된다. 연산 결과에 따라 OF, ZF 플래그가 세트될 수 있다.
ex. MUL reg

IMUL(Integer Multiplication)
부호 있는 al, ax, eax의 값을 피연산자와 곱한다. 연산결과에 따라 CF, OF가 세트될 수 있다.
ex. IMUL r/m8 : 단일 피연산자이고 피연산자를 al, ax, eax에 곱한다.
     IMUL r16(destination), r/m16(value) : value를 al, ax, eax와 곱해서 destination에 저장
    IMUL r16(destination), r/m8(value), imm8(value) : value끼리 곱해서 destination에 저장
    (연산 결과가 destination 레지스터의 크기보다 크다면 OF, CF가 세트된다.)

DIV(Unsigned Integer Divide)
8, 16, 32비트 부호 없는 정수의 나눗셈을 수행한다. 연산결과에 따라 CF, OF, ZF가 세트될 수 있다.
ex. DIV reg

MOV(Move)
Source에서 Destination으로 데이터를 복사한다.
ex. MOV reg(Destination), mem(Source)

MOVS(Move String)
Source에서 Destination으로 데이터를 복사한다.
ex. MOVS Destination, Source

MOVSB, MOVSW, MOVSE(Move String)
SI 또는 ESI 레지스터에 의해 지정된 메모리 주소의 내용을 DI 또는 EDI 레지스터에 의해 지정되는 메모리 주소로 복사한다.
MOVSB는 BYTE 단위, MOVSW는 WORD 단위, MOVSD는 DWORD 단위로 복사한다. 방향 플래그(DF)가 1로 세트되어 있으면 ESI와 EDI는 복사 시에 감소하게 되고 DF가 0으로 세트되어 있으면 ESI와 EDI는 복사 시에 증가하게 된다.
ex. MOVSB, MOVSW, MOVSD

MOVSX(Move with Sign-Extend)
BYTE나 WORD크기의 피연산자를 WORD나 DWORD크기로 확장하고 부호는 그대로 유지.
ex. MOVSX reg32, reg16

MOVZX(Move with Zero-Extend)
BYTE나 WORD크기의 피연산자를 WORD나 DWORD크기로 확장하고 남은 비트는 0으로 채운다.
ex. MOVZX reg32, reg16

INT(Interrupt)
소프트웨어 인터럽트를 발생시켜 운영체제의 서브루틴을 호출한다.
ex. INT imm

AND(Logical AND)
Destination과 Source 피연산자의 각 비트가 AND 연산된다.
AND 연산은 각 비트가 모두 1일 때만 결과 값이 1이 된다.
ex. Destination : 10011100
      Source      : 11001010 
      결과          : 10001000
     AND reg(Destination), mem(Source) : reg와 mem을 AND 연산한 후 결과를 reg에 저장
AND 연산을 통해서 OF, CF가 0으로 세트되고 결과에 따라서 ZF가 1로 세트될 수 있다.

OR(Inclusive OR)
Destination과 Source 피연산자의 각 비트가 OR 연산된다.
OR 연산은 각 비트가 모두 0이면 결과는 0이고 모두 0이 아니면 결과는 1이 된다.
ex. Destination : 10011100
      Source      : 11001010
      결과          : 11011110
      OR reg(Destination), mem(Source) : reg와 mem을 OR 연산한 후 결과를 reg에 저장
 OR 연산을 통해서 OF, CF가 0으로 세트되고 결과에 따라서 ZF가 1로 세트될 수 있다.

XOR(Exclusive OR)
Destination과 Source 피연산자의 각 비트가 XOR 연산된다.
XOR 연산은 각 비트가 서로 다른 값일 때만 결과가 1이다. 같은 값이라면 결과는 0이 된다.
ex. Destination : 10011100
      Source      : 11001010
      결과          : 01010110
XOR 연산을 통해서 OF, CF가 0으로 세트되고 결과에 따라서 ZF가 1로 세트될 수 있다.
피연산자의 두 값이 같은 값이라면 결과는 항상 0이 된다.
레지스터를 0으로 초기화시킬때 MOV 명령어를 사용하기보다는 XOR reg, reg으로 많이 사용한다.

TEST(Test)
두 피연산자 사이에 논리적인 AND 연산을 수행하여 플래그 레지스터에 영향을 주지만 결과값은 저장하지 않는다. OF, CF는 항상 0으로 세트되고 TEST 연산 결과값이 0이면 ZF가 1로
세트, 0이 아니면 ZF가 0으로 세트된다.
ex. TEST reg, reg

STC(Set Carry Flag)
캐리 플래그(CF)를 1로 세트한다.
ex. STC

CLC(Clear Carry Flag)
캐리 플래그(CF)를 0으로 세트한다.
ex. CLC

STD(Set Direction Flag)
방향 플래그(DF)를 1로 세트한다.
ex. STD

CLD(Clear Direction Flag)
방향 플래그(DF)를 0으로 세트한다.
ex. CLD

STI(Set Interrupt Flag)
인터럽트 플래그(IF)를 1로 세트한다.
ex. STI

CLI(Clear Interrupt Flag)
인터럽트 플래그(IF)를 0으로 세트한다.
ex. CLI

SHR(Shift Right)
Destination 피연산자를 Source 피연산자의 크기만큼 오른쪽으로 각 비트를 시프트시킨다.
최상위 비트는 0으로 채워지고 최하위 비트는 캐리 플래그(CF)로 복사된다.
ex. SHR reg, imm16

SHL(Shift Left)
Destination 피연산자를 Source 피연산자의 크기만큼 왼쪽으로 각 비트를 시프트시킨다.
최상위 비트는 캐리 플래그(CF)로 복사되고 최하위 비트는 0으로 채워진다.
ex. SHL reg, imm16

PUSH(Push on Stack)
스택에 값을 넣는다.
ESP의 값이 4만큼 줄어들고 이 위치에 새로운 값이 채워진다.
ex. PUSH reg8

PUSHAD(Push All)
EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터의 값을 스택에 PUSH한다.
레지스터들의 값을 보관해야 할 때 사용한다.
ex. PUSHAD

PUSHFD(Push Flags)
플래그 레지스터를 스택에 PUSH한다.
플래그 레지스터의 값을 보관해야 할 때 사용한다.
ex. PUSHFD

POP(Pop from Stack)
ESP 레지스터가 가리키고 있는 위치의 스택 공간에서 4byte 만큼을 Destination 피연산자에 복사하고 ESP 레지스터의 값에 4를 더한다.
ex. POP reg16(Destination)

POPAD(Pop All Flags from Stack)
스택에 존재하는 값을 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터로 POP한다.
PUSHAD 명령어로 스택에 보관해 놓은 레지스터 정보를 다시 이용할 때 사용한다.
ex. POPAD

POPFD(Pop Flags from Stack)
스택에 존재하는 값을 플래그 레지스터로 POP한다.
PUSHFD 명령어로 스택에 보관해 놓은 레지스터 정보를 다시 이용할 때 사용한다.
ex. POPFD

XCHG(Exchange)
두 피연산자의 내용이 서로 교환된다.
XCHG 명령은 imm 값이 피연산자로 올 수 없다.
ex. XCHG reg, mem

NEG(Negate)
피연산자의 2의 보수를 계산하여 결과를 피연산자에 저장한다.
ex. NEG reg

PTR
피연산자의 크기를 재설정한다.
ex. WORD PTR value : value의 크기를 WORD의 크기로 재설정한다.

OFFSET
세그먼트의 시작으로부터 변수가 위치한 거리까지의 상대적 거리를 리턴한다.
ex. OFFSET value : value가 존재하는 위치를 세그먼트 시작 지점으로부터의 상대적 거리를 구한다.

LEA(Load Effective Address)
Source 피연산자의 유효 주소를 계산하여 Destination 피연산자에 복사한다.
간단히 주소를 알아내서 복사하는 명령어다.
ex. LEA reg(Destination), mem(Source)

REP(Repeat String)
ECX 레지스터를 카운터로 사용해서 문자열 관련 명령을 ECX>0인 동안 반복한다.
한번 진행될 때마다 ECX 레지스터값이 -1 된다.
ex. REP MOVS destination, source

JMP(Jump Unconditionally to Lable)
피연산자의 위치로 실행 흐름이 변경된다. 피연산자가 가리키는 코드로 점프 뛰어서 실행한다고 생각하면 된다. 피연산자에는 레이블이나 레지스터, 메모리 값이 올 수 있다.
short점프는 -127 ~ 127 byte범위 안에서, near점프는 같은 세그먼트 내부에서, far점프는 현재 세그먼트를 벗어날 때 사용된다. JMP 명령어는 되돌아올 리턴 어드레스 값을 저장하지 않는다.
ex. JMP shortlabel, JMP nearlabel, JMP farlabel

CALL(Call a Procedure)
함수 호출시 사용된다. JMP명렁어 같이 프로그램의 실행 흐름이 변경되지만 JMP명령와 다른 점은 되돌아올 리턴 어드레스(CALL 다음 명령)를 스택에 저장한다는 것이다. 되돌아올 주소를 저장하기 떄문에 함수 호출 후 원래 위치로 실행 흐름을 되돌릴 수 있다. 호출한 함수가 일을 다 마치면 원래 위치에서 다시 프로그램이 실행될 수 있음을 의미한다.
ex. CALL nearlabel, CALL farlabel, CALL mem16, CALL 함수주소,
CALL DWORD PTR[EAX+8], CALL <JMP to API> : 특정 api 지목

CMP(Compare)
두 피연산자를 비교하는 작업을 한다. Destination 피연산자에서 Source 피연산자를 묵시적으로 빼서 값을 비교한다. 두 피연산자의 값이 같다면 결과는 0이 되고 제로 플래그(ZF)가 1로 세트된다. 다르다면 제로 플래그(ZF)는 0으로 세트된다.
ex. CMP reg, reg

NOP(No Operation)
아무 일도 하지 않는 명령어이다.
ex. NOP

조건 점프 명령
JMP 명령어와는 다르게 특정 조건이 만족하게 된다면 점프를 수행하게 되는 명령어이다.

 명령어 명령어의 의미 명령어가 수행되기 위한
 플래그 레지스터와
 범용 레지스터의 상태 
 JA Jump if (unsigned) above CF=0 and ZF=0
 JAE Jump if (unsigned) above
 or equal CF=0
 JB Jump if (unsigned) below CF=1
 JBE Jump if (unsigned) below
 or equal CF=1 or ZF=1
 JC Jump if carry flag set CF=1
 JCXZ Jump if CX is 0 CX=0
 JE Jump if equal ZF=1
 JECXZ Jump if ECX is 0 ECX=0
 JG Jump if (signed) greater ZF=0 and SF=0
 JGE Jump if (signed) greater
 or equal SF=OF
 JL Jump if (signed) less SF!=OF
 JLE Jump if (signed) less
 or equal ZF=1 and OF!=OF
 JNA Jump if (unsigned) not 
 above CF=1 or ZF=1
 JNAE Jump if (unsigned) not
 above or equal CF=1
 JNB Jump if (unsigned) not
 below CF=0
 JNBE Jump if (unsigned) not
 below or equal CF=0 and ZF=0
 JNC Jump if carry flag not set CF=0
 JNE Jump if not equal ZF=0
 JNG Jump if (signed) not greater ZF=1 or SF!=OF
 JNGE Jump if (signed) not greater
 or equal SF!=OF
 JNL Jump if (signed) not less SF=OF
 JNLE Jump if (signed) not less
 or equal ZF=0 and SF=OF
 JNO Jump if overflow flag not set OF=0
 JNP Jump if parity flag not set PF=0
 JNS Jump if sign flag not set SF=0
 JNZ Jump if not zero ZF=0
 JO Jump if overflow flag is set OF=1
 JP Jump if parity flag set PF=1
 JPE Jump if parity is equal PF=1
 JPO Jump if parity is odd PF=0
 JS Jump if sign flag is set SF=1
 JZ Jump is zero ZF=1

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

1. 데이터 이동 :

어셈블리에서 데이터를 옮기고자 할때는 MOV 명령어를 사용한다.

MOV [복사될 곳], [읽어들일 곳]

MOV EAX, EBX의 경우 EBX 레지스터를 EAX에 대입한다.
MOV EAX, [EBX]로 표현되어 있는 경우에는 EBX가 가리키는 값을 EAX에 대입한다.
MOV EAX, [EBP+10]의 경우 EBP의 주소에서 20만큼 증가한 주소지의 값을 EAX에 대입한다
MOV EAX, [EBP-10]의 경우 EBP의 주소에서 20만큼 감소한 주소지의 값을 EAX에 대입한다
[EBP+10]과 [EBP-10]의 경우에 함수 내부에 존재하는 명령이었다면 EBP를 기준으로 10 증가된 경우에는 파라미터로 넘어오는 값일 수 있고, 10 감소한 경우에는 함수 내부에서 쓰이는 지역변수일 수 있다.
LEA EAX, [EBP+10] 명령의 경우 EBP 주소에서 10만큼의 주소지를 더한 값이 아니라 EBP의 주소지 값에서 10을 더한 값을 EAX로 대입한다.



2. 전역변수 :

전역변수의 경우 data 섹션에 저장이 되고, 프로그램을 초기화하는 과정에서 세팅되거나 실행중에 변경이 될 수도 있다. 예를 들어 문자열이 참조되는 방법은

.data:0088A1A1  'This Program is powerful' , 0

data 섹션의 문자열이 이렇게 존재하면

.text:006A2A22 mov eax, 0x88A1A1h
.text:006A2A27 retn



3. 비교구문 cmp, test :

cmp 명령어는 주어지는 두 값을 뺄셈을 하여 처리하고, test 명령어는 주어지는 두 값을 논리적 AND 연산해서 처리하는 점이 다르며, 두 명령어가 조건 분기문을 결정하기 위해서 사용된다는 점은 같다.

cmp eax, ebx는 두 값을 뺴서 0이면 참(같은 값)이 되는 형태이다.

test 연산은 보통 호출된 함수들이 일반적으로 리턴값을 EAX 레지스터에 저장하는 것을 이용하여, 다음과 같이 사용된다.

CALL function
TEST eax, eax
JZ 주소지

함수를 call하고, 함수의 리턴값으로 EAX값이 세팅되면, 비교를 하는 것이다.

cmp가 영향을 미치는 플래그들은 ZF, OF, SF, CF이고 만약 ZF 플래그가 ZF=1로 세팅되었다면 주어진 두 값이 같았다는 것을 의미한다.
그 외에 같지 않았을 경우에는 OF, SF, CF를 이용해서 어느 쪽이 더 큰지를 알 수 있다.

test가 영향을 미치는 플래그는 SF, ZF, PF가 이고, test 명령어는 같은 피연산자를 가지게 되는데 그러므로 중요한것은 피연산자가 0이냐, 0이 아니냐하는 것이다. 0이면 ZF가 1로 세트되고, 0이 아니면 ZF가 0으로 세트된다.



4. 분기문의 사용 :

분기문은 일반적으로 점프 구문을 이야기하며, if문을 어떻게 사용하는지에 따라서 달라지는 어셈코드들을 살펴보겠다.

if 조건문은 if(rsult == 0)라면 다음과 같이 변경된다.

cmp dword ptr [ebp-4], 1
jne 00401234

cmp로 비교를 한 후에 점프 구문이 된다.


시언어 소스
if(변수==0){
          if문 내의 함수 호출
}
이후 동작

어셈블리어 변환
mov eax, 변수값
test eax, eax
jnz 이후 동작
if문 내의 함수 호출

if에서 사용되는 변수를 먼저 mov로 값을 대입하고, test로 비교(즉 eax값이 0이냐 0이 아니냐를 판단)를 한 다음에 JNZ를 이용하여 이후 동작을 할지 if문 내부의 함수를 호출할지를 결정한다. 이외에 if문은 if 다음에 else 구문이 여러 번 더 나타날 수 있다는 점을 고려해서 비슷한 방식으로 분석하면된다.



5. 반복문의 사용 :

같은 내용이 반복되어 처리되다가 반복문을 종료하는 비교문을 만나서 점프하여 반복문을 벗어나도록 구현 있다.

loop :
      mov al, [ecx]
      mov [edx], al
      inc ecx
      inc edx
      cmp al, 'c'
      jnz short loop

ECX와 EDX는 메모리상의 문자열을 지칭하는 포인터이고, ECX가 가리키는 문자열에서 EDX가 가리키는 문자열로 복사가 일어나고 있다다. 반복을 할 때마다 포인터가 둘 다 1씩 증가되고, 'c'문자열이 나타날 때까지 복사를 하는 것으로 생각할 수 있다.


for 반복문은 for(int i = 1 ; i<= param ; i++)이라면
mov dword ptr [ebp-8], 1        //  i 변수 = 1
jmp 004017FF
mov eax, dword ptr [ebp-8]    //  i 변수를 eax에 대입
add eax, 1                             //  eax + 1
mov dword ptr [ebp-8], eax    //  eax + 1을 i에 대입       
mov ecx, dword ptr [ebp-8]    //  ecx로 카운팅을 한다.
cmp ecx, dword ptr [ebp+8]    //  종료인지 체크
jg 00401812

어셈블리코드를 보면 i 변수의 값이 변경되고, 반복하면서 카운팅을 하는 것을 알 수 있다.
그리고 카운팅을 하는 변수가 반복 횟수를 초과했는지를 cmp로 검사해서 반복 구문을 빠져나온다.



6. 함수의 사용 :

함수는 시작하는 부분과 끝나는 부분을 파악할 수 있는 구분자가 있어서 확인이 어렵지 않다. 시작하는 부분의 코드를 보자.

push ebp
mov ebp, esp
sub esp, 20

EBP값은 스택에 넣어두고, 현재 ESP값을 EBP에 대입하고, 지역변수를 만들 공간을 확보하기 위해서 ESP값을 변경한다.
그리고 끝나는 부분은 호출이 끝나서 리턴되기 때문에 RET가 쓰이게 된다.

mov eax, -1
mov esp, ebp
pop ebp
ret

이와 같이 ESP는 원래 값으로 돌아가고, EBP는 저장되었던 값으로 복구된다. 그런 후에 RET를 이요하여 함수를 CALL했던 부분으로 다시 돌아간다. 그리고 eax에 -1을 넣어서 결과가 -1임을 표현해주기도 한다.

출처 : asmlove.co.kr // dakuo.tistory.com/7,8
 

'gravity-free > assembly' 카테고리의 다른 글

ARM asm  (1) 2012.06.25
assembly  (0) 2011.01.04
:

assembly

gravity-free/assembly 2011. 1. 4. 16:08
http://zerohz.tistory.com/61 
http://blog.naver.com/PostView.nhn?blogId=hypermin&logNo=70069184103


1.어셈블리어
Push  : sp 레지스터를 조작하는 명령어중의 하나이다.
       스택에 데이터를 저장하는데 쓰인다.
ex:) Push eax : 스택에 Eax의 값을 스택에 저장한다.
ex:) Push 20 :즉석값인 20을 스택에 저장한다.
ex:) Push 401F47 : 메모리 오프셋 401F47의 값을 스택에 저장한다.

Pop  : 이또한 sp 레지스터를 조작하는 명령어중 하나이다.
스택에서 데이터를 꺼내는데 쓰인다.
ex:) Pop eax :스택에 가장 상위에 있는 값을 꺼내애서 eax에 저장한다.
주의점 : Push 의 역순으로 값은 스택에서 Pop 된다.

Mov  : 메모리나 레지스터의 값을 옮길 때[로 만들 때]쓰인다.
ex:) Mov eax,ebx    :ebx 레지스터의 값을 eax로 옮긴다[로 만든다].
ex:) Mov eax,20     :즉석값인 20을 eax레지스터 에 옮긴다[로 만든다].
ex:) Mov eax,dword ptr[401F47]   :메모리 오프셋 401F47 의 값을 eax에 옮긴다[로 만든다]

Lea  : 오퍼렌드1의 값을 오퍼렌드2의 값으로 만들어준다.
ex:) Lea eax,ebx    : eax레지스터의 값을 ebx의 값으로 만든다.

Inc  : 레지스터의 값을 1증가 시킨다.
ex:) Inc eax  : Eax 레지스터의 값을 1증가 시킨다.
Dec  : 레지스터의 값을 1 감소 시킨다.
ex:) Dec eax : Eax 레지스터의 값을 1 감소 시킨다.

Add  : 레지스터나 메모리의 값을 덧셈할떄 쓰임.
ex:) Add eax,ebx   :Eax 레지스터의 값에 ebx 값을 더한다.
ex:) Add eax,50    :Eax 레지스터에 즉석값인 50을 더한다.
ex:) Add eax,dword ptr[401F47]  : Eax 레지스터에 메모리 오프셋 401F47의 값을 더한다.
Sub  : 레지스터나 메모리의 값을 뻇셈할떄 쓰임.
ex:) Sub eax,ebx   : Eax 레지스터에서 ebx 레지스터의 값을 뺸다.
ex:) Sub eax,50
Eax  : 레지스터에서 즉석값 50을 뺸다.
ex:) Sub eax,dword ptr[401F47]    :Eax 레지스터에서 메모리 오프셋 401F47의 값을 뺸다.
Nop  : 아무동작도 하지 않는다. : 90
Call : 프로시저를 호출할떄 쓰인다.
ex:) Call dword ptr[401F47]    : 메모리 오프셋 401F47을 콜한다.
Ret : 콜한 지점으로 돌아간다.
Cmp : 레지스터와 레지스터혹은 레지스터 값을 비교하기위하여 쓰인다.
ex:) Cmp eax,ebx    :Eax 레지스터와 Ebx 레지스터의 값을 비교한다.
ex:) Cmp eax,50     :Eax 레지스터와 즉석값 50을 비교한다.
ex:) Cmp eax,dword ptr[401F47]
:Eax 레지스터와 메모리 오프셋 401F47의 값을 비교한다.
Jmp : 특정한 메모리 오프셋으로 이동할떄 쓰인다.
ex:) Jmp dword ptr[401F47]   :메모리 오프셋 401F47 로 점프한다.
조건부 점프: Cmp나 Test 같은 명령어의 결과에 따라점프한다.
  Je  : Cmp나 Test 의 결과가 같다면 점프
  Jne : Cmp나 Text 의 결과가 같지 않다면 점프
  Jz  : 왼쪽 인자의 값이 0 이라면 점프
Jnz  : 왼쪽 인자의 값이 0 이 아니라면 점프
Jl   : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작으면 점프(부호있는)
Jnl  : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작지 않으면(크거나 같으면) 점프 (부호있는)
Jb   : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작으면 점프(부호없는)
Jnb  : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작지 않으면(크거나 같으면) 점프 (부호없는)
Jg   : 왼쪽 인자의 값이 오른쪽 인자의 값보다 크면 점프
Jng  : 왼쪽 인자의 값이 오른쪽 인자의 값보다 크지 않으면 (작거나 같으면) 점프
Jle   : 왼쪽 인자의 값이 오른쪽 인자의 값보다 작거나 같으면점프 (부호있는)
Jge  : 왼쪽 인자의 값이 오른쪽 인자의 값보다 크거나 같으면 점프
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
약 이정도의 명령어들이 가장 많이 나오는 것들임으로
최소한 위에 나온것들은 외워 두도록 하자.
3. 논리연산
이글에서는 5가지 논리연산에 대해서 쓸것이다.
논리연산자는 두 오퍼렌드의 값의 비트들을 대응시켜 명령에 따른 적절한 값을 구하여 첫번쨰 오퍼렌드의 값을 바꾸어 주는것이다.
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
AND 연산
대응되는 비트가 둘다 1이면 결과는 1이고 그외의 결과들은 모두 0 이 된다.
ex:) MOV EAX,8
AND EAX,10 :위를 계산하기 위해 우선 두 오퍼렌드의 값을 2진수로 바꾸어 주면 8은 1000 이 되고 10은 1010 이 되고 AND 연산은 둘다 1이여야 1이 됨으로 결과는 1000 이 됩니다.

OR 연산
대응되는 비트중 하나가 1 또는 둘다 1이면 결과는 1이고 그외는 모두 0이 된다.
ex:) MOV EAX,8
OR EAX,10
:위를 계산하기 위해 두 오퍼렌드의 값을 2진수로 바꾸어 주면 8은 1000이 되고 10은 1010이 되고 OR 연산은 한쪽 또는 양쪽둘다 1이면 1이고그외는 모두 0 임으로 결과는 1010이 된다.
XOR 연산
대응되는 비트 중에서 한비트가 1이고 다른 비트가 0이면 1이 되고 두개의 비트가 1이면 0 이 되고 두개다 0 이어도 0이 된다.
ex:) MOV EAX,8
XOR EAX,10
:위를 계산하기 위해 두 오퍼렌드의 값을 2진수로 바꾸어 주면 8은 1000이 되고 10은 1010이 되고 XOR 연산은 한쪽만 1이어야 1임으로 결과는 10이 된다.

NOT 연산
NOT 연산은 오퍼렌드의 값을 반대로 하여 준다.
ex:) MOV EAX,10
NOT EAX
:위를 계산하기 위해 오퍼렌드의 값을 2진수로 바꾸어 주면 10은 1010이 되고 NOT 연산은 1 과 0을 반대로 하여 줌으로 결과는 0101 이 된다.
*Test 연산은 오퍼렌드에 영향을 주지 않으며 플래그만 세트 시키어 준다.

 

 

 

 

2.레지스터
범용 레지스터
(1) Eax 레지스터
누산기인 Eax 레지스터는 입출력과 거의 모든 산술연산에 사용된다. 곱셋과 나눗셈, 변환 명령어등은 반드시 Eax 레지스터를 필요하게 된다.

Eax 레지스터는 32bit의 레지스터이고 16bit 의 레지스터로 ax가 있다.
(ax는 왼쪽의 ah와 오른쪽의 al로 이루어져 있다)
 (2) Ebx 레지스터
Ebx는 주소지정을 확대하기 위한 인덱스로서 사용될수 있는 유일한 범용 레지스터 이며, 다른 일반적인 계산 용도로도 쓰인다.
Ebx는 32bit 레지스터이고 16bit로 eb가 있다.
(eb는 왼쪽의 bh와 오른쪽의 bl로 이루어져 있다)
(3) Ecx 레지스터
Ecx는 루프의 반복 횟수나 좌우방향의 시프트 비트 수를 기억한다. 그외의 계산에도 사용된다.
Ecx는 32bit 레지스터이고 16bit로 cx가 있다.
(cx는 왼쪽의 ch와 오른쪽의 cl로 이루어져 있다.)
(4) Edx 레지스터
Edx는 몇몇 입출력 동작에서 사용 된다.
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
플래그 레지스터
(1) OF [Over Flow]
산술연산후 상위 비트의 오버플로를 나타냄
(2) DF [Direction]
스트링 데이터를 이동하거나 비교할떄 왼쪽 또는 오른쪽으로의 방향을 결정한다.
(4) SF [Sign]
산술결과의 부호를 나타낸다.[0=양수,1=음수]
(5) ZF [zero]
산술연산 또는 비교동작의 결과를 나타낸다.
[0=결과가 0이 아님,1=결과가 0임]

(6) CF [Carry]
산술연산후 상위 비트로부터의 캐리 그리고 시프트 또는 회전동작의 결과 마지막 비트
내용을 저장한다.

(7) TF [trap]
프로세서가 단일 스텝 모드(single-step mode)를 동작할수 있도록 해준다.

 

 

 

 

 

 

 

 

 


5. Assembly 명령어

 

MOV <Destination>, <Source> - Move. 값을 대입하려고 할때 사용한다. 사용 방법에는 <Destination>에는 레지스터, 메모리등이 올 수 있으며 <Source>에는 <Destination>와 같은 것 이외에 직접적인 값 등이 올 수 있다.
Example:
MOV EAX, 14h
MOV EAX, DWORD PTR SS:[EBP-14]
MOV DWORD PTR SS:[EBP-14], EDX

 

MOVS, MOVSB, MOVSW, MOVSD(Move String Byte or Word or DWord) - ESI가 가르키는 곳의 값들을 Byte(1byte), Word(2byte), DWord(4byte)크기만음 EDI가 가르키는 곳으로 이동시킨다.

MOVSX(Move with Sign-Extended) - Byte나 Word크기의 Operand를 Word나 DWord사이즈로 늘려준다. 부호는 유지된다.

 

MOVZX(Move with Zero-Extended) - Byte나 Word크기의 Operand를 Word나 DWord사이즈로 늘려준다. 연산 후 채워지지 않은 공간에는 0으로 매꾼다.

 

INT <Value> - Interrupt. 해당 Value에 따른 Interrupt를 발생시킨다. <Value>에 해당하는 것들이 너무 많으므로 다음에 더욱 자세하게 다루기로 하자.
Example:
INT 21h

 

ADD <Destination>, <Source> - Addition. 간단하다. <Destination>+<Source>한 값을 <Destination>에 저장한다. <Destination>에는 레지스터, 메모리등이 올 수 있으며 <Source>에는 <Destination>와 같은 것 이외에 직접적인 값 등이 올 수 있다.
Example:
MOV EAX, 5h
MOV EBX, 12h
ADD EAX, 5h  ; EAX에는 5h + 5h = Ah(10진수로 10)가 저장된다.
ADD EAX, EBX ; EAX에는 Ah + 12h = 1Ch(10진수로 28)가 저장된다.

 

SUB <Destination>, <Source> - Subtact. ADD명령어와 사용법은 동일하다. 단지 기능은 빼주는 역할이라는거!
Example:
MOV EAX, 4h
SUB EAX, 3h ; EAX에는 4h - 3h = 1h가 저장된다.

 

INC <Target> - Increments. 타겟의 값에서 1을 증가시킨다. <Target>에는 레지스터, 메모리등이 올 수 있으며, 직접적인 값은 불가능하다.
Example:
MOV EAX, 4h
INC EAX     ; EAX에는 4h에서 1증가된 값인 5h가 저장된다.

 

DEC <Target> - Decrements. 타겟의 값에서 1을 감소시킨다. <Target>에는 레지스터, 메모리등이 올 수 있으며, 직접적인 값은 불가능하다.
Example:
MOV EAX, 4h
DEC EAX     ; EAX에는 4h에서 1감수한 값인 3h가 저장된다.

 

JMP <Location> - Jump. 어떠한 장소로 점프한다. 쉽게 생각해서 GOTO문을 생각하면 될것이다. <Location>에는 점프할 곳의 코드주소를 담고있는 레지스터나 메모리등이 올 수 있다.
Example:
JMP 0041D983
JMP DWORD PTR SS:[EBP-14]

 

** Conditional JUMP **

 

JMP명령어와 같다. 단지 조건에 따라서 Jump할지 안할지가 결정되므로 Conditional Jump라고 하겠다. Conditional JUMP는 CMP명령어와 같이 사용된다.

 

CMP <Target1>, <Target2> - 두 타겟을 비교해서 Flag Register를 변경시킨다. <Target1>이 <Target2>보다 작으면 CF가 Set되고, 같으면 ZF가 Set되고 이런것 들이 있긴하지만, 다음에 왠만하면 Conditional Jump가 있기때문에 Jump문 의미로 이해하도록 하고 CMP로 인한 Flag 변화는 그냥 넘어가도록 하겠다.
Example:
CMP EAX, EBX

 

Unsigned
JA(JUMP if ABOVE)                                     CF=0 and ZF=0
JAE(JUMP if ABOVE or EQUAL)                    CF=0
JB(JUMP if BELOW)                                     CF=1
JBE(JUMP if BELOW or EQUAL)                    CF=1 and ZF=1
JNA(JUMP if NOT ABOVE)                            CF=1 or ZF=1
JNAE(JUMP if NOT ABOVE or EQUAL)           CF=1
JNB(JUMP if NOT BELOW)                            CF=0
JNBE(JUMP if NOW BELOW or EQUAL)          CF=0 and ZF=0
JE(JUMP if EQUAL)                                      ZF=1
JNE(JUMP if NOT EQUAL)                             ZF=0

Signed
JG(JUMP if GREATER)                                 ZF=0 and SF=OF
JGE(JUMP if GREATER or EQUAL)                SF=OF
JL(JUMP if LOWER)                                     SF!=OF
JLE(JUMP if LOWER or EQUAL)                    ZF=1 and SF!=OF
JNG(JUMP if NOT GREATER)                        ZF=1 or SF!=OF
JNGE(JUMP if NOT GREATER or EQUAL)       SF!=OF
JNL(JUMP if NOT LOWER)                            SF=OF
JNLE(JUMP if NOT LOWER or EQUAL)           ZF=0 and SF=OF
JE(JUMP if EQUAL)                                     ZF=1
JNE(JUMP if NOT EQUAL)                            ZF=0

Flag
JC(JUMP if CARRY flag set)                        CF=1
JNC(JUMP if NOT CARRY flag set)               CF=0
JO(JUMP if OVERFLOW flag set)                  OF=1
JNO(JUMP if NOT OVERFLOW flag set)         OF=0
JP(JUMP if PARITY flag set)                        PF=1
JNP(JUMP if NOT PARITY flag set)               PF=0
JPE(JUMP if PARITY is EVEN)                     PF=1
JPO(JUMP if PARITY is ODD)                      PF=0
JS(JUMP if SIGNAL flag set)                       SF=1
JNS(JUMP if NOT SIGNAL flag set)              SF=0
JCXZ(JUMP if CX is ZERO)                       CX=0

AND <Destination>, <Source> - Logical AND. 논리회로에 있어서의 AND연산을 수행한다. 그리고 AND연산을 시행할때, OF, CF는 초기화되고, ZF는 Set될 수 있다.
Example:
AND EAX, EBX

CALL <Location> - Call. 말그대로 함수를 부를때 사용한다. Jump와 같다고 느껴질지도 모르지만 Call문을 시행할 경우 스택에 Call문이 시행된것을 Push를 하며 Return이 가능하다.
Example:
CALL 0041D983
CALL DWORD PTR [EBX]

DIV <Target> - Division. EAX를 <Target>으로 나누는 연산이다. 결과로 몫은EAX에 나머지는 ECX에 들어간다.
Example:
MOV EAX, 64h
MOV ECX, 9h
DIV ECX       ; 64h(100) / 9h(9) = 몫 : 0Bh(11) , 나머지 1h이므로
                EAX = 0Bh, ECX = 1h가 저장된다.

 

IDIV <Target> - Integer Division. DIV와 똑같다. 하지만 다른점은 부호있는 정수를 다룬다는 점이다.

 

MUL <Target> - Multiplication. EAX와 <Target>을 곱하여 EAX에 저장한다.
Example:
MOV EAX, 2h
MUL 4h       ; EAX에는 2h * 4h = 8h가 저장된다.

 

IMUL <Value> - Integer Multiplication. EAX와 <Value>를 곱하여 EAX에 저장한다.
IMUL <Destination>, <Value> - <Destination>과 <Value>를 곱하여 <Destination>에 저장한다.
IMUL <Destination>, <Value>, <Value> - 2개의 <Value>를 곱한 후에 <Destination>에 저장한다.

 

LEA <Destination>, <Source> - Load Effective Address. <Source>의 실제 주소를 <Destination>에 저장한다. 하지만 이 용도보다는 빠른 계산을 위해 주로 사용된다. 예를들어 LEA EAX, DWORD PTR [2*EAX+ECX]를 하면, EAX에 2*EAX+ECX계산값이 들어가게 된다.

 

NOP - No Operation. 아무것도 하지 않는다.

 

OR <Destination>, <Source> - Logical OR. 논리회로에 있어서의 OR연산을 수행한다. 그리고 OR연산을 시행할때, OF, CF는 초기화되고, ZF는 Set될 수 있다.
Example:
OR EAX, EBX

 

POP <Destination> - POP. 스택에서 ESP가 가르키는 곳에서 주소값을 불러내어 <Destination>에 저장한다. 그리고 ESP는 다음값을 가르키게 된다.
Example:
POP EAX

 

PUSH <Source> - 스택에 <Source>를 집어넣는다. ESP는 최근에 PUSH한 값을 가르키게 된다.
Example:
PUSH EAX

 

RET - Return. 스택에서 주소를 POP해온 후 그 주소로 돌아간다. PUSH와 RET을 조합해서 JMP처럼 사용 할 수 있다.

 

TEST <Target1>, <Target2> - 이 연산은 대부분이 <Target1>과 <Target2>가 같게 설정된다. 예를들면 TEST EAX, EAX. 이 연산은 논리회로의 AND연산을 수행하지만 결과값을 저장하지 않는다. 단지 EAX=0일경우 ZF=1이 되고 EAX!=0일경우 ZF=0이 된다. 그리고 OF, CF는 0이된다.
Example:
TEST EAX, EAX

 

XOR <Destination>, <Source> - Logical Exclusive OR. 논리회로에 있어서 XOR연산을 수행한다. XOR연산을 시행할때, OF, CF는 초기화되고, ZF는 Set될 수 있다. 이 연산은 XOR EAX, EAX처럼 많이 사용되는데, 이렇게 할 경우 XOR=0이 된다. 이유는 직접 해보면 알것이다.
Example:
XOR EAX, EBX
XOR EAX, EAX     ; EAX=0이 됨.

 

LODS, LODSB, LODSW, LODSD(Load String Byte, Word, DWord) - ESI가 가르키는 곳에서 지정한 크기(Byte, Word, DWord) 만큼 읽어와 EAX에 복사한다. ESI는 복사한만큼 이동한다.

 

STOS, STOSB, STOSW, STOSD(Store String Byte, Word, DWord) - EAX에 들어이있는 데이터를 지정한 크기만큼 EDI가 가르키는 주소에 복사한다. EDI는 복사된 만큼 이동한다.

 

CLD(Clear Direction flag), STD(Set Direction flag) - Direction Flag를 Set하거나 Clear할때 사용한다.

 

CMC(Complement Carry flag), CLC(Clear Carry flag), STC(Set Carry flag) - Carry flag를 순서대로 반전, Clear, Set시킨다.

 

SHL <Destination>, <Value> - Shift Logical Left. <Destination>에 <Value>만큼 Shift연산을 왼쪽으로 수행한다. 만약 <Destination>보다 커질경우 CF=1이 된다.

 

SHR <Destination>, <Value> - Shift Logical Right. SHL과 기능은 동일하며 Shift연산이 오른쪽으로 진행된다.

 

ROL <Destination>, <Value> - Rotate Left. SHL과 기능은 동일하다. 단지 자리수가 늘어날경우 해당 비트가 오른쪽 끝으로 이동한다.

 

ROR <Destination>, <Value> - Rotate Reft. SHR과 기능은 동일하다. 단지 자리수가 없어질경우 해당 비트가 왼쪽 끝으로 이동한다.

 


1. 나눗셈 연산의 피젯수는(32bit의 나눗셈을 가정) 항상 edx:eax 이다.
2. cdq 는 나눗셈을 위해 피젯수의 사이즈를 확장하는 것이다.


나눗셈연산(div, idiv)은 eax와 edx에 의해서만 이루어집니다
- 피젯수(나눔을 당하는 수) 는 eax, edx에만 들어갈 수 있다는 얘기에요
16 / 5 연산을 한다고 가정해 봅시다.

16과 5 둘다 32bit data라고 가정하구요

그럼 일단 eax에 16을 넣습니다. 그 다음 ebx(다른레지스터나 메모리도 상관없음)에

5를 넣습니다. 그 다음 div 연산을 하면.........될것 같지만 안됩니다..

일반적으로 제수(여기서는 5)가 32bit이면 피젯수(여기서는 16) 는 64bit가 되어야

32bit 값을 가지는 몫을 얻을 수 있습니다.

그래서 피젯수의 bit를 확장 시켜주는것이 바로 cdq 연산입니다

32bit 크기의 eax의 값을 64bit의 값인 edx:eax로 만들어줍니다.

여기서 edx는 상위자리가되고 eax는 하위 자리가 되죠

자..그럼 cdq 연산까지 끝났으면 edx:eax에 16이 들어가있고 ebx에 5가 들어있겠네요

그럼 idiv연산을 해봅시다(div는 부호가없는 나눗셈 idiv 부호가 있는 나눗셈)

그럼 몫과 나머지가 나와야 하겠죠? 그 결과는 다시 eax와 edx로 들어가는데

eax에는 몫이, edx에는 나머지 부분이 들어갑니다~

 


LoadLibrary GetProcAddress (로드 라이브러리 겟프락어드레스) 후에 변하지 않는 레지스터는
EBX EBP ESI EDI 이다. EAX, ECX, EDX 가 변한다



'gravity-free > assembly' 카테고리의 다른 글

ARM asm  (1) 2012.06.25
asm pt2  (0) 2012.03.23
: