Hey fellow Streamers and YouTubers,

I spent some time looking at the internal structure of the AMF SDK library and found some interesting things. Let’s take a look together, shall we?

I will update this post as I find more info.

DllMain (Library Entry Point)

.text:00000001800EE9B0 ; BOOL __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
.text:00000001800EE9B0 public DllMain
.text:00000001800EE9B0 DllMain proc near ; CODE XREF: __DllMainCRTStartup+88p
.text:00000001800EE9B0 ; __DllMainCRTStartup+A4p
.text:00000001800EE9B0 ; DATA XREF: ...
.text:00000001800EE9B0 sub rsp, 28h
.text:00000001800EE9B4 cmp edx, 1
.text:00000001800EE9B7 jnz short loc_1800EE9C6
.text:00000001800EE9B9 mov cs:hModule, rcx
.text:00000001800EE9C0 call cs:DisableThreadLibraryCalls
.text:00000001800EE9C6 loc_1800EE9C6: ; CODE XREF: DllMain+7j
.text:00000001800EE9C6 mov eax, 1
.text:00000001800EE9CB add rsp, 28h
.text:00000001800EE9CF retn
.text:00000001800EE9CF DllMain endp

AMF SDKs Library disables DLL_THREAD_ATTACH and DLL_THREAD_DETACH messages. This is not abnormal, but still is odd considering you’d use these messages to clean up thread local memory.


.text:0000000180115F40 ; __int64 __fastcall AMFInit(__int64 amfVersion, _QWORD *amfFactoryVarPtr)
.text:0000000180115F40 public AMFInit
.text:0000000180115F40 AMFInit proc near ; DATA XREF: .rdata:stru_1801EFE5Co
.text:0000000180115F40 ; .rdata:off_1801F5EB8o ...
.text:0000000180115F40 var_18 = qword ptr -18h
.text:0000000180115F40 sub rsp, 18h
.text:0000000180115F44 mov [rsp+18h+var_18], 0FFFFFFFFFFFFFFFEh
.text:0000000180115F4C mov eax, cs:amf_isInitialized
.text:0000000180115F52 test al, 1
.text:0000000180115F54 jnz short loc_180115F6D
.text:0000000180115F56 or eax, 1
.text:0000000180115F59 mov cs:amf_isInitialized, eax
.text:0000000180115F5F loc_180115F5F: ; DATA XREF: .rdata:stru_1801EFE5Co
.text:0000000180115F5F lea rax, AMFFactory_Init
.text:0000000180115F66 mov cs:amf_AMFFactoryObj, rax
.text:0000000180115F6D loc_180115F6D: ; CODE XREF: AMFInit+14j
.text:0000000180115F6D ; DATA XREF: .rdata:stru_1801EFE5Co
.text:0000000180115F6D lea rax, amf_AMFFactoryObj
.text:0000000180115F74 mov [rdx], rax
.text:0000000180115F77 xor eax, eax
.text:0000000180115F79 add rsp, 18h
.text:0000000180115F7D retn
.text:0000000180115F7D AMFInit endp

AMFInit is one of the weirder ones: It takes two parameters (int64_t Version, amf::AMFFactory** Factory) but only uses one here. It seems like this is a feature that was planned at some point, but never implemented. It’s easier to see in pseudo-C code:

__int64 __fastcall AMFInit(__int64 amfVersion, _QWORD *amfFactoryVarPtr)
  if ( !(amf_isInitialized & 1) ) // Singleton check
  { // If it doesn't exist, create it now.
    amf_isInitialized |= 1u; // Set the initialized flag to 1.
    amf_AMFFactoryObj = (__int64)&AMFFactory_Init; // And finally call the Init function for AMFFactory, creating the object.
  *amfFactoryVarPtr = &amf_AMFFactoryObj;
  return 0i64; // AMF_OK, never returns anything else.


.text:0000000180115F80 public AMFQueryVersion
.text:0000000180115F80 AMFQueryVersion proc near ; DATA XREF: .rdata:stru_1801EFE90o
.text:0000000180115F80 ; .rdata:off_1801F5EB8o ...
.text:0000000180115F80 var_88 = qword ptr -88h
.text:0000000180115F80 var_80 = qword ptr -80h
.text:0000000180115F80 var_78 = qword ptr -78h
.text:0000000180115F80 var_70 = qword ptr -70h
.text:0000000180115F80 var_60 = qword ptr -60h
.text:0000000180115F80 var_58 = qword ptr -58h
.text:0000000180115F80 Memory = qword ptr -50h
.text:0000000180115F80 var_40 = qword ptr -40h
.text:0000000180115F80 var_38 = qword ptr -38h
.text:0000000180115F80 Dst = qword ptr -30h
.text:0000000180115F80 var_20 = qword ptr -20h
.text:0000000180115F80 var_18 = qword ptr -18h
.text:0000000180115F80 var_10 = qword ptr -10h
.text:0000000180115F80 arg_8 = qword ptr 10h
.text:0000000180115F80 push rdi
.text:0000000180115F82 sub rsp, 0A0h
.text:0000000180115F89 mov [rsp+0A8h+var_78], 0FFFFFFFFFFFFFFFEh
.text:0000000180115F92 mov [rsp+0A8h+arg_8], rbx
.text:0000000180115F9A mov rax, cs:__security_cookie
.text:0000000180115FA1 xor rax, rsp
.text:0000000180115FA4 mov [rsp+0A8h+var_10], rax
.text:0000000180115FAC xor edi, edi
.text:0000000180115FAE mov eax, edi
.text:0000000180115FB0 test rcx, rcx
.text:0000000180115FB3 setnz al
.text:0000000180115FB6 test eax, eax
.text:0000000180115FB8 jnz loc_1801160B1
.text:0000000180115FBE lea r9, aPversionNull ; "pVersion==NULL"
.text:0000000180115FC5 lea r8, aPversionNull_0 ; "pVersion != NULL"
.text:0000000180115FCC xor edx, edx ; Args
.text:0000000180115FCE lea rcx, [rsp+0A8h+Dst] ; Dst
.text:0000000180115FD3 call sub_18000AE50
.text:0000000180115FD8 mov rbx, rax
.text:0000000180115FDB loc_180115FDB: ; DATA XREF: .rdata:stru_1801EFE90o
.text:0000000180115FDB xor edx, edx
.text:0000000180115FDD lea rcx, [rsp+0A8h+Memory]
.text:0000000180115FE2 call sub_180008180
.text:0000000180115FE7 nop
.text:0000000180115FE8 loc_180115FE8: ; DATA XREF: .rdata:stru_1801EFE90o
.text:0000000180115FE8 mov r8, rbx
.text:0000000180115FEB mov rdx, rax
.text:0000000180115FEE lea rcx, [rsp+0A8h+var_70]
.text:0000000180115FF3 call sub_1800091E0
.text:0000000180115FF8 nop
.text:0000000180115FF9 loc_180115FF9: ; DATA XREF: .rdata:stru_1801EFE90o
.text:0000000180115FF9 cmp [rsp+0A8h+var_38], 8
.text:0000000180115FFF jb short loc_18011600C
.text:0000000180116001 mov rcx, [rsp+0A8h+Memory] ; Memory
.text:0000000180116006 call cs:__imp_free
.text:000000018011600C loc_18011600C: ; CODE XREF: AMFQueryVersion+7Fj
.text:000000018011600C mov [rsp+0A8h+var_38], 7
.text:0000000180116015 mov [rsp+0A8h+var_40], rdi
.text:000000018011601A mov word ptr [rsp+0A8h+Memory], di
.text:000000018011601F loc_18011601F: ; DATA XREF: .rdata:stru_1801EFE90o
.text:000000018011601F cmp [rsp+0A8h+var_18], 8
.text:0000000180116028 jb short loc_180116035
.text:000000018011602A mov rcx, [rsp+0A8h+Dst] ; Memory
.text:000000018011602F call cs:__imp_free
.text:0000000180116035 loc_180116035: ; CODE XREF: AMFQueryVersion+A8j
.text:0000000180116035 mov [rsp+0A8h+var_18], 7
.text:0000000180116041 mov [rsp+0A8h+var_20], rdi
.text:0000000180116049 mov word ptr [rsp+0A8h+Dst], di
.text:000000018011604E lea rax, [rsp+0A8h+var_70]
.text:0000000180116053 cmp [rsp+0A8h+var_58], 8
.text:0000000180116059 cmovnb rax, [rsp+0A8h+var_70]
.text:000000018011605F mov [rsp+0A8h+var_80], rax
.text:0000000180116064 mov [rsp+0A8h+var_88], rdi
.text:0000000180116069 mov r9, cs:qword_1801FE050
.text:0000000180116070 xor r8d, r8d
.text:0000000180116073 lea edx, [r8+19h]
.text:0000000180116077 lea rcx, a__________R_48 ; "..\\..\\..\\..\\..\\runtime\\src\\core\"...
.text:000000018011607E call sub_18000BDF0
.text:0000000180116083 nop
.text:0000000180116084 loc_180116084: ; DATA XREF: .rdata:stru_1801EFE90o
.text:0000000180116084 cmp [rsp+0A8h+var_58], 8
.text:000000018011608A jb short loc_180116097
.text:000000018011608C mov rcx, [rsp+0A8h+var_70] ; Memory
.text:0000000180116091 call cs:__imp_free
.text:0000000180116097 loc_180116097: ; CODE XREF: AMFQueryVersion+10Aj
.text:0000000180116097 mov [rsp+0A8h+var_58], 7
.text:00000001801160A0 mov [rsp+0A8h+var_60], rdi
.text:00000001801160A5 mov word ptr [rsp+0A8h+var_70], di
.text:00000001801160AA mov eax, 4
.text:00000001801160AF jmp short loc_1801160C0
.text:00000001801160B1 ; ---------------------------------------------------------------------------
.text:00000001801160B1 loc_1801160B1: ; CODE XREF: AMFQueryVersion+38j
.text:00000001801160B1 mov rax, 1000300000004h
.text:00000001801160BB mov [rcx], rax
.text:00000001801160BE xor eax, eax
.text:00000001801160C0 loc_1801160C0: ; CODE XREF: AMFQueryVersion+12Fj
.text:00000001801160C0 mov rcx, [rsp+0A8h+var_10]
.text:00000001801160C8 xor rcx, rsp ; StackCookie
.text:00000001801160CB call __security_check_cookie
.text:00000001801160D0 mov rbx, [rsp+0A8h+arg_8]
.text:00000001801160D8 add rsp, 0A0h
.text:00000001801160DF pop rdi
.text:00000001801160E0 retn
.text:00000001801160E0 AMFQueryVersion endp

Now this is one of the stranger ones as the fail code is massively overblown. AMFQueryVersion expects a pointer to a uin64_t to store the current version in, but will return AMF_INVALID_ARG if you didn’t do that. However, this function is much larger than it needs to be. It seems to do some sort of logging or tracing, which includes a file name and line too. Helpful if someone ever wants to truly open source this.

AMFFactory_Init (Decompiled Function)

signed __int64 __fastcall AMFFactory_Init_0(__int64 a1, void (****a2)(void))
 return AMFFactory_Initialize(a2);

Nothing really special, the interesting things happen inside AMFFactory_Initialize.


signed __int64 __fastcall AMFFactory_Initialize(void (****this)(void))
 void (****thisCpy)(void); // rbx@1
 void *memory; // rax@1
 __int64 v3; // rdx@1
 void (***memPtr)(void); // rcx@2
 __int64 v5; // rax@5
 __int64 v6; // rbx@5
 __int64 v7; // rax@5
 void *v8; // rax@9
 signed __int64 result; // rax@13
 void *v10; // [sp+40h] [bp-78h]@5
 __int64 v11; // [sp+50h] [bp-68h]@13
 unsigned __int64 v12; // [sp+58h] [bp-60h]@9
 void *Memory; // [sp+60h] [bp-58h]@5
 __int64 v14; // [sp+70h] [bp-48h]@7
 unsigned __int64 v15; // [sp+78h] [bp-40h]@5
 void *v16; // [sp+80h] [bp-38h]@5
 __int64 v17; // [sp+90h] [bp-28h]@9
 unsigned __int64 v18; // [sp+98h] [bp-20h]@7

 thisCpy = this;
 memory = operator new(160ui64);
 if ( memory )
 memPtr = (void (***)(void))AMFFactory_Constructor_Prepare((__int64)memory);
 memPtr = 0i64;
 *thisCpy = memPtr;
 if ( memPtr != 0i64 )
 (**memPtr)(); // Call Constructor
 result = 0i64; // AMF_OK
 LODWORD(v5) = sub_180008100(&v16, v3, L"(*ppContext = new AMFContextImpl) != NULL");
 v6 = v5;
 LODWORD(v7) = sub_180008180(&Memory, 0i64);
 sub_1800091E0(&v10, v7, v6);
 if ( v15 >= 8 )
 v15 = 7i64;
 v14 = 0i64;
 LOWORD(Memory) = 0;
 if ( v18 >= 8 )
 v18 = 7i64;
 v17 = 0i64;
 LOWORD(v16) = 0;
 v8 = &v10;
 if ( v12 >= 8 )
 v8 = v10;
 if ( v12 >= 8 )
 v12 = 7i64;
 v11 = 0i64;
 LOWORD(v10) = 0;
 result = 6i64; // AMF_OUT_OF_MEMORY
 return result;
Bookmark the permalink.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.