DGND3700 V1 Transmission Firmware Reverse Decompile

=Foreword= This is all new to me so please don't take everything as fact as I may well be mistaken in how the assembler is actually working! I also use 'function' where I probably should be using 'MIPS subroutine' too.

=httpd daemon (web server) filtering=

How to get around the httpd's filter for OpenVPN access.

blocking non subnet traffic
The httpd daemon listens on all interfaces but firing it up from the terminal will show this when you try and connect from outside the LAN, i.e. on 10.8.0.x via VPN it won't have it.

~ # httpd -S -E /usr/sbin/ca.pem /usr/sbin/httpsd.pem ~ # Info: No FWPT default policies. httpd: socket bound in 0.0.0.0:80.

~ # check remote access to non-shares... Reject remote access to non-shares

Appears to be being filtered.

After some fiddling around I find it is looking at the nvram parameter 'lan_netmask'

~ # param get lan_netmask 255.255.255.0

Changing this to 0.0.0.0 then allows it to work as the subnet check always passes. However changing this may break other things!

decompiling httpd with REC Studio4
(You need to rename it appending .exe before REC will see it to let you open it)

The first thing to happen from main is to run http_d startup.



Looking at the 'call tree' tab, through main, it leads onto http_d. Here you can see one of the things it does at the start is calling 'isLanSubnet' function, which sounded promising!



Looking through http_d function further in 'decompile' tab, you can see how it starts up checking to see if remote access and such is enabled, then further along more detail about what 'isLanSubnet' may be doing.



It appears to fire off the 'isLanSubnet' function which presumably checks the incoming IP against the subnet value as set in 'lan_netmask', then returns 'something' non-zero on successful match, passed to an 'if/else'. You can see we want to take the 'if' branch, as the else one produces the errors (underlined in purple) (see page top).

The 't' values are in the header, streams? they don't seem to relate to what the decompile shows though? I don't understand this bit.

http_d {// addr = 0x0040E334  struct _IO_FILE* _t601;  // _t601 struct _IO_FILE* _t603;  // _t603 struct _IO_FILE* _t604;  // _t604 char* _t607;                          // _t607

looking at isLanSubnet function
Initially I thought about trying to make this function always return a value (presumably setting a pointer r31?) but I realised I had no idea how to do that!

(This function is loaded at address 0x0048BAA0)



Also after delving into it, you can see it is called by lots of other things than just http_d, so that was probably a bad idea. (called from 10 places)



Anyway I will leave that as is!

Looking at httpd in dissasembler
Going back to 'http_d' in the Disasm tab (tick 'show offsets', 'show opcodes', 'show src lines'). 'http_d' starts at memory address 0x0040E344, then going down to the section where 'isLanSubnet' is called at 0x0040F198 via a jump and link (JALR). (The previous lw is specifying where the jump table entry is for this.)





After the call to the 'isLanSubnet' function, the next line (0x0040F19C) (lw, load word) is restoring the passed parameters back ($a0), it loads from a 'RAM value' '10984(sp)' as an argument ($a0). I don't understand how that fits into the decompiled code!

The next line is the thing that matters, at 0x0040F1A0, "bne v0,zero,L0040FA04" equates to the "if(_t602 !=0 ) { " in the above decompiled code. (_t602 is the returned data, i.e. v0.)

i.e. bne(branch if not equal) means if the result is false, carry on (follow the 'if'), which is what we want to happen. (the 'else' results in the errors)

Thus branch if v0 isn't equal to 0, where v0 (return value register) which represents the return code from 'isLanSubnet'. [MIPS register names]

Hence we want this to always happen even if v0 is returning 0!

Fixing it to always return true
We need to find out what to alter in the actual /usr/sbin/http file's hex code!

From the above screenshot, the bne instruction at the load address 0x0040F1A0 (second column) corresponds to the actual file offset 0xF1A0 (first column) (as it loads in at 0x0400000, PT_LOAD in target map). The third column is the hex value of the instruction 14400218.

At this point it would be a good idea to watch this great video explaining how the hex is calculated from the instruction. It is not a simple conversion as the below will illustrate due to the bit field separations.

[Great explanation about converting the MIPS assembler into hex]

We need a easy way to do this as life is too short for handwriting binary out. Unfortunately I couldn't find an online convertor that did MIPS > HEX > MIPS. Only this one that does MIPS to hex which is as good as I could find.

[to calculate the hex from the MIPS assember]

In the about bne, the 'immediate' is shown by REC Studio as 'L0040FA04' which is where it branches too, but this needs to be an offset for conversion to hex. So it is jumping an offset of 0x0040FA04 - 0x0040F1A0 = 0x864

So as a MIPS assembler instruction this is 'bne $v0,$zero,0x0864' that when converted as above is hex '0x14400218'. (which is what REC Studio shows as the hex)

From the MIPS ASM calculator to verify....

bne $v0,$zero,0x0864 Line 1: 0x00000000:0x14400218 [bne $v0 $zero 0x0864 => I(op:5(bne) rs:2(v0) rt:0(zero) immed:0x00000218)]

Anyway so what I want to do is make this instruction always return 'true' and follow the 'if' branch.

Having a look though this [MIPS Instruction reference] it appears I want to do is simply make it compare 0 with 0 and be true. i.e. "if ( 0 != 0 ) {", a BEQ.

i.e. so we want to change this

BNE -- Branch on not equal, Branches if the two registers are not equal

...to this

BEQ -- Branches if the two registers are equal

...and replace the return register value with a zero. So replace BNE with BEQ and $v0 with $zero.

So we want 'beq $zero,$zero,0x0864', and just need to convert it to hex!

beq $zero,$zero,0x0864 Line 1: 0x00000000:0x10000218 [beq $zero $zero 0x0864 => I(op:4(beq) rs:0(zero) rt:0(zero) immed:0x00000218)]

Thus the machine instruction hex now becomes 0x10000218

Editing httpd
Now you just need to change 14400218 to 10000218 at offset 0xF1A0 in the httpd program file to remove that pesky subnet check!

$ printf '\x10\x00' | dd of="$fsinstall/usr/sbin/httpd" bs=1 seek=$((0xF1A0)) count=2 conv=notrunc



http via VPN


Connected via openVPN on Android showing web interface now working on different subnet.