Since day one of our ownership of this particular 1NZFE powered Toyota IST the engine was a bit on louder side.
Previously all my other cars had timing belts and I put it down to simply being a “feature” of chain.
After more family and friends upgraded their cars to xNZFE, it was clear that this engine was slightly louder in chain department then the rest (especially in the mornings).

One day after coming back from a holiday I started the car only to be greeted by loud chain slapping noise.

OMG! My wife’s car turned into a Nissan!

I decided to exorcise the Nissan out of it by replacing the chain tensioner and guides.

Below is how I did it, not necessary the “correct” way. This process took about 6 hours, good chunk of it was spent on cleaning the surfaces.
It was pretty much like replacing timing belt, except with way more RTV.
Over all I replaced two oil pump O-rings, front crank seal, valve cover gasket, both chain guides and chain tensioner.

In retrospect I prefer timing belts as opposed to timing chains, especially considering that the tensioners still fail on chains occasionally.

Interesting notes:

  • Old guides were PA66, new guides are PA46 (improved)
  • Old tensioner had larger oil hole than the new tensioner.
  • In hand old tensioner functioned correctly, but while fitted it would skip.
  • For some reason Toyota decided it is great idea to incorporate water pump flange into front cover. This creates a potential of RTV failure and leakage of coolant into sump. It also requires pump removal when removing front cover. It would saved me 2 hours if the pump was not part of the front cover.
  • The oil pump is mounted on front cover, thus requiring two O-rings for inlet and outlet.
  • One of those O-ring was completely flat, possibly leaking oil. It is hard to tell if the O-rings seated properly when fitting the cover.
  • For the crank pulley Toyota gone away from woodruff key in favour of tiny hollow pin.
  • The 10mm cover bolts and water pump bolts torqued at 11Nm. The 12mm cover nut and bolts torqued at 24Nm. The crank pulley went in hella-tight with crappy rattle gun and on top with some hammer on spanner action (It should be 128Nm). The tensioner and guides bolts are torqued to 9Nm.
  • I used Threebond grey RTV. The manual specified two kinds of RTVs for water pump and the rest of the cover, good luck buying two Toyota genuine RTV tubes ;).

Slack Before and After:

Continue reading

HUSQVARNA rip-off pricing in NZ

So, I came across an interesting, but not surprising thing with Husqvarna MSRP prices in NZ compared to USA.

I recently looked at Husqvarna 445 and when googled for it, I got multiple MSRP prices (one for NZ, on for AU and another for USA).

Husqvarna 445 MSRP by country:
USA: 329.95 USD

AU: 899.00 AUD (~$690 USD)

NZ: 1019.00 NZD (~$745 USD)

According to NZ customs duty calculator using US MSRP price as base the fair MSRP price in NZ should have been $599 NZD (~$440 USD).

What is with the ~$150 USD price difference between NZ and Australia? According to Husqvarna Americans are twice better than Australians and more than twice better than Kiwis. They treat Kiwis like chumps that will buy thing at whatever prices Husqvarna feels like.

Guess what, from now on I will be avoiding the Husqvarna until these greedy corporate types will pull their heads out of their asses and set fair MSRP prices. In this global economy it is very stupid not to have standardised prices across the globe.

If I really wanted to buy one, no way in hell I would be buying it in NZ, as for about $150NZ I can get it shipped from the states.

Recently we got a nice 2005 Toyota Vitz RS (NCP91) with a manual transmission.

It is great car in all aspects except power delivery and throttle response is pathetic.

We already have 1NZ-FE powered car in the family – Toyota IST and unlike the Vitz RS it is not that pathetic (in fact it is kind of fun to drive).
Yes, I have driven multiple Vitz RS and they were all asthmatic.
So what is so different between IST and RS? IST happens to have cable driven throttle, while RS has drive-by-wire.

Here is where Toyota screwed up and completely ruined the car: they made throttle map to emulate steam roller.

If I was Toyota I would not stick RS badge on the car, it is not a hot hatch simply because it lacks in engine department. Vitz RS should have came out with 1ZZFE at least.
What is even worse they decided to stick this anaemic throttle map on top. As if the car was not boring enough.

After consulting google and various forums I had two minor mod options:

1) Stick a throttle controller
2) Stick a throttle body off 1ZZFE.

I decided to go for second option as it is transparent from end user point of view. There are a few minor hurdles with this mod.

Continue reading

Cross-flash M1015/9420-8i LSI controller quasi-official way

WARNING: don’t blame me if you brick your card ;).

Building large cost effective storage solutions present a problem: lack of cost effective HBAs.
The solution to this problem is an interesting controller from IBM/Lenovo M1015. These can be bought for about 70USD on ebay.
The only problem with that controller is that the firmware it comes with (aka WebBIOS) is utter crap.
This problem can be resolved thanks to LSI providing alternative firmware images.

I generally flash these cards in Pass-Through mode, as they are not suitable for hardware raid scenario (lack of battery and RAM). Besides in low cost situation it is far better to use mdadm RAID (or ZFS) than hardware RAID (due to flexibility in reshaping, monitoring and fault recovery). With hardware RAID you simply cannot start with 3-disk RAID5 and grow it a disk at a time to 12-disk RAID6 without moving data to another storage.

I decided to write this guide as I could not find a guide that would use the tools and firmware images directly acquired from official sources (LSI/Avago/Broadcom).

All the guides include their own download links, which present security (malware) and possible bricking issues (if images were corrupted).

The goal of this guide is to stick to only files sourced from LSI (now Broadcom) as well as avoiding using Windows (Linux friendly).
By the time someone reads this the version and links will change so I am including search keywords on how to find the files in question.

I will be focusing on Pass-Through mode firmware (IT mode), but the only difference to IR mode is the 2118it.bin vs 2118ir.bin.

Continue reading

ILDVR – and their lack of professionalism

It all started with me purchasing ILDVR INC-MH40D06 IP Camera. I decided to poke at it and discovered some interesting and blatant security flaws.

About a year ago I contacted ILDVR (Arnold and Marika Wei) regarding the security issues, which got no response.
After about a year of the camera sitting on a shelf, I decided to poke at it again.
Which prompted me to send them this email:

For which I got a friendly response from Marika:

To which I replied, asking for firmware update (which I thought was reasonable to expect firmware updates for products with serious security flaws):

The only response I got is this peculiar email from sales@ildvr.com:

So, it seems that:
1) ILDVR.com/ILDVR does not care about security
2) ILDVR.com/ILDVR does not care about PR
3) ILDVR.com/ILDVR does not care about customers

Perhaps they should adopt the following motto:

“GO TO HELL! – ILDVR (where security does not matter)”.

To be honest, I would probably let go this whole thing if they simply not responded. It would have taken them less effort to not to respond either. Instead they chose to send me email with “GO TO HELL!”. I find this thing very hilarious.

It is even more hilarious if you look at google search results:

ILDVR INC-MH40D06 security nightmare part 2

I have put off the ILDVR camera, as I kind of lost interest.
For previous posts see here, here and here.

I was bored so I decided to poke at again.

I was interested where does the camera store users and in what format. What I found out is an atrocious mocking of security.
The camera stores local users and their passwords (in plain text) in following file:

/mnt/flash/data/OwnUserInfo.txt

Yep: the same directory which is accessible without auth via port 10081. So if you forgot password (and forgot the silly hardcoded HANKVISION), then you can get a reminder what it is by simply going here:

http://${CAMERA_IP}:10081/OwnUserInfo.txt

There is also another “binary” file that contains interesting references to HANKVISION and local users:

/mnt/flash/data/UserInfo

strings that and you get following:

HANKVISION
e82f5af1f39f021b44e78089b5a40a8e0aa8d2768c705e8f139bec04d87d5a54
8f081b5a8e0685ca975a01d4159930f9
0d9a1f80bcc7a1e4a00f04588062ed67
admin
8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
21232f297a57a5a743894a0e4a801fc3
76eb00c6458e9b2755b570ae565ba0a6

Changing the password to HANKVISION reveals that this string is “encoded” “HANKVISION”:

e82f5af1f39f021b44e78089b5a40a8e0aa8d2768c705e8f139bec04d87d5a54
8f081b5a8e0685ca975a01d4159930f9

Not sure if the obfuscation is worth spending time on, especially when we already know HANKVISION is hardcoded in web server binary and OwnUserInfo.txt already contains passwords in clear texts.

strace-ing ‘webs’ process during certain conditions opens the /tmp/umconfig.txt, which contains following:

TABLE=users
ROW=0
name=HANKVISION
password=0d9a1f80bcc7a1e4a00f04588062ed67
group=Administrator
prot=1
disable=0
ROW=1
name=admin
password=76eb00c6458e9b2755b570ae565ba0a6
group=Administrator
prot=1
disable=0
ROW=2
name=adminadmin
password=5ca1e16e4fa3fa58b6656b9ad547fa0f
group=Normal
prot=0
disable=0
TABLE=groups
ROW=0
name=Administrator
priv=4
method=2
prot=0
disable=0
ROW=1
name=Normal
priv=4
method=2
prot=0
disable=0
TABLE=access
ROW=0
name=/browse/
method=2
secure=0
ROW=1
name=/jpgimage/
method=2
secure=0
ROW=2
name=/mjpgstreamreq/
method=2
secure=0
ROW=3
name=/form/
method=2
secure=0
group=Administrator
ROW=4
name=/cgi/
method=2
secure=0

The “hashes” correlate to /mnt/flash/data/UserInfo…

Looking firmware upload function (in browse/javascript/sysInf.js) I found this bit:

function fileUpload(){
...
		var typeAllow = [".ifu", "macaddr.txt", "deviceid.txt", "sn.txt", "audio.dat", ".bin", ".png", ".ifc", ".lib", ".uid", ".pid","logo.gif","whitelist.txt"];
		var fileType = ["ifu", "mac", "deviceid", "sn", "audio", "bin", "png", "ifc", "lib", "uid", "pid","gif","wlst"];
....

I have tested the upload function with logo.gif and that worked: the logo on top got replaced, so it brings a possibility of doing something more (sneaking in a binary?).

Looking at ‘webs’ binary I decided to google for strings in case someone leaked the source or these bastards stole somebody else’s work.
Here what I found:
The string:

webs: websWrite lost data, buffer overflow

Matches suspiciously named file here:
https://github.com/socoola/yhrouter/blob/master/user/goahead/src/webs.c

Same could be said for these strings:

webs: Listening for HTTP requests at address %s
webs: accept request

What is surprising is that they avoided doing execve calls where they could. IP addresses, routes, all set via ioctl, even time is set via settimeofday function. This removed possibility of command injection.

Here is what I believe is going on with this firmware:

The video side and core functionality has been lifted off SDK by Hisilicon. The web server stuff has been implemented by actual Hankvision people, most likely low paid undergraduate Chinese students. The core web server functionality has been lifted off the internet (see above).

What could have been done better without spending much on development:

Remove hard coded passwords!
Throw away all activeX crap (use MJPEG stream for “preview”).
Turn off telnet and leave ssh on with configurable password (perhaps make it a separate user?).
Do not store plain passwrods anywhere
Throw away all the dyndns and cloud nonsense.
Add actual off checkbox for FTP, Mail and SIP stuff (and possbly throw away SIP stuff).
Add VLC plug-in functionality.
Remove web server that listens on port 10081 exposing whole bunch of private data.

I am not sure what they are trying to achieve by not allowing SSH/Telnet access, but this is counter productive. I will not buy a security product to which I do not have control! Besides if I wanted to get access to your firmware, I don’t need SSH or Telnet, when I have RS232 and soldering iron.

For those who purchased this camera, if you really have to use it do the following:

Hexedit webs binary and change the HANKVISION bit to something else

And

Remove gateway setting (set it the same IP as camera) and preferably isolate camera from rest of the network (separate VLAN and port forwarding to recorder).

Or

Just chuck it in the bin and never purchase anything from ILDVR again.

Shame on you ILDVR for not responding to me when I contacted you almost a year ago about hard coded passwords. Shame on you ILDVR for not providing root password or firmware updates.

python OpenCV basic motion detection

Here I will describe how I use OpenCV for capturing RTSP streams, with purpose of motion detection.

For basic OpenCV I use these two libraries:

import cv2
import numpy as np

cv2 is OpenCV library (second version), and numpy is python numeric lybrary (useful for manipulating matrices among other things).

To initiate capture one simply does following:

cap = cv2.VideoCapture('rtsp://192.168.1.69:554/Streaming/Channels/2')

In this example I use second stream (of lower resolution) for motion detection.

From there you can get heigh and width of the frame (this will be useful later):

width = cap.get(3)
height = cap.get(4)

I use BackgroundSubtractorMOG for motion detection (somewhat cheating ;)):

bg = cv2.BackgroundSubtractorMOG(100,3,0.6,30)

The magic is in parameters, I used following:
100 – history
3 – number of Gaussian mixtures
0.6 – background ratio
30 – noise strength
The numbers above are not necessarily “correct” but I came to them with error and trial (and “guestimation”).
Here is document in detail describing this algorithm: http://personal.ee.surrey.ac.uk/Personal/R.Bowden/publications/avbs01/avbs01.pdf

The actual capture loop looks something like this:

while(True):
    ...
    ret, frame = cap.read()
    motion = bg.apply(frame, learningRate=0.005)
    kernel = np.ones((3, 3), np.uint8)
    motion = cv2.morphologyEx(motion, cv2.MORPH_CLOSE, kernel, iterations=1)
    motion = cv2.morphologyEx(motion, cv2.MORPH_OPEN, kernel, iterations=1)
    motion = cv2.dilate(motion,kernel,iterations = 1)
    contours, hierarchy = cv2.findContours(motion, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    ...
ret, frame = cap.read()

captures a single frame

motion = bg.apply(frame, learningRate=0.005)

extracts a black and white image with the background removed (learnignRate value has been chosed by error and trial).

Next four lines simply manipulate extracted image in such that it does following:
MORPH_CLOSE: removes small holes (up to 3×3 pixel, defined by kernel) within the object (“white”) in the extracted motion matrix.
MORPH_OPEN: removes small dots within the “background” (black) in the extracted motion matrix.
dilate: is making sure there all adjacent islands are joined together, so when we extract contours we get small amount of contours as result.

The “3×3 pixel” block comes from here:

kernel = np.ones((3, 3), np.uint8

The last step from processing frame is extracting the contours:

contours, hierarchy = cv2.findContours(motion, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

The extracted contours can then be iterated and hull drawn around them:

for cnt in contours:
    hull = cv2.convexHull(cnt)

The hull points then can be checked against the mask if motion is inside of the area of interest:

Lets define the mask as the whole frame (I am pretty sure there is a better way;)):

mask_points = [
( 0 , 0 ),
( 1 , 0 ),
( 1 , 1 ),
( 0 , 1 ),
]

mask_array=[]
for point in mask_points:
    mask_array.append([[int(point[0] * width ), int(point[1] * height )]])
mask = np.array(mask_array, np.int32)

This looks cumbersome, but what I am achieving here is converting mask_points list of human readable relative coordinate tuples (eg: centre will be at (0.5,0.5)). Mask can be defined as a polygon with relative positioning of each corner to the frame (independent from pixel size).

We check if hull point is inside our mask

for point in hull:
    distance = cv2.pointPolygonTest(mask,tuple(point[0]),1)
    if distance > 0:
        it_is_inside()

and vice-versa (in case if mask is smaller than the frame):

Note: The colour is defined by this tuple: (0,255,0)

Then do display the whole thing insert this inside of the while(True):

cv2.imshow('motion',frame)
k = cv2.waitKey(30) & 0xff
if k == 27:
    break

The above is basic idea behind my motion detection scripts. I have omitted a lot of glue logic and arithmetic due to my script is not ready for public display ;).