나는 이것을 여러 번했고 이것을 계속한다. 이 경우 기본 목표가 어셈블러를 작성하지 않고 읽는 것입니다.
자신 만의 디스어셈블러를 작성하십시오. 다음으로 위대한 디스어셈블러를 만들기위한 것이 아니라, 이것은 엄격히 당신을위한 것입니다. 목표는 명령어 세트를 배우는 것입니다. 새로운 플랫폼에서 어셈블러를 배우 든, 한때 알고 있던 플랫폼의 어셈블러를 기억합니다. 몇 줄의 코드로 시작하고, 예를 들어 레지스터를 추가하고, 바이너리 출력을 분해하고 입력 측에 점점 더 복잡한 명령어를 추가하는 사이에 핑퐁을 사용합니다.
1) 특정 프로세서에 대한 명령어 세트 학습
2) 모든 명령어에서 모든 opcode 비트를 흔들 수 있도록 해당 프로세서에 대해 어셈블에서 코드를 작성하는 방법의 뉘앙스를 배웁니다.
3) 그 명령 세트를 사용하여 생계를 유지하는 대부분의 엔지니어보다 명령 세트를 더 잘 배웁니다.
귀하의 경우 몇 가지 문제가 있습니다. 일반적으로 ARM 명령어 세트를 시작하는 것이 좋습니다. 오늘 출하되는 ARM 기반 제품이 다른 어떤 것보다 많습니다 (x86 컴퓨터 포함). 그러나 현재 ARM을 사용하고 있고 ARM이 수행하려는 작업에 도움이 될 수도 있고 도움이되지 않을 수도 있다는 것을 알고있는 시작 코드 또는 기타 루틴을 작성하기에 충분한 어셈블러를 알지 못할 가능성이 있습니다. ARM 우선의 두 번째이자 더 중요한 이유는 명령어 길이가 고정 된 크기이고 정렬되기 때문입니다. x86과 같은 가변 길이 명령어를 분해하는 것은 첫 번째 프로젝트로 악몽이 될 수 있으며 여기서 목표는 연구 프로젝트를 만들지 않는 명령어 세트를 배우는 것입니다. 세 번째 ARM은 잘 수행 된 명령어 세트이며 레지스터는 동일하게 생성되며 개별적인 특별한 뉘앙스가 없습니다.
따라서 시작하려는 프로세서를 파악해야합니다. msp430 또는 ARM을 먼저 제안하고 ARM을 먼저 또는 두 번째로 x86의 혼돈을 제안합니다. 어떤 플랫폼을 사용하든 사용할 가치가있는 모든 플랫폼에는 명령 세트와 연산 코드 (기계 언어의 비트 및 바이트) 인코딩이 포함 된 공급 업체에서 무료로 제공하는 데이터 시트 또는 프로그래머 참조 설명서가 있습니다. 컴파일러가하는 일과 컴파일러가 고생 할 필요가없는 코드를 작성하는 방법을 배우기 위해서는 몇 가지 명령어 세트를 알고 각 최적화를 통해 각 컴파일러의 각 명령어 세트에서 동일한 상위 레벨 코드가 어떻게 구현되는지 확인하는 것이 좋습니다. 환경. 하나의 컴파일러 / 플랫폼에 대해서는 더 좋게 만들었지 만 다른 모든 것에 대해서는 훨씬 더 나쁘게 만들기 위해 코드를 최적화하는 것을 원하지 않습니다.
ARM과 같이 또는 msp430과 같이 2 바이트마다 메모리를 통해 단순히 처음부터 시작하여 4 바이트 워드마다 선형 적으로 분해하는 대신 가변 길이 명령어 세트를 분해하는 것이 좋습니다 (msp430에는 가변 길이 명령어가 있지만 여전히 인터럽트 벡터 테이블의 진입 점에서 시작하면 메모리를 통해 선형으로 이동). 가변 길이의 경우 벡터 테이블 또는 프로세서 부팅 방법에 대한 지식을 기반으로 진입 점을 찾고 실행 순서대로 코드를 따르고 싶습니다. 사용 된 바이트 수를 알기 위해 각 명령어를 완전히 디코딩해야합니다. 그러면 명령어가 무조건 분기가 아닌 경우 해당 명령어 이후의 다음 바이트가 다른 명령어라고 가정합니다. 가능한 모든 분기 주소도 저장해야하며 자세한 지침을 위해 시작 바이트 주소라고 가정해야합니다. 한 번 성공했을 때 바이너리를 여러 번 통과했습니다. 진입 점에서 시작하여 해당 바이트를 명령어의 시작으로 표시 한 다음 무조건 분기에 도달 할 때까지 메모리를 통해 선형으로 디코딩했습니다. 모든 분기 대상은 명령어의 시작 주소로 태그가 지정되었습니다. 새 분기 대상을 찾을 수 없을 때까지 바이너리를 여러 번 통과했습니다. 언제라도 3 바이트 명령어를 찾았지만 어떤 이유로 두 번째 바이트를 명령어의 시작으로 태그 한 경우 문제가 있습니다. 코드가 상위 수준의 컴파일러에 의해 생성 된 경우 컴파일러가 악의적 인 작업을 수행하지 않는 한 발생해서는 안됩니다. 코드에 손으로 작성된 어셈블러 (예 : 오래된 아케이드 게임)가있는 경우 r0 = 0과 같이 발생하지 않는 조건부 분기가있을 수 있으며 0이 아니면 점프가 이어질 수 있습니다. 계속하려면 바이너리에서 직접 편집해야 할 수도 있습니다. 내가 x86에 있다고 가정하는 즉각적인 목표에 대해서는 문제가 있다고 생각하지 않습니다.
gcc 도구를 권장합니다. mingw32는 x86이 대상인 경우 Windows에서 gcc 도구를 사용하는 쉬운 방법입니다. mingw32 plus msys는 binutils 및 gcc 소스에서 크로스 컴파일러를 생성하기위한 훌륭한 플랫폼입니다 (일반적으로 매우 쉽습니다). mingw32는 훨씬 빠른 프로그램과 같이 cygwin에 비해 몇 가지 장점이 있으며 cygwin dll 지옥을 피할 수 있습니다. gcc 및 binutils를 사용하면 C 또는 어셈블러로 작성하고 코드를 디스 어셈블 할 수 있으며 세 가지 중 하나 또는 모두를 수행하는 방법을 보여주는 웹 페이지가 읽을 수있는 것보다 많습니다. 가변 길이 명령어 세트로이 작업을 수행하려면 디스어셈블러가 포함 된 도구 세트를 사용하는 것이 좋습니다. 예를 들어 x86 용 써드 파티 디스어셈블러는 올바르게 디스 어셈블되었는지 알 수 없기 때문에 사용하기 어려울 것입니다. 이 중 일부는 운영 체제에 따라 다르며, 목표는 디스어셈블러가보다 정확한 작업을 수행 할 수 있도록 데이터의 정보 표시 명령을 포함하는 이진 형식으로 모듈을 컴파일하는 것입니다. 이 기본 목표에 대한 다른 선택은 검사를 위해 어셈블러로 직접 컴파일 할 수있는 도구를 보유한 다음 바이너리 형식으로 컴파일 할 때 동일한 명령어를 생성하기를 바라는 것입니다.
질문에 대한 짧은 (약간 짧게) 답변입니다. 디스어셈블러를 작성하여 명령어 세트를 학습하십시오. 나는 ARM처럼 위험하고 배우기 쉬운 것으로 시작합니다. 하나의 명령어 세트를 알게되면 다른 명령어 세트를 훨씬 쉽게 선택할 수 있습니다. 종종 몇 시간 안에 세 번째 명령어 세트를 통해 구문에 대한 데이터 시트 / 참조 매뉴얼을 사용하여 거의 즉시 코드 작성을 시작할 수 있습니다. 사용할 가치가있는 모든 프로세서에는 연산 코드의 비트 및 바이트에 대한 지침을 설명하는 데이터 시트 또는 참조 설명서가 있습니다. ARM과 같은 RISC 프로세서와 x86과 같은 CISC를 배우면 모든 것에 대해 레지스터를 거쳐야하거나 레지스터가 적거나없는 메모리에서 직접 연산을 수행 할 수있는 것과 같은 차이점을 충분히 느낄 수 있습니다. 세 개의 피연산자 명령어 대 두 개 등. 높은 수준의 코드를 조정할 때 둘 이상의 프로세서에 대해 컴파일하고 출력을 비교합니다. 여러분이 배우게 될 가장 중요한 것은 높은 수준의 코드가 아무리 잘 작성 되더라도 컴파일러의 품질과 최적화 선택이 실제 명령어에 큰 차이를 만든다는 것입니다. llvm 및 gcc (binutils 포함)를 권장하지만 생성하지 않습니다.훌륭한 코드이지만 다중 플랫폼 및 다중 대상이며 둘 다 최적화 프로그램이 있습니다. 그리고 둘 다 무료이며 다양한 대상 프로세서의 소스에서 크로스 컴파일러를 쉽게 빌드 할 수 있습니다.